diff options
Diffstat (limited to 'src/core/hle')
-rw-r--r-- | src/core/hle/kernel/errors.h | 2 | ||||
-rw-r--r-- | src/core/hle/kernel/handle_table.h | 6 | ||||
-rw-r--r-- | src/core/hle/kernel/process.cpp | 80 | ||||
-rw-r--r-- | src/core/hle/kernel/process.h | 58 | ||||
-rw-r--r-- | src/core/hle/kernel/process_capability.cpp | 355 | ||||
-rw-r--r-- | src/core/hle/kernel/process_capability.h | 264 |
6 files changed, 642 insertions, 123 deletions
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index d8240ec6d..d17eb0cb6 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}; @@ -30,6 +31,7 @@ constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122}; constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; +constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126}; constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; } // namespace Kernel 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.cpp b/src/core/hle/kernel/process.cpp index 5356a4a3f..4f209a979 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -28,13 +28,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { SharedPtr<Process> process(new Process(kernel)); process->name = std::move(name); - process->flags.raw = 0; - process->flags.memory_region.Assign(MemoryRegion::APPLICATION); process->resource_limit = kernel.GetSystemResourceLimit(); process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = kernel.CreateNewProcessID(); - process->svc_access_mask.set(); + process->capabilities.InitializeForMetadatalessProcess(); std::mt19937 rng(Settings::values.rng_seed.value_or(0)); std::uniform_int_distribution<u64> distribution; @@ -64,83 +62,15 @@ ResultCode Process::ClearSignalState() { return RESULT_SUCCESS; } -void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { +ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { program_id = metadata.GetTitleID(); ideal_processor = metadata.GetMainThreadCore(); is_64bit_process = metadata.Is64BitProgram(); - vm_manager.Reset(metadata.GetAddressSpaceType()); -} - -void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { - for (std::size_t i = 0; i < len; ++i) { - u32 descriptor = kernel_caps[i]; - u32 type = descriptor >> 20; - - if (descriptor == 0xFFFFFFFF) { - // Unused descriptor entry - continue; - } else if ((type & 0xF00) == 0xE00) { // 0x0FFF - // Allowed interrupts list - LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); - } else if ((type & 0xF80) == 0xF00) { // 0x07FF - // Allowed syscalls mask - unsigned int index = ((descriptor >> 24) & 7) * 24; - u32 bits = descriptor & 0xFFFFFF; - - while (bits && index < svc_access_mask.size()) { - svc_access_mask.set(index, bits & 1); - ++index; - bits >>= 1; - } - } else if ((type & 0xFF0) == 0xFE0) { // 0x00FF - // Handle table size - handle_table_size = descriptor & 0x3FF; - } else if ((type & 0xFF8) == 0xFF0) { // 0x007F - // Misc. flags - flags.raw = descriptor & 0xFFFF; - } else if ((type & 0xFFE) == 0xFF8) { // 0x001F - // Mapped memory range - if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) { - LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); - continue; - } - u32 end_desc = kernel_caps[i + 1]; - ++i; // Skip over the second descriptor on the next iteration - AddressMapping mapping; - mapping.address = descriptor << 12; - VAddr end_address = end_desc << 12; - - if (mapping.address < end_address) { - mapping.size = end_address - mapping.address; - } else { - mapping.size = 0; - } + vm_manager.Reset(metadata.GetAddressSpaceType()); - mapping.read_only = (descriptor & (1 << 20)) != 0; - mapping.unk_flag = (end_desc & (1 << 20)) != 0; - - address_mappings.push_back(mapping); - } else if ((type & 0xFFF) == 0xFFE) { // 0x000F - // Mapped memory page - AddressMapping mapping; - mapping.address = descriptor << 12; - mapping.size = Memory::PAGE_SIZE; - mapping.read_only = false; - mapping.unk_flag = false; - - address_mappings.push_back(mapping); - } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF - // Kernel version - kernel_version = descriptor & 0xFFFF; - - int minor = kernel_version & 0xFF; - int major = (kernel_version >> 8) & 0xFF; - LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor); - } else { - LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor); - } - } + const auto& caps = metadata.GetKernelCapabilities(); + return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); } void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 7da367251..2c0b20f9e 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -11,9 +11,9 @@ #include <string> #include <vector> #include <boost/container/static_vector.hpp> -#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/wait_object.h" @@ -42,24 +42,6 @@ enum class MemoryRegion : u16 { BASE = 3, }; -union ProcessFlags { - u16 raw; - - BitField<0, 1, u16> - allow_debug; ///< Allows other processes to attach to and debug this process. - BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they - /// don't have allow_debug set. - BitField<2, 1, u16> allow_nonalphanum; - BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions. - BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24. - BitField<5, 1, u16> allow_main_args; - BitField<6, 1, u16> shared_device_mem; - BitField<7, 1, u16> runnable_on_sleep; - BitField<8, 4, MemoryRegion> - memory_region; ///< Default region for memory allocations for this process - BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). -}; - /** * Indicates the status of a Process instance. * @@ -192,13 +174,13 @@ public: } /// Gets the bitmask of allowed CPUs that this process' threads can run on. - u32 GetAllowedProcessorMask() const { - return allowed_processor_mask; + u64 GetAllowedProcessorMask() const { + return capabilities.GetCoreMask(); } /// Gets the bitmask of allowed thread priorities. - u32 GetAllowedThreadPriorityMask() const { - return allowed_thread_priority_mask; + u64 GetAllowedThreadPriorityMask() const { + return capabilities.GetPriorityMask(); } u32 IsVirtualMemoryEnabled() const { @@ -239,15 +221,12 @@ public: * Loads process-specifics configuration info with metadata provided * by an executable. * - * @param metadata The provided metadata to load process specific info. - */ - void LoadFromMetadata(const FileSys::ProgramMetadata& metadata); - - /** - * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them - * to this process. + * @param metadata The provided metadata to load process specific info from. + * + * @returns RESULT_SUCCESS if all relevant metadata was able to be + * loaded and parsed. Otherwise, an error code is returned. */ - void ParseKernelCaps(const u32* kernel_caps, std::size_t len); + ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); /** * Applies address space changes and launches the process main thread. @@ -308,22 +287,8 @@ private: /// Resource limit descriptor for this process SharedPtr<ResourceLimit> resource_limit; - /// The process may only call SVCs which have the corresponding bit set. - std::bitset<0x80> svc_access_mask; - /// Maximum size of the handle table for the process. - u32 handle_table_size = 0x200; - /// Special memory ranges mapped into this processes address space. This is used to give - /// processes access to specific I/O regions and device memory. - boost::container::static_vector<AddressMapping, 8> address_mappings; - ProcessFlags flags; - /// Kernel compatibility version for this process - u16 kernel_version = 0; /// The default CPU for this process, threads are scheduled on this cpu by default. u8 ideal_processor = 0; - /// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse - /// this value from the process header. - u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; - u32 allowed_thread_priority_mask = 0xFFFFFFFF; u32 is_virtual_address_memory_enabled = 0; /// The Thread Local Storage area is allocated as processes create threads, @@ -333,6 +298,9 @@ private: /// This vector will grow as more pages are allocated for new threads. std::vector<std::bitset<8>> tls_slots; + /// Contains the parsed process capability descriptors. + ProcessCapabilities capabilities; + /// Whether or not this process is AArch64, or AArch32. /// By default, we currently assume this is true, unless otherwise /// specified by metadata provided to the process during loading. diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp new file mode 100644 index 000000000..3a2164b25 --- /dev/null +++ b/src/core/hle/kernel/process_capability.cpp @@ -0,0 +1,355 @@ +// 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; + + program_type = ProgramType::SysModule; + + is_debuggable = false; + can_force_debug = false; +} + +ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { + if (priority_mask != 0 || core_mask != 0) { + return ERR_INVALID_CAPABILITY_DESCRIPTOR; + } + + const u32 core_num_min = (flags >> 16) & 0xFF; + const u32 core_num_max = (flags >> 24) & 0xFF; + if (core_num_min > core_num_max) { + return ERR_INVALID_COMBINATION; + } + + const u32 priority_min = (flags >> 10) & 0x3F; + const u32 priority_max = (flags >> 4) & 0x3F; + if (priority_min > priority_max) { + return ERR_INVALID_COMBINATION; + } + + // The switch only has 4 usable cores. + if (core_num_max >= 4) { + return ERR_INVALID_PROCESSOR_ID; + } + + const auto make_mask = [](u64 min, u64 max) { + const u64 range = max - min + 1; + const u64 mask = (1ULL << range) - 1; + + return mask << min; + }; + + core_mask = make_mask(core_num_min, core_num_max); + priority_mask = make_mask(priority_min, priority_max); + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { + const u32 index = flags >> 29; + const u32 svc_bit = 1U << index; + + // If we've already set this svc before, bail. + if ((set_svc_bits & svc_bit) != 0) { + return ERR_INVALID_COMBINATION; + } + set_svc_bits |= svc_bit; + + const u32 svc_mask = (flags >> 5) & 0xFFFFFF; + for (u32 i = 0; i < 24; ++i) { + const u32 svc_number = index * 24 + i; + + if ((svc_mask & (1U << i)) == 0) { + continue; + } + + if (svc_number >= svc_capabilities.size()) { + return ERR_OUT_OF_RANGE; + } + + svc_capabilities[svc_number] = true; + } + + 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) { + constexpr u32 interrupt_ignore_value = 0x3FF; + const u32 interrupt0 = (flags >> 12) & 0x3FF; + const u32 interrupt1 = (flags >> 22) & 0x3FF; + + for (u32 interrupt : {interrupt0, interrupt1}) { + if (interrupt == interrupt_ignore_value) { + continue; + } + + // NOTE: + // This should be checking a generic interrupt controller value + // as part of the calculation, however, given we don't currently + // emulate that, it's sufficient to mark every interrupt as defined. + + if (interrupt >= interrupt_capabilities.size()) { + return ERR_OUT_OF_RANGE; + } + + interrupt_capabilities[interrupt] = true; + } + + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { + const u32 reserved = flags >> 17; + if (reserved != 0) { + return ERR_RESERVED_VALUE; + } + + program_type = static_cast<ProgramType>((flags >> 14) & 0b111); + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { + // Yes, the internal member variable is checked in the actual kernel here. + // This might look odd for options that are only allowed to be initialized + // just once, however the kernel has a separate initialization function for + // kernel processes and userland processes. The kernel variant sets this + // member variable ahead of time. + + const u32 major_version = kernel_version >> 19; + + if (major_version != 0 || flags < 0x80000) { + return ERR_INVALID_CAPABILITY_DESCRIPTOR; + } + + kernel_version = flags; + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { + const u32 reserved = flags >> 26; + if (reserved != 0) { + return ERR_RESERVED_VALUE; + } + + handle_table_size = (flags >> 16) & 0x3FF; + return RESULT_SUCCESS; +} + +ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) { + const u32 reserved = flags >> 19; + if (reserved != 0) { + return ERR_RESERVED_VALUE; + } + + is_debuggable = (flags & 0x20000) != 0; + can_force_debug = (flags & 0x40000) != 0; + 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..fbc8812a3 --- /dev/null +++ b/src/core/hle/kernel/process_capability.h @@ -0,0 +1,264 @@ +// 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; + +/// The possible types of programs that may be indicated +/// by the program type capability descriptor. +enum class ProgramType { + SysModule, + Application, + Applet, +}; + +/// 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(); + + /// Gets the allowable core mask + u64 GetCoreMask() const { + return core_mask; + } + + /// Gets the allowable priority mask + u64 GetPriorityMask() const { + return priority_mask; + } + + /// Gets the SVC access permission bits + const SyscallCapabilities& GetServiceCapabilities() const { + return svc_capabilities; + } + + /// Gets the valid interrupt bits. + const InterruptCapabilities& GetInterruptCapabilities() const { + return interrupt_capabilities; + } + + /// Gets the program type for this process. + ProgramType GetProgramType() const { + return program_type; + } + + /// Gets the number of total allowable handles for the process' handle table. + u32 GetHandleTableSize() const { + return handle_table_size; + } + + /// Gets the kernel version value. + u32 GetKernelVersion() const { + return kernel_version; + } + + /// Whether or not this process can be debugged. + bool IsDebuggable() const { + return is_debuggable; + } + + /// Whether or not this process can forcibly debug another + /// process, even if that process is not considered debuggable. + bool CanForceDebug() const { + return can_force_debug; + } + +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; + + ProgramType program_type = ProgramType::SysModule; + + bool is_debuggable = false; + bool can_force_debug = false; +}; + +} // namespace Kernel |