summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorLioncash <mathew1800@gmail.com>2018-12-19 18:57:47 +0100
committerLioncash <mathew1800@gmail.com>2018-12-21 13:05:31 +0100
commit6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e (patch)
treef2b45468a65f650c03e47fe3a494cf1d27c1fab3 /src/core/hle/kernel
parentcommon: Add basic bit manipulation utility function to Common (diff)
downloadyuzu-6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e.tar
yuzu-6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e.tar.gz
yuzu-6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e.tar.bz2
yuzu-6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e.tar.lz
yuzu-6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e.tar.xz
yuzu-6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e.tar.zst
yuzu-6ff5135521a59cd510ba3ecc8d5ff5d56cdbf08e.zip
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/errors.h1
-rw-r--r--src/core/hle/kernel/handle_table.h6
-rw-r--r--src/core/hle/kernel/process_capability.cpp253
-rw-r--r--src/core/hle/kernel/process_capability.h209
4 files changed, 466 insertions, 3 deletions
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 8b58d701d..a3d725866 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -11,6 +11,7 @@ namespace Kernel {
// Confirmed Switch kernel error codes
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
+constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index 6b7927fd8..89a3bc740 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -43,6 +43,9 @@ enum KernelHandle : Handle {
*/
class HandleTable final : NonCopyable {
public:
+ /// This is the maximum limit of handles allowed per process in Horizon
+ static constexpr std::size_t MAX_COUNT = 1024;
+
HandleTable();
~HandleTable();
@@ -91,9 +94,6 @@ public:
void Clear();
private:
- /// This is the maximum limit of handles allowed per process in Horizon
- static constexpr std::size_t MAX_COUNT = 1024;
-
/// Stores the Object referenced by the handle or null if the slot is empty.
std::array<SharedPtr<Object>, MAX_COUNT> objects;
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
new file mode 100644
index 000000000..8d787547b
--- /dev/null
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -0,0 +1,253 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_util.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/process_capability.h"
+#include "core/hle/kernel/vm_manager.h"
+
+namespace Kernel {
+namespace {
+
+// clang-format off
+
+// Shift offsets for kernel capability types.
+enum : u32 {
+ CapabilityOffset_PriorityAndCoreNum = 3,
+ CapabilityOffset_Syscall = 4,
+ CapabilityOffset_MapPhysical = 6,
+ CapabilityOffset_MapIO = 7,
+ CapabilityOffset_Interrupt = 11,
+ CapabilityOffset_ProgramType = 13,
+ CapabilityOffset_KernelVersion = 14,
+ CapabilityOffset_HandleTableSize = 15,
+ CapabilityOffset_Debug = 16,
+};
+
+// Combined mask of all parameters that may be initialized only once.
+constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
+ (1U << CapabilityOffset_ProgramType) |
+ (1U << CapabilityOffset_KernelVersion) |
+ (1U << CapabilityOffset_HandleTableSize) |
+ (1U << CapabilityOffset_Debug);
+
+// Packed kernel version indicating 10.4.0
+constexpr u32 PackedKernelVersion = 0x520000;
+
+// Indicates possible types of capabilities that can be specified.
+enum class CapabilityType : u32 {
+ Unset = 0U,
+ PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
+ Syscall = (1U << CapabilityOffset_Syscall) - 1,
+ MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
+ MapIO = (1U << CapabilityOffset_MapIO) - 1,
+ Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
+ ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
+ KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
+ HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
+ Debug = (1U << CapabilityOffset_Debug) - 1,
+ Ignorable = 0xFFFFFFFFU,
+};
+
+// clang-format on
+
+constexpr CapabilityType GetCapabilityType(u32 value) {
+ return static_cast<CapabilityType>((~value & (value + 1)) - 1);
+}
+
+u32 GetFlagBitOffset(CapabilityType type) {
+ const auto value = static_cast<u32>(type);
+ return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value));
+}
+
+} // Anonymous namespace
+
+ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
+ std::size_t num_capabilities,
+ VMManager& vm_manager) {
+ Clear();
+
+ // Allow all cores and priorities.
+ core_mask = 0xF;
+ priority_mask = 0xFFFFFFFFFFFFFFFF;
+ kernel_version = PackedKernelVersion;
+
+ return ParseCapabilities(capabilities, num_capabilities, vm_manager);
+}
+
+ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
+ std::size_t num_capabilities,
+ VMManager& vm_manager) {
+ Clear();
+
+ return ParseCapabilities(capabilities, num_capabilities, vm_manager);
+}
+
+void ProcessCapabilities::InitializeForMetadatalessProcess() {
+ // Allow all cores and priorities
+ core_mask = 0xF;
+ priority_mask = 0xFFFFFFFFFFFFFFFF;
+ kernel_version = PackedKernelVersion;
+
+ // Allow all system calls and interrupts.
+ svc_capabilities.set();
+ interrupt_capabilities.set();
+
+ // Allow using the maximum possible amount of handles
+ handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT);
+
+ // Allow all debugging capabilities.
+ is_debuggable = true;
+ can_force_debug = true;
+}
+
+ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
+ std::size_t num_capabilities,
+ VMManager& vm_manager) {
+ u32 set_flags = 0;
+ u32 set_svc_bits = 0;
+
+ for (std::size_t i = 0; i < num_capabilities; ++i) {
+ const u32 descriptor = capabilities[i];
+ const auto type = GetCapabilityType(descriptor);
+
+ if (type == CapabilityType::MapPhysical) {
+ i++;
+
+ // The MapPhysical type uses two descriptor flags for its parameters.
+ // If there's only one, then there's a problem.
+ if (i >= num_capabilities) {
+ return ERR_INVALID_COMBINATION;
+ }
+
+ const auto size_flags = capabilities[i];
+ if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
+ return ERR_INVALID_COMBINATION;
+ }
+
+ const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager);
+ if (result.IsError()) {
+ return result;
+ }
+ } else {
+ const auto result =
+ ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager);
+ if (result.IsError()) {
+ return result;
+ }
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
+ u32 flag, VMManager& vm_manager) {
+ const auto type = GetCapabilityType(flag);
+
+ if (type == CapabilityType::Unset) {
+ return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+ }
+
+ // Bail early on ignorable entries, as one would expect,
+ // ignorable descriptors can be ignored.
+ if (type == CapabilityType::Ignorable) {
+ return RESULT_SUCCESS;
+ }
+
+ // Ensure that the give flag hasn't already been initialized before.
+ // If it has been, then bail.
+ const u32 flag_length = GetFlagBitOffset(type);
+ const u32 set_flag = 1U << flag_length;
+ if ((set_flag & set_flags & InitializeOnceMask) != 0) {
+ return ERR_INVALID_COMBINATION;
+ }
+ set_flags |= set_flag;
+
+ switch (type) {
+ case CapabilityType::PriorityAndCoreNum:
+ return HandlePriorityCoreNumFlags(flag);
+ case CapabilityType::Syscall:
+ return HandleSyscallFlags(set_svc_bits, flag);
+ case CapabilityType::MapIO:
+ return HandleMapIOFlags(flag, vm_manager);
+ case CapabilityType::Interrupt:
+ return HandleInterruptFlags(flag);
+ case CapabilityType::ProgramType:
+ return HandleProgramTypeFlags(flag);
+ case CapabilityType::KernelVersion:
+ return HandleKernelVersionFlags(flag);
+ case CapabilityType::HandleTableSize:
+ return HandleHandleTableFlags(flag);
+ case CapabilityType::Debug:
+ return HandleDebugFlags(flag);
+ default:
+ break;
+ }
+
+ return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+}
+
+void ProcessCapabilities::Clear() {
+ svc_capabilities.reset();
+ interrupt_capabilities.reset();
+
+ core_mask = 0;
+ priority_mask = 0;
+
+ handle_table_size = 0;
+ kernel_version = 0;
+
+ is_debuggable = false;
+ can_force_debug = false;
+}
+
+ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
+ // TODO: Implement
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
+ // TODO: Implement
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
+ VMManager& vm_manager) {
+ // TODO(Lioncache): Implement once the memory manager can handle this.
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) {
+ // TODO(Lioncache): Implement once the memory manager can handle this.
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
+ // TODO: Implement
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
+ // TODO: Implement
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
+ // TODO: Implement
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
+ // TODO: Implement
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
+ // TODO: Implement
+ return RESULT_SUCCESS;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
new file mode 100644
index 000000000..5cff10476
--- /dev/null
+++ b/src/core/hle/kernel/process_capability.h
@@ -0,0 +1,209 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <bitset>
+
+#include "common/common_types.h"
+
+union ResultCode;
+
+namespace Kernel {
+
+class VMManager;
+
+/// Handles kernel capability descriptors that are provided by
+/// application metadata. These descriptors provide information
+/// that alters certain parameters for kernel process instance
+/// that will run said application (or applet).
+///
+/// Capabilities are a sequence of flag descriptors, that indicate various
+/// configurations and constraints for a particular process.
+///
+/// Flag types are indicated by a sequence of set low bits. E.g. the
+/// types are indicated with the low bits as follows (where x indicates "don't care"):
+///
+/// - Priority and core mask : 0bxxxxxxxxxxxx0111
+/// - Allowed service call mask: 0bxxxxxxxxxxx01111
+/// - Map physical memory : 0bxxxxxxxxx0111111
+/// - Map IO memory : 0bxxxxxxxx01111111
+/// - Interrupts : 0bxxxx011111111111
+/// - Application type : 0bxx01111111111111
+/// - Kernel version : 0bx011111111111111
+/// - Handle table size : 0b0111111111111111
+/// - Debugger flags : 0b1111111111111111
+///
+/// These are essentially a bit offset subtracted by 1 to create a mask.
+/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
+/// subtracted by one (7 -> 0b0111)
+///
+/// An example of a bit layout (using the map physical layout):
+/// <example>
+/// The MapPhysical type indicates a sequence entry pair of:
+///
+/// [initial, memory_flags], where:
+///
+/// initial:
+/// bits:
+/// 7-24: Starting page to map memory at.
+/// 25 : Indicates if the memory should be mapped as read only.
+///
+/// memory_flags:
+/// bits:
+/// 7-20 : Number of pages to map
+/// 21-25: Seems to be reserved (still checked against though)
+/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
+/// </example>
+///
+class ProcessCapabilities {
+public:
+ using InterruptCapabilities = std::bitset<1024>;
+ using SyscallCapabilities = std::bitset<128>;
+
+ ProcessCapabilities() = default;
+ ProcessCapabilities(const ProcessCapabilities&) = delete;
+ ProcessCapabilities(ProcessCapabilities&&) = default;
+
+ ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
+ ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
+
+ /// Initializes this process capabilities instance for a kernel process.
+ ///
+ /// @param capabilities The capabilities to parse
+ /// @param num_capabilities The number of capabilities to parse.
+ /// @param vm_manager The memory manager to use for handling any mapping-related
+ /// operations (such as mapping IO memory, etc).
+ ///
+ /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
+ /// otherwise, an error code upon failure.
+ ///
+ ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
+ VMManager& vm_manager);
+
+ /// Initializes this process capabilities instance for a userland process.
+ ///
+ /// @param capabilities The capabilities to parse.
+ /// @param num_capabilities The total number of capabilities to parse.
+ /// @param vm_manager The memory manager to use for handling any mapping-related
+ /// operations (such as mapping IO memory, etc).
+ ///
+ /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
+ /// otherwise, an error code upon failure.
+ ///
+ ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
+ VMManager& vm_manager);
+
+ /// Initializes this process capabilities instance for a process that does not
+ /// have any metadata to parse.
+ ///
+ /// This is necessary, as we allow running raw executables, and the internal
+ /// kernel process capabilities also determine what CPU cores the process is
+ /// allowed to run on, and what priorities are allowed for threads. It also
+ /// determines the max handle table size, what the program type is, whether or
+ /// not the process can be debugged, or whether it's possible for a process to
+ /// forcibly debug another process.
+ ///
+ /// Given the above, this essentially enables all capabilities across the board
+ /// for the process. It allows the process to:
+ ///
+ /// - Run on any core
+ /// - Use any thread priority
+ /// - Use the maximum amount of handles a process is allowed to.
+ /// - Be debuggable
+ /// - Forcibly debug other processes.
+ ///
+ /// Note that this is not a behavior that the kernel allows a process to do via
+ /// a single function like this. This is yuzu-specific behavior to handle
+ /// executables with no capability descriptors whatsoever to derive behavior from.
+ /// It being yuzu-specific is why this is also not the default behavior and not
+ /// done by default in the constructor.
+ ///
+ void InitializeForMetadatalessProcess();
+
+private:
+ /// Attempts to parse a given sequence of capability descriptors.
+ ///
+ /// @param capabilities The sequence of capability descriptors to parse.
+ /// @param num_capabilities The number of descriptors within the given sequence.
+ /// @param vm_manager The memory manager that will perform any memory
+ /// mapping if necessary.
+ ///
+ /// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
+ ///
+ ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
+ VMManager& vm_manager);
+
+ /// Attempts to parse a capability descriptor that is only represented by a
+ /// single flag set.
+ ///
+ /// @param set_flags Running set of flags that are used to catch
+ /// flags being initialized more than once when they shouldn't be.
+ /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
+ /// @param flag The flag to attempt to parse.
+ /// @param vm_manager The memory manager that will perform any memory
+ /// mapping if necessary.
+ ///
+ /// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
+ ///
+ ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
+ VMManager& vm_manager);
+
+ /// Clears the internal state of this process capability instance. Necessary,
+ /// to have a sane starting point due to us allowing running executables without
+ /// configuration metadata. We assume a process is not going to have metadata,
+ /// and if it turns out that the process does, in fact, have metadata, then
+ /// we attempt to parse it. Thus, we need this to reset data members back to
+ /// a good state.
+ ///
+ /// DO NOT ever make this a public member function. This isn't an invariant
+ /// anything external should depend upon (and if anything comes to rely on it,
+ /// you should immediately be questioning the design of that thing, not this
+ /// class. If the kernel itself can run without depending on behavior like that,
+ /// then so can yuzu).
+ ///
+ void Clear();
+
+ /// Handles flags related to the priority and core number capability flags.
+ ResultCode HandlePriorityCoreNumFlags(u32 flags);
+
+ /// Handles flags related to determining the allowable SVC mask.
+ ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
+
+ /// Handles flags related to mapping physical memory pages.
+ ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager);
+
+ /// Handles flags related to mapping IO pages.
+ ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager);
+
+ /// Handles flags related to the interrupt capability flags.
+ ResultCode HandleInterruptFlags(u32 flags);
+
+ /// Handles flags related to the program type.
+ ResultCode HandleProgramTypeFlags(u32 flags);
+
+ /// Handles flags related to the handle table size.
+ ResultCode HandleHandleTableFlags(u32 flags);
+
+ /// Handles flags related to the kernel version capability flags.
+ ResultCode HandleKernelVersionFlags(u32 flags);
+
+ /// Handles flags related to debug-specific capabilities.
+ ResultCode HandleDebugFlags(u32 flags);
+
+ SyscallCapabilities svc_capabilities;
+ InterruptCapabilities interrupt_capabilities;
+
+ u64 core_mask = 0;
+ u64 priority_mask = 0;
+
+ u32 handle_table_size = 0;
+ u32 kernel_version = 0;
+ u32 program_type = 0;
+
+ bool is_debuggable = false;
+ bool can_force_debug = false;
+};
+
+} // namespace Kernel