diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 8903ed1d3f08d85b4f80a6a44ff382536da27cfa..e90c8c2de0519d5266f005076c3bb24b0b77cacd 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.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); diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index e4470d6f086d73937d8ee621892ea8575beb1804..0033ba3475edc55d0ce99f7b608856ddeb2efae2 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -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 diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 5356a4a3f68c42f9eb95056cdfee80b1e9aa5f5e..4f209a979f863d383958d4c172aa2768639c9f08 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -28,13 +28,11 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { SharedPtr<Process> process(new Process(kernel)); process->name = std::move(name); - process->flags.raw = 0; - process->flags.memory_region.Assign(MemoryRegion::APPLICATION); process->resource_limit = kernel.GetSystemResourceLimit(); process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = kernel.CreateNewProcessID(); - process->svc_access_mask.set(); + process->capabilities.InitializeForMetadatalessProcess(); std::mt19937 rng(Settings::values.rng_seed.value_or(0)); std::uniform_int_distribution<u64> distribution; @@ -64,83 +62,15 @@ ResultCode Process::ClearSignalState() { return RESULT_SUCCESS; } -void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { +ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { program_id = metadata.GetTitleID(); ideal_processor = metadata.GetMainThreadCore(); is_64bit_process = metadata.Is64BitProgram(); - vm_manager.Reset(metadata.GetAddressSpaceType()); -} - -void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { - for (std::size_t i = 0; i < len; ++i) { - u32 descriptor = kernel_caps[i]; - u32 type = descriptor >> 20; - - if (descriptor == 0xFFFFFFFF) { - // Unused descriptor entry - continue; - } else if ((type & 0xF00) == 0xE00) { // 0x0FFF - // Allowed interrupts list - LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); - } else if ((type & 0xF80) == 0xF00) { // 0x07FF - // Allowed syscalls mask - unsigned int index = ((descriptor >> 24) & 7) * 24; - u32 bits = descriptor & 0xFFFFFF; - - while (bits && index < svc_access_mask.size()) { - svc_access_mask.set(index, bits & 1); - ++index; - bits >>= 1; - } - } else if ((type & 0xFF0) == 0xFE0) { // 0x00FF - // Handle table size - handle_table_size = descriptor & 0x3FF; - } else if ((type & 0xFF8) == 0xFF0) { // 0x007F - // Misc. flags - flags.raw = descriptor & 0xFFFF; - } else if ((type & 0xFFE) == 0xFF8) { // 0x001F - // Mapped memory range - if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) { - LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); - continue; - } - u32 end_desc = kernel_caps[i + 1]; - ++i; // Skip over the second descriptor on the next iteration - AddressMapping mapping; - mapping.address = descriptor << 12; - VAddr end_address = end_desc << 12; - - if (mapping.address < end_address) { - mapping.size = end_address - mapping.address; - } else { - mapping.size = 0; - } + vm_manager.Reset(metadata.GetAddressSpaceType()); - mapping.read_only = (descriptor & (1 << 20)) != 0; - mapping.unk_flag = (end_desc & (1 << 20)) != 0; - - address_mappings.push_back(mapping); - } else if ((type & 0xFFF) == 0xFFE) { // 0x000F - // Mapped memory page - AddressMapping mapping; - mapping.address = descriptor << 12; - mapping.size = Memory::PAGE_SIZE; - mapping.read_only = false; - mapping.unk_flag = false; - - address_mappings.push_back(mapping); - } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF - // Kernel version - kernel_version = descriptor & 0xFFFF; - - int minor = kernel_version & 0xFF; - int major = (kernel_version >> 8) & 0xFF; - LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor); - } else { - LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor); - } - } + const auto& caps = metadata.GetKernelCapabilities(); + return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); } void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 459eedfa6d83656ad863bf1a7582d5cc35e94705..f6fb72770fea48619f38595291ad9d7b319a7481 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -11,9 +11,9 @@ #include <string> #include <vector> #include <boost/container/static_vector.hpp> -#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/process_capability.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/wait_object.h" @@ -42,24 +42,6 @@ enum class MemoryRegion : u16 { BASE = 3, }; -union ProcessFlags { - u16 raw; - - BitField<0, 1, u16> - allow_debug; ///< Allows other processes to attach to and debug this process. - BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they - /// don't have allow_debug set. - BitField<2, 1, u16> allow_nonalphanum; - BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions. - BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24. - BitField<5, 1, u16> allow_main_args; - BitField<6, 1, u16> shared_device_mem; - BitField<7, 1, u16> runnable_on_sleep; - BitField<8, 4, MemoryRegion> - memory_region; ///< Default region for memory allocations for this process - BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). -}; - /** * Indicates the status of a Process instance. * @@ -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. - */ - void LoadFromMetadata(const FileSys::ProgramMetadata& metadata); - - /** - * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them - * to this process. + * @param metadata The provided metadata to load process specific info from. + * + * @returns RESULT_SUCCESS if all relevant metadata was able to be + * loaded and parsed. Otherwise, an error code is returned. */ - void ParseKernelCaps(const u32* kernel_caps, std::size_t len); + ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); /** * Applies address space changes and launches the process main thread. @@ -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. diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index ac04d72d7930c859d6f9fa6b4d9aa7a8ccaaf294..07aa7a1cd67e0991e6dac289f724fff52466a1a0 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -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 diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 9cd0b0ccd0a39d430a34dd22148d9c0df8734d84..d8cc3095955d13cfe9c3b53974fd5bfc66a94f3f 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -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.", diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 0838e303b3ec67eb37f82504bb5055e8386dfcc6..a39cb812ddb044a2a34eff1d5043855bbdcbde24 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -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,