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.h | ||||
|     bit_field.h | ||||
|     bit_util.h | ||||
|     cityhash.cpp | ||||
|     cityhash.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/process.cpp | ||||
|     hle/kernel/process.h | ||||
|     hle/kernel/process_capability.cpp | ||||
|     hle/kernel/process_capability.h | ||||
|     hle/kernel/readable_event.cpp | ||||
|     hle/kernel/readable_event.h | ||||
|     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)) | ||||
|         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; | ||||
| } | ||||
|  | ||||
| @@ -71,6 +78,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const { | ||||
|     return aci_file_access.permissions; | ||||
| } | ||||
|  | ||||
| const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { | ||||
|     return aci_kernel_capabilities; | ||||
| } | ||||
|  | ||||
| void ProgramMetadata::Print() const { | ||||
|     LOG_DEBUG(Service_FS, "Magic:                  {:.4}", npdm_header.magic.data()); | ||||
|     LOG_DEBUG(Service_FS, "Main thread priority:   0x{:02X}", npdm_header.main_thread_priority); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <vector> | ||||
| #include "common/bit_field.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/swap.h" | ||||
| @@ -38,6 +39,8 @@ enum class ProgramFilePermission : u64 { | ||||
|  */ | ||||
| class ProgramMetadata { | ||||
| public: | ||||
|     using KernelCapabilityDescriptors = std::vector<u32>; | ||||
|  | ||||
|     ProgramMetadata(); | ||||
|     ~ProgramMetadata(); | ||||
|  | ||||
| @@ -50,6 +53,7 @@ public: | ||||
|     u32 GetMainThreadStackSize() const; | ||||
|     u64 GetTitleID() const; | ||||
|     u64 GetFilesystemPermissions() const; | ||||
|     const KernelCapabilityDescriptors& GetKernelCapabilities() const; | ||||
|  | ||||
|     void Print() const; | ||||
|  | ||||
| @@ -154,6 +158,8 @@ private: | ||||
|  | ||||
|     FileAccessControl acid_file_access; | ||||
|     FileAccessHeader aci_file_access; | ||||
|  | ||||
|     KernelCapabilityDescriptors aci_kernel_capabilities; | ||||
| }; | ||||
|  | ||||
| } // namespace FileSys | ||||
|   | ||||
| @@ -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 | ||||
| @@ -129,7 +129,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) | ||||
|         return ResultStatus::Error32BitISA; | ||||
|     } | ||||
|  | ||||
|     process.LoadFromMetadata(metadata); | ||||
|     if (process.LoadFromMetadata(metadata).IsError()) { | ||||
|         return ResultStatus::ErrorUnableToParseKernelMetadata; | ||||
|     } | ||||
|  | ||||
|     const FileSys::PatchManager pm(metadata.GetTitleID()); | ||||
|  | ||||
|     // Load NSO modules | ||||
|   | ||||
| @@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) { | ||||
|     return "unknown"; | ||||
| } | ||||
|  | ||||
| constexpr std::array<const char*, 60> RESULT_MESSAGES{ | ||||
| constexpr std::array<const char*, 62> RESULT_MESSAGES{ | ||||
|     "The operation completed successfully.", | ||||
|     "The loader requested to load is already loaded.", | ||||
|     "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 file has a bad file access control.", | ||||
|     "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 incorrect size as determined by the 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 game is missing a program metadata file (main.npdm).", | ||||
|     "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 ELF file has incorrect size as determined by the header.", | ||||
|     "There was a general error loading the NRO into emulated memory.", | ||||
|   | ||||
| @@ -71,6 +71,7 @@ enum class ResultStatus : u16 { | ||||
|     ErrorBadACIHeader, | ||||
|     ErrorBadFileAccessControl, | ||||
|     ErrorBadFileAccessHeader, | ||||
|     ErrorBadKernelCapabilityDescriptors, | ||||
|     ErrorBadPFSHeader, | ||||
|     ErrorIncorrectPFSFileSize, | ||||
|     ErrorBadNCAHeader, | ||||
| @@ -93,6 +94,7 @@ enum class ResultStatus : u16 { | ||||
|     ErrorNullFile, | ||||
|     ErrorMissingNPDM, | ||||
|     Error32BitISA, | ||||
|     ErrorUnableToParseKernelMetadata, | ||||
|     ErrorNoRomFS, | ||||
|     ErrorIncorrectELFFileSize, | ||||
|     ErrorLoadingNRO, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user