Merge pull request #1928 from lioncash/caps
kernel: Handle kernel capability descriptors
This commit is contained in:
		| @@ -44,6 +44,7 @@ add_library(common STATIC | |||||||
|     detached_tasks.cpp |     detached_tasks.cpp | ||||||
|     detached_tasks.h |     detached_tasks.h | ||||||
|     bit_field.h |     bit_field.h | ||||||
|  |     bit_util.h | ||||||
|     cityhash.cpp |     cityhash.cpp | ||||||
|     cityhash.h |     cityhash.h | ||||||
|     color.h |     color.h | ||||||
|   | |||||||
							
								
								
									
										61
									
								
								src/common/bit_util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/common/bit_util.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | // Copyright 2018 yuzu emulator team | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <climits> | ||||||
|  | #include <cstddef> | ||||||
|  |  | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | #include <intrin.h> | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  |  | ||||||
|  | namespace Common { | ||||||
|  |  | ||||||
|  | /// Gets the size of a specified type T in bits. | ||||||
|  | template <typename T> | ||||||
|  | constexpr std::size_t BitSize() { | ||||||
|  |     return sizeof(T) * CHAR_BIT; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | inline u32 CountLeadingZeroes32(u32 value) { | ||||||
|  |     unsigned long leading_zero = 0; | ||||||
|  |  | ||||||
|  |     if (_BitScanReverse(&leading_zero, value) != 0) { | ||||||
|  |         return 31 - leading_zero; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 32; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline u64 CountLeadingZeroes64(u64 value) { | ||||||
|  |     unsigned long leading_zero = 0; | ||||||
|  |  | ||||||
|  |     if (_BitScanReverse64(&leading_zero, value) != 0) { | ||||||
|  |         return 63 - leading_zero; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 64; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | inline u32 CountLeadingZeroes32(u32 value) { | ||||||
|  |     if (value == 0) { | ||||||
|  |         return 32; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return __builtin_clz(value); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline u64 CountLeadingZeroes64(u64 value) { | ||||||
|  |     if (value == 0) { | ||||||
|  |         return 64; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return __builtin_clzll(value); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | } // namespace Common | ||||||
| @@ -115,6 +115,8 @@ add_library(core STATIC | |||||||
|     hle/kernel/object.h |     hle/kernel/object.h | ||||||
|     hle/kernel/process.cpp |     hle/kernel/process.cpp | ||||||
|     hle/kernel/process.h |     hle/kernel/process.h | ||||||
|  |     hle/kernel/process_capability.cpp | ||||||
|  |     hle/kernel/process_capability.h | ||||||
|     hle/kernel/readable_event.cpp |     hle/kernel/readable_event.cpp | ||||||
|     hle/kernel/readable_event.h |     hle/kernel/readable_event.h | ||||||
|     hle/kernel/resource_limit.cpp |     hle/kernel/resource_limit.cpp | ||||||
|   | |||||||
| @@ -40,6 +40,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | |||||||
|     if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) |     if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) | ||||||
|         return Loader::ResultStatus::ErrorBadFileAccessHeader; |         return Loader::ResultStatus::ErrorBadFileAccessHeader; | ||||||
|  |  | ||||||
|  |     aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32)); | ||||||
|  |     const u64 read_size = aci_header.kac_size; | ||||||
|  |     const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset; | ||||||
|  |     if (file->ReadBytes(aci_kernel_capabilities.data(), read_size, read_offset) != read_size) { | ||||||
|  |         return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return Loader::ResultStatus::Success; |     return Loader::ResultStatus::Success; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -71,6 +78,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const { | |||||||
|     return aci_file_access.permissions; |     return aci_file_access.permissions; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { | ||||||
|  |     return aci_kernel_capabilities; | ||||||
|  | } | ||||||
|  |  | ||||||
| void ProgramMetadata::Print() const { | void ProgramMetadata::Print() const { | ||||||
|     LOG_DEBUG(Service_FS, "Magic:                  {:.4}", npdm_header.magic.data()); |     LOG_DEBUG(Service_FS, "Magic:                  {:.4}", npdm_header.magic.data()); | ||||||
|     LOG_DEBUG(Service_FS, "Main thread priority:   0x{:02X}", npdm_header.main_thread_priority); |     LOG_DEBUG(Service_FS, "Main thread priority:   0x{:02X}", npdm_header.main_thread_priority); | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <array> | #include <array> | ||||||
|  | #include <vector> | ||||||
| #include "common/bit_field.h" | #include "common/bit_field.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
| @@ -38,6 +39,8 @@ enum class ProgramFilePermission : u64 { | |||||||
|  */ |  */ | ||||||
| class ProgramMetadata { | class ProgramMetadata { | ||||||
| public: | public: | ||||||
|  |     using KernelCapabilityDescriptors = std::vector<u32>; | ||||||
|  |  | ||||||
|     ProgramMetadata(); |     ProgramMetadata(); | ||||||
|     ~ProgramMetadata(); |     ~ProgramMetadata(); | ||||||
|  |  | ||||||
| @@ -50,6 +53,7 @@ public: | |||||||
|     u32 GetMainThreadStackSize() const; |     u32 GetMainThreadStackSize() const; | ||||||
|     u64 GetTitleID() const; |     u64 GetTitleID() const; | ||||||
|     u64 GetFilesystemPermissions() const; |     u64 GetFilesystemPermissions() const; | ||||||
|  |     const KernelCapabilityDescriptors& GetKernelCapabilities() const; | ||||||
|  |  | ||||||
|     void Print() const; |     void Print() const; | ||||||
|  |  | ||||||
| @@ -154,6 +158,8 @@ private: | |||||||
|  |  | ||||||
|     FileAccessControl acid_file_access; |     FileAccessControl acid_file_access; | ||||||
|     FileAccessHeader aci_file_access; |     FileAccessHeader aci_file_access; | ||||||
|  |  | ||||||
|  |     KernelCapabilityDescriptors aci_kernel_capabilities; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } // namespace FileSys | } // namespace FileSys | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ namespace Kernel { | |||||||
| // Confirmed Switch kernel error codes | // Confirmed Switch kernel error codes | ||||||
|  |  | ||||||
| constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; | 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_SIZE{ErrorModule::Kernel, 101}; | ||||||
| constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; | ||||||
| constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; | 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_BUSY{ErrorModule::Kernel, 122}; | ||||||
| constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; | ||||||
| constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; | 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}; | constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; | ||||||
|  |  | ||||||
| } // namespace Kernel | } // namespace Kernel | ||||||
|   | |||||||
| @@ -43,6 +43,9 @@ enum KernelHandle : Handle { | |||||||
|  */ |  */ | ||||||
| class HandleTable final : NonCopyable { | class HandleTable final : NonCopyable { | ||||||
| public: | public: | ||||||
|  |     /// This is the maximum limit of handles allowed per process in Horizon | ||||||
|  |     static constexpr std::size_t MAX_COUNT = 1024; | ||||||
|  |  | ||||||
|     HandleTable(); |     HandleTable(); | ||||||
|     ~HandleTable(); |     ~HandleTable(); | ||||||
|  |  | ||||||
| @@ -91,9 +94,6 @@ public: | |||||||
|     void Clear(); |     void Clear(); | ||||||
|  |  | ||||||
| private: | 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. |     /// Stores the Object referenced by the handle or null if the slot is empty. | ||||||
|     std::array<SharedPtr<Object>, MAX_COUNT> objects; |     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)); |     SharedPtr<Process> process(new Process(kernel)); | ||||||
|  |  | ||||||
|     process->name = std::move(name); |     process->name = std::move(name); | ||||||
|     process->flags.raw = 0; |  | ||||||
|     process->flags.memory_region.Assign(MemoryRegion::APPLICATION); |  | ||||||
|     process->resource_limit = kernel.GetSystemResourceLimit(); |     process->resource_limit = kernel.GetSystemResourceLimit(); | ||||||
|     process->status = ProcessStatus::Created; |     process->status = ProcessStatus::Created; | ||||||
|     process->program_id = 0; |     process->program_id = 0; | ||||||
|     process->process_id = kernel.CreateNewProcessID(); |     process->process_id = kernel.CreateNewProcessID(); | ||||||
|     process->svc_access_mask.set(); |     process->capabilities.InitializeForMetadatalessProcess(); | ||||||
|  |  | ||||||
|     std::mt19937 rng(Settings::values.rng_seed.value_or(0)); |     std::mt19937 rng(Settings::values.rng_seed.value_or(0)); | ||||||
|     std::uniform_int_distribution<u64> distribution; |     std::uniform_int_distribution<u64> distribution; | ||||||
| @@ -64,83 +62,15 @@ ResultCode Process::ClearSignalState() { | |||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { | ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { | ||||||
|     program_id = metadata.GetTitleID(); |     program_id = metadata.GetTitleID(); | ||||||
|     ideal_processor = metadata.GetMainThreadCore(); |     ideal_processor = metadata.GetMainThreadCore(); | ||||||
|     is_64bit_process = metadata.Is64BitProgram(); |     is_64bit_process = metadata.Is64BitProgram(); | ||||||
|  |  | ||||||
|     vm_manager.Reset(metadata.GetAddressSpaceType()); |     vm_manager.Reset(metadata.GetAddressSpaceType()); | ||||||
| } |  | ||||||
|  |  | ||||||
| void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { |     const auto& caps = metadata.GetKernelCapabilities(); | ||||||
|     for (std::size_t i = 0; i < len; ++i) { |     return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); | ||||||
|         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); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | ||||||
|   | |||||||
| @@ -11,9 +11,9 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <boost/container/static_vector.hpp> | #include <boost/container/static_vector.hpp> | ||||||
| #include "common/bit_field.h" |  | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "core/hle/kernel/handle_table.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/thread.h" | ||||||
| #include "core/hle/kernel/vm_manager.h" | #include "core/hle/kernel/vm_manager.h" | ||||||
| #include "core/hle/kernel/wait_object.h" | #include "core/hle/kernel/wait_object.h" | ||||||
| @@ -42,24 +42,6 @@ enum class MemoryRegion : u16 { | |||||||
|     BASE = 3, |     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. |  * 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. |     /// Gets the bitmask of allowed CPUs that this process' threads can run on. | ||||||
|     u32 GetAllowedProcessorMask() const { |     u64 GetAllowedProcessorMask() const { | ||||||
|         return allowed_processor_mask; |         return capabilities.GetCoreMask(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Gets the bitmask of allowed thread priorities. |     /// Gets the bitmask of allowed thread priorities. | ||||||
|     u32 GetAllowedThreadPriorityMask() const { |     u64 GetAllowedThreadPriorityMask() const { | ||||||
|         return allowed_thread_priority_mask; |         return capabilities.GetPriorityMask(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     u32 IsVirtualMemoryEnabled() const { |     u32 IsVirtualMemoryEnabled() const { | ||||||
| @@ -239,15 +221,12 @@ public: | |||||||
|      * Loads process-specifics configuration info with metadata provided |      * Loads process-specifics configuration info with metadata provided | ||||||
|      * by an executable. |      * 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); |     ResultCode 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); |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Applies address space changes and launches the process main thread. |      * Applies address space changes and launches the process main thread. | ||||||
| @@ -308,22 +287,8 @@ private: | |||||||
|     /// Resource limit descriptor for this process |     /// Resource limit descriptor for this process | ||||||
|     SharedPtr<ResourceLimit> resource_limit; |     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. |     /// The default CPU for this process, threads are scheduled on this cpu by default. | ||||||
|     u8 ideal_processor = 0; |     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; |     u32 is_virtual_address_memory_enabled = 0; | ||||||
|  |  | ||||||
|     /// The Thread Local Storage area is allocated as processes create threads, |     /// 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. |     /// This vector will grow as more pages are allocated for new threads. | ||||||
|     std::vector<std::bitset<8>> tls_slots; |     std::vector<std::bitset<8>> tls_slots; | ||||||
|  |  | ||||||
|  |     /// Contains the parsed process capability descriptors. | ||||||
|  |     ProcessCapabilities capabilities; | ||||||
|  |  | ||||||
|     /// Whether or not this process is AArch64, or AArch32. |     /// Whether or not this process is AArch64, or AArch32. | ||||||
|     /// By default, we currently assume this is true, unless otherwise |     /// By default, we currently assume this is true, unless otherwise | ||||||
|     /// specified by metadata provided to the process during loading. |     /// 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 | ||||||
| @@ -129,7 +129,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) | |||||||
|         return ResultStatus::Error32BitISA; |         return ResultStatus::Error32BitISA; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     process.LoadFromMetadata(metadata); |     if (process.LoadFromMetadata(metadata).IsError()) { | ||||||
|  |         return ResultStatus::ErrorUnableToParseKernelMetadata; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const FileSys::PatchManager pm(metadata.GetTitleID()); |     const FileSys::PatchManager pm(metadata.GetTitleID()); | ||||||
|  |  | ||||||
|     // Load NSO modules |     // Load NSO modules | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) { | |||||||
|     return "unknown"; |     return "unknown"; | ||||||
| } | } | ||||||
|  |  | ||||||
| constexpr std::array<const char*, 60> RESULT_MESSAGES{ | constexpr std::array<const char*, 62> RESULT_MESSAGES{ | ||||||
|     "The operation completed successfully.", |     "The operation completed successfully.", | ||||||
|     "The loader requested to load is already loaded.", |     "The loader requested to load is already loaded.", | ||||||
|     "The operation is not implemented.", |     "The operation is not implemented.", | ||||||
| @@ -103,6 +103,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{ | |||||||
|     "The NPDM has a bad ACI header,", |     "The NPDM has a bad ACI header,", | ||||||
|     "The NPDM file has a bad file access control.", |     "The NPDM file has a bad file access control.", | ||||||
|     "The NPDM has a bad file access header.", |     "The NPDM has a bad file access header.", | ||||||
|  |     "The NPDM has bad kernel capability descriptors.", | ||||||
|     "The PFS/HFS partition has a bad header.", |     "The PFS/HFS partition has a bad header.", | ||||||
|     "The PFS/HFS partition has incorrect size as determined by the header.", |     "The PFS/HFS partition has incorrect size as determined by the header.", | ||||||
|     "The NCA file has a bad header.", |     "The NCA file has a bad header.", | ||||||
| @@ -125,6 +126,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{ | |||||||
|     "The file could not be found or does not exist.", |     "The file could not be found or does not exist.", | ||||||
|     "The game is missing a program metadata file (main.npdm).", |     "The game is missing a program metadata file (main.npdm).", | ||||||
|     "The game uses the currently-unimplemented 32-bit architecture.", |     "The game uses the currently-unimplemented 32-bit architecture.", | ||||||
|  |     "Unable to completely parse the kernel metadata when loading the emulated process", | ||||||
|     "The RomFS could not be found.", |     "The RomFS could not be found.", | ||||||
|     "The ELF file has incorrect size as determined by the header.", |     "The ELF file has incorrect size as determined by the header.", | ||||||
|     "There was a general error loading the NRO into emulated memory.", |     "There was a general error loading the NRO into emulated memory.", | ||||||
|   | |||||||
| @@ -71,6 +71,7 @@ enum class ResultStatus : u16 { | |||||||
|     ErrorBadACIHeader, |     ErrorBadACIHeader, | ||||||
|     ErrorBadFileAccessControl, |     ErrorBadFileAccessControl, | ||||||
|     ErrorBadFileAccessHeader, |     ErrorBadFileAccessHeader, | ||||||
|  |     ErrorBadKernelCapabilityDescriptors, | ||||||
|     ErrorBadPFSHeader, |     ErrorBadPFSHeader, | ||||||
|     ErrorIncorrectPFSFileSize, |     ErrorIncorrectPFSFileSize, | ||||||
|     ErrorBadNCAHeader, |     ErrorBadNCAHeader, | ||||||
| @@ -93,6 +94,7 @@ enum class ResultStatus : u16 { | |||||||
|     ErrorNullFile, |     ErrorNullFile, | ||||||
|     ErrorMissingNPDM, |     ErrorMissingNPDM, | ||||||
|     Error32BitISA, |     Error32BitISA, | ||||||
|  |     ErrorUnableToParseKernelMetadata, | ||||||
|     ErrorNoRomFS, |     ErrorNoRomFS, | ||||||
|     ErrorIncorrectELFFileSize, |     ErrorIncorrectELFFileSize, | ||||||
|     ErrorLoadingNRO, |     ErrorLoadingNRO, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user