forked from eden-emu/eden
		
	Merge pull request #1928 from lioncash/caps
kernel: Handle kernel capability descriptors
This commit is contained in:
		
						commit
						795335af0f
					
				
					 14 changed files with 732 additions and 125 deletions
				
			
		|  | @ -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
 | ||||
|  |  | |||
|  | @ -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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|             } | ||||
| 
 | ||||
|             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) { | ||||
|  |  | |||
|  | @ -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. | ||||
|      * @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 LoadFromMetadata(const FileSys::ProgramMetadata& metadata); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them | ||||
|      * to this process. | ||||
|      */ | ||||
|     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.
 | ||||
|  |  | |||
							
								
								
									
										355
									
								
								src/core/hle/kernel/process_capability.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								src/core/hle/kernel/process_capability.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||
							
								
								
									
										264
									
								
								src/core/hle/kernel/process_capability.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										264
									
								
								src/core/hle/kernel/process_capability.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bunnei
						bunnei