kernel/process: Hook up the process capability parser to the process itself
While we're at it, we can also toss out the leftover capability parsing from Citra.
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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. | ||||
|  * | ||||
| @@ -180,13 +162,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 { | ||||
| @@ -227,15 +209,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. | ||||
| @@ -296,22 +275,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, | ||||
| @@ -321,6 +286,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. | ||||
|   | ||||
| @@ -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.", | ||||
|   | ||||
| @@ -67,6 +67,7 @@ enum class ResultStatus : u16 { | ||||
|     ErrorBadACIHeader, | ||||
|     ErrorBadFileAccessControl, | ||||
|     ErrorBadFileAccessHeader, | ||||
|     ErrorBadKernelCapabilityDescriptors, | ||||
|     ErrorBadPFSHeader, | ||||
|     ErrorIncorrectPFSFileSize, | ||||
|     ErrorBadNCAHeader, | ||||
| @@ -89,6 +90,7 @@ enum class ResultStatus : u16 { | ||||
|     ErrorNullFile, | ||||
|     ErrorMissingNPDM, | ||||
|     Error32BitISA, | ||||
|     ErrorUnableToParseKernelMetadata, | ||||
|     ErrorNoRomFS, | ||||
|     ErrorIncorrectELFFileSize, | ||||
|     ErrorLoadingNRO, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user