diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/common/bit_util.h | 61 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/file_sys/program_metadata.cpp | 11 | ||||
| -rw-r--r-- | src/core/file_sys/program_metadata.h | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/errors.h | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/handle_table.h | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.cpp | 80 | ||||
| -rw-r--r-- | src/core/hle/kernel/process.h | 58 | ||||
| -rw-r--r-- | src/core/hle/kernel/process_capability.cpp | 355 | ||||
| -rw-r--r-- | src/core/hle/kernel/process_capability.h | 264 | ||||
| -rw-r--r-- | src/core/loader/deconstructed_rom_directory.cpp | 5 | ||||
| -rw-r--r-- | src/core/loader/loader.cpp | 4 | ||||
| -rw-r--r-- | src/core/loader/loader.h | 2 |
14 files changed, 732 insertions, 125 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index a5e71d879..845626fc5 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -44,6 +44,7 @@ add_library(common STATIC | |||
| 44 | detached_tasks.cpp | 44 | detached_tasks.cpp |
| 45 | detached_tasks.h | 45 | detached_tasks.h |
| 46 | bit_field.h | 46 | bit_field.h |
| 47 | bit_util.h | ||
| 47 | cityhash.cpp | 48 | cityhash.cpp |
| 48 | cityhash.h | 49 | cityhash.h |
| 49 | color.h | 50 | color.h |
diff --git a/src/common/bit_util.h b/src/common/bit_util.h new file mode 100644 index 000000000..1eea17ba1 --- /dev/null +++ b/src/common/bit_util.h | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <climits> | ||
| 8 | #include <cstddef> | ||
| 9 | |||
| 10 | #ifdef _MSC_VER | ||
| 11 | #include <intrin.h> | ||
| 12 | #endif | ||
| 13 | |||
| 14 | #include "common/common_types.h" | ||
| 15 | |||
| 16 | namespace Common { | ||
| 17 | |||
| 18 | /// Gets the size of a specified type T in bits. | ||
| 19 | template <typename T> | ||
| 20 | constexpr std::size_t BitSize() { | ||
| 21 | return sizeof(T) * CHAR_BIT; | ||
| 22 | } | ||
| 23 | |||
| 24 | #ifdef _MSC_VER | ||
| 25 | inline u32 CountLeadingZeroes32(u32 value) { | ||
| 26 | unsigned long leading_zero = 0; | ||
| 27 | |||
| 28 | if (_BitScanReverse(&leading_zero, value) != 0) { | ||
| 29 | return 31 - leading_zero; | ||
| 30 | } | ||
| 31 | |||
| 32 | return 32; | ||
| 33 | } | ||
| 34 | |||
| 35 | inline u64 CountLeadingZeroes64(u64 value) { | ||
| 36 | unsigned long leading_zero = 0; | ||
| 37 | |||
| 38 | if (_BitScanReverse64(&leading_zero, value) != 0) { | ||
| 39 | return 63 - leading_zero; | ||
| 40 | } | ||
| 41 | |||
| 42 | return 64; | ||
| 43 | } | ||
| 44 | #else | ||
| 45 | inline u32 CountLeadingZeroes32(u32 value) { | ||
| 46 | if (value == 0) { | ||
| 47 | return 32; | ||
| 48 | } | ||
| 49 | |||
| 50 | return __builtin_clz(value); | ||
| 51 | } | ||
| 52 | |||
| 53 | inline u64 CountLeadingZeroes64(u64 value) { | ||
| 54 | if (value == 0) { | ||
| 55 | return 64; | ||
| 56 | } | ||
| 57 | |||
| 58 | return __builtin_clzll(value); | ||
| 59 | } | ||
| 60 | #endif | ||
| 61 | } // namespace Common | ||
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 93f5ba3fe..f38271336 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -115,6 +115,8 @@ add_library(core STATIC | |||
| 115 | hle/kernel/object.h | 115 | hle/kernel/object.h |
| 116 | hle/kernel/process.cpp | 116 | hle/kernel/process.cpp |
| 117 | hle/kernel/process.h | 117 | hle/kernel/process.h |
| 118 | hle/kernel/process_capability.cpp | ||
| 119 | hle/kernel/process_capability.h | ||
| 118 | hle/kernel/readable_event.cpp | 120 | hle/kernel/readable_event.cpp |
| 119 | hle/kernel/readable_event.h | 121 | hle/kernel/readable_event.h |
| 120 | hle/kernel/resource_limit.cpp | 122 | hle/kernel/resource_limit.cpp |
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 8903ed1d3..e90c8c2de 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) { | |||
| 40 | if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) | 40 | if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) |
| 41 | return Loader::ResultStatus::ErrorBadFileAccessHeader; | 41 | return Loader::ResultStatus::ErrorBadFileAccessHeader; |
| 42 | 42 | ||
| 43 | aci_kernel_capabilities.resize(aci_header.kac_size / sizeof(u32)); | ||
| 44 | const u64 read_size = aci_header.kac_size; | ||
| 45 | const u64 read_offset = npdm_header.aci_offset + aci_header.kac_offset; | ||
| 46 | if (file->ReadBytes(aci_kernel_capabilities.data(), read_size, read_offset) != read_size) { | ||
| 47 | return Loader::ResultStatus::ErrorBadKernelCapabilityDescriptors; | ||
| 48 | } | ||
| 49 | |||
| 43 | return Loader::ResultStatus::Success; | 50 | return Loader::ResultStatus::Success; |
| 44 | } | 51 | } |
| 45 | 52 | ||
| @@ -71,6 +78,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const { | |||
| 71 | return aci_file_access.permissions; | 78 | return aci_file_access.permissions; |
| 72 | } | 79 | } |
| 73 | 80 | ||
| 81 | const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { | ||
| 82 | return aci_kernel_capabilities; | ||
| 83 | } | ||
| 84 | |||
| 74 | void ProgramMetadata::Print() const { | 85 | void ProgramMetadata::Print() const { |
| 75 | LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data()); | 86 | LOG_DEBUG(Service_FS, "Magic: {:.4}", npdm_header.magic.data()); |
| 76 | LOG_DEBUG(Service_FS, "Main thread priority: 0x{:02X}", npdm_header.main_thread_priority); | 87 | 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 e4470d6f0..0033ba347 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <vector> | ||
| 8 | #include "common/bit_field.h" | 9 | #include "common/bit_field.h" |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| @@ -38,6 +39,8 @@ enum class ProgramFilePermission : u64 { | |||
| 38 | */ | 39 | */ |
| 39 | class ProgramMetadata { | 40 | class ProgramMetadata { |
| 40 | public: | 41 | public: |
| 42 | using KernelCapabilityDescriptors = std::vector<u32>; | ||
| 43 | |||
| 41 | ProgramMetadata(); | 44 | ProgramMetadata(); |
| 42 | ~ProgramMetadata(); | 45 | ~ProgramMetadata(); |
| 43 | 46 | ||
| @@ -50,6 +53,7 @@ public: | |||
| 50 | u32 GetMainThreadStackSize() const; | 53 | u32 GetMainThreadStackSize() const; |
| 51 | u64 GetTitleID() const; | 54 | u64 GetTitleID() const; |
| 52 | u64 GetFilesystemPermissions() const; | 55 | u64 GetFilesystemPermissions() const; |
| 56 | const KernelCapabilityDescriptors& GetKernelCapabilities() const; | ||
| 53 | 57 | ||
| 54 | void Print() const; | 58 | void Print() const; |
| 55 | 59 | ||
| @@ -154,6 +158,8 @@ private: | |||
| 154 | 158 | ||
| 155 | FileAccessControl acid_file_access; | 159 | FileAccessControl acid_file_access; |
| 156 | FileAccessHeader aci_file_access; | 160 | FileAccessHeader aci_file_access; |
| 161 | |||
| 162 | KernelCapabilityDescriptors aci_kernel_capabilities; | ||
| 157 | }; | 163 | }; |
| 158 | 164 | ||
| 159 | } // namespace FileSys | 165 | } // namespace FileSys |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index d8240ec6d..d17eb0cb6 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -11,6 +11,7 @@ namespace Kernel { | |||
| 11 | // Confirmed Switch kernel error codes | 11 | // Confirmed Switch kernel error codes |
| 12 | 12 | ||
| 13 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; | 13 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; |
| 14 | constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; | ||
| 14 | constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; | 15 | constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; |
| 15 | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; | 16 | constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; |
| 16 | constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; | 17 | constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; |
| @@ -30,6 +31,7 @@ constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; | |||
| 30 | constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122}; | 31 | constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122}; |
| 31 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; | 32 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; |
| 32 | constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; | 33 | constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; |
| 34 | constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126}; | ||
| 33 | constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; | 35 | constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; |
| 34 | 36 | ||
| 35 | } // namespace Kernel | 37 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index 6b7927fd8..89a3bc740 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h | |||
| @@ -43,6 +43,9 @@ enum KernelHandle : Handle { | |||
| 43 | */ | 43 | */ |
| 44 | class HandleTable final : NonCopyable { | 44 | class HandleTable final : NonCopyable { |
| 45 | public: | 45 | public: |
| 46 | /// This is the maximum limit of handles allowed per process in Horizon | ||
| 47 | static constexpr std::size_t MAX_COUNT = 1024; | ||
| 48 | |||
| 46 | HandleTable(); | 49 | HandleTable(); |
| 47 | ~HandleTable(); | 50 | ~HandleTable(); |
| 48 | 51 | ||
| @@ -91,9 +94,6 @@ public: | |||
| 91 | void Clear(); | 94 | void Clear(); |
| 92 | 95 | ||
| 93 | private: | 96 | private: |
| 94 | /// This is the maximum limit of handles allowed per process in Horizon | ||
| 95 | static constexpr std::size_t MAX_COUNT = 1024; | ||
| 96 | |||
| 97 | /// Stores the Object referenced by the handle or null if the slot is empty. | 97 | /// Stores the Object referenced by the handle or null if the slot is empty. |
| 98 | std::array<SharedPtr<Object>, MAX_COUNT> objects; | 98 | std::array<SharedPtr<Object>, MAX_COUNT> objects; |
| 99 | 99 | ||
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 5356a4a3f..4f209a979 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) { | |||
| 28 | SharedPtr<Process> process(new Process(kernel)); | 28 | SharedPtr<Process> process(new Process(kernel)); |
| 29 | 29 | ||
| 30 | process->name = std::move(name); | 30 | process->name = std::move(name); |
| 31 | process->flags.raw = 0; | ||
| 32 | process->flags.memory_region.Assign(MemoryRegion::APPLICATION); | ||
| 33 | process->resource_limit = kernel.GetSystemResourceLimit(); | 31 | process->resource_limit = kernel.GetSystemResourceLimit(); |
| 34 | process->status = ProcessStatus::Created; | 32 | process->status = ProcessStatus::Created; |
| 35 | process->program_id = 0; | 33 | process->program_id = 0; |
| 36 | process->process_id = kernel.CreateNewProcessID(); | 34 | process->process_id = kernel.CreateNewProcessID(); |
| 37 | process->svc_access_mask.set(); | 35 | process->capabilities.InitializeForMetadatalessProcess(); |
| 38 | 36 | ||
| 39 | std::mt19937 rng(Settings::values.rng_seed.value_or(0)); | 37 | std::mt19937 rng(Settings::values.rng_seed.value_or(0)); |
| 40 | std::uniform_int_distribution<u64> distribution; | 38 | std::uniform_int_distribution<u64> distribution; |
| @@ -64,83 +62,15 @@ ResultCode Process::ClearSignalState() { | |||
| 64 | return RESULT_SUCCESS; | 62 | return RESULT_SUCCESS; |
| 65 | } | 63 | } |
| 66 | 64 | ||
| 67 | void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { | 65 | ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { |
| 68 | program_id = metadata.GetTitleID(); | 66 | program_id = metadata.GetTitleID(); |
| 69 | ideal_processor = metadata.GetMainThreadCore(); | 67 | ideal_processor = metadata.GetMainThreadCore(); |
| 70 | is_64bit_process = metadata.Is64BitProgram(); | 68 | is_64bit_process = metadata.Is64BitProgram(); |
| 71 | vm_manager.Reset(metadata.GetAddressSpaceType()); | ||
| 72 | } | ||
| 73 | |||
| 74 | void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { | ||
| 75 | for (std::size_t i = 0; i < len; ++i) { | ||
| 76 | u32 descriptor = kernel_caps[i]; | ||
| 77 | u32 type = descriptor >> 20; | ||
| 78 | |||
| 79 | if (descriptor == 0xFFFFFFFF) { | ||
| 80 | // Unused descriptor entry | ||
| 81 | continue; | ||
| 82 | } else if ((type & 0xF00) == 0xE00) { // 0x0FFF | ||
| 83 | // Allowed interrupts list | ||
| 84 | LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored"); | ||
| 85 | } else if ((type & 0xF80) == 0xF00) { // 0x07FF | ||
| 86 | // Allowed syscalls mask | ||
| 87 | unsigned int index = ((descriptor >> 24) & 7) * 24; | ||
| 88 | u32 bits = descriptor & 0xFFFFFF; | ||
| 89 | |||
| 90 | while (bits && index < svc_access_mask.size()) { | ||
| 91 | svc_access_mask.set(index, bits & 1); | ||
| 92 | ++index; | ||
| 93 | bits >>= 1; | ||
| 94 | } | ||
| 95 | } else if ((type & 0xFF0) == 0xFE0) { // 0x00FF | ||
| 96 | // Handle table size | ||
| 97 | handle_table_size = descriptor & 0x3FF; | ||
| 98 | } else if ((type & 0xFF8) == 0xFF0) { // 0x007F | ||
| 99 | // Misc. flags | ||
| 100 | flags.raw = descriptor & 0xFFFF; | ||
| 101 | } else if ((type & 0xFFE) == 0xFF8) { // 0x001F | ||
| 102 | // Mapped memory range | ||
| 103 | if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) { | ||
| 104 | LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored."); | ||
| 105 | continue; | ||
| 106 | } | ||
| 107 | u32 end_desc = kernel_caps[i + 1]; | ||
| 108 | ++i; // Skip over the second descriptor on the next iteration | ||
| 109 | 69 | ||
| 110 | AddressMapping mapping; | 70 | vm_manager.Reset(metadata.GetAddressSpaceType()); |
| 111 | mapping.address = descriptor << 12; | ||
| 112 | VAddr end_address = end_desc << 12; | ||
| 113 | |||
| 114 | if (mapping.address < end_address) { | ||
| 115 | mapping.size = end_address - mapping.address; | ||
| 116 | } else { | ||
| 117 | mapping.size = 0; | ||
| 118 | } | ||
| 119 | 71 | ||
| 120 | mapping.read_only = (descriptor & (1 << 20)) != 0; | 72 | const auto& caps = metadata.GetKernelCapabilities(); |
| 121 | mapping.unk_flag = (end_desc & (1 << 20)) != 0; | 73 | return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); |
| 122 | |||
| 123 | address_mappings.push_back(mapping); | ||
| 124 | } else if ((type & 0xFFF) == 0xFFE) { // 0x000F | ||
| 125 | // Mapped memory page | ||
| 126 | AddressMapping mapping; | ||
| 127 | mapping.address = descriptor << 12; | ||
| 128 | mapping.size = Memory::PAGE_SIZE; | ||
| 129 | mapping.read_only = false; | ||
| 130 | mapping.unk_flag = false; | ||
| 131 | |||
| 132 | address_mappings.push_back(mapping); | ||
| 133 | } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF | ||
| 134 | // Kernel version | ||
| 135 | kernel_version = descriptor & 0xFFFF; | ||
| 136 | |||
| 137 | int minor = kernel_version & 0xFF; | ||
| 138 | int major = (kernel_version >> 8) & 0xFF; | ||
| 139 | LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor); | ||
| 140 | } else { | ||
| 141 | LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | } | 74 | } |
| 145 | 75 | ||
| 146 | void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | 76 | 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 7da367251..2c0b20f9e 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -11,9 +11,9 @@ | |||
| 11 | #include <string> | 11 | #include <string> |
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | #include <boost/container/static_vector.hpp> | 13 | #include <boost/container/static_vector.hpp> |
| 14 | #include "common/bit_field.h" | ||
| 15 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 16 | #include "core/hle/kernel/handle_table.h" | 15 | #include "core/hle/kernel/handle_table.h" |
| 16 | #include "core/hle/kernel/process_capability.h" | ||
| 17 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 18 | #include "core/hle/kernel/vm_manager.h" | 18 | #include "core/hle/kernel/vm_manager.h" |
| 19 | #include "core/hle/kernel/wait_object.h" | 19 | #include "core/hle/kernel/wait_object.h" |
| @@ -42,24 +42,6 @@ enum class MemoryRegion : u16 { | |||
| 42 | BASE = 3, | 42 | BASE = 3, |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | union ProcessFlags { | ||
| 46 | u16 raw; | ||
| 47 | |||
| 48 | BitField<0, 1, u16> | ||
| 49 | allow_debug; ///< Allows other processes to attach to and debug this process. | ||
| 50 | BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they | ||
| 51 | /// don't have allow_debug set. | ||
| 52 | BitField<2, 1, u16> allow_nonalphanum; | ||
| 53 | BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions. | ||
| 54 | BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24. | ||
| 55 | BitField<5, 1, u16> allow_main_args; | ||
| 56 | BitField<6, 1, u16> shared_device_mem; | ||
| 57 | BitField<7, 1, u16> runnable_on_sleep; | ||
| 58 | BitField<8, 4, MemoryRegion> | ||
| 59 | memory_region; ///< Default region for memory allocations for this process | ||
| 60 | BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000). | ||
| 61 | }; | ||
| 62 | |||
| 63 | /** | 45 | /** |
| 64 | * Indicates the status of a Process instance. | 46 | * Indicates the status of a Process instance. |
| 65 | * | 47 | * |
| @@ -192,13 +174,13 @@ public: | |||
| 192 | } | 174 | } |
| 193 | 175 | ||
| 194 | /// Gets the bitmask of allowed CPUs that this process' threads can run on. | 176 | /// Gets the bitmask of allowed CPUs that this process' threads can run on. |
| 195 | u32 GetAllowedProcessorMask() const { | 177 | u64 GetAllowedProcessorMask() const { |
| 196 | return allowed_processor_mask; | 178 | return capabilities.GetCoreMask(); |
| 197 | } | 179 | } |
| 198 | 180 | ||
| 199 | /// Gets the bitmask of allowed thread priorities. | 181 | /// Gets the bitmask of allowed thread priorities. |
| 200 | u32 GetAllowedThreadPriorityMask() const { | 182 | u64 GetAllowedThreadPriorityMask() const { |
| 201 | return allowed_thread_priority_mask; | 183 | return capabilities.GetPriorityMask(); |
| 202 | } | 184 | } |
| 203 | 185 | ||
| 204 | u32 IsVirtualMemoryEnabled() const { | 186 | u32 IsVirtualMemoryEnabled() const { |
| @@ -239,15 +221,12 @@ public: | |||
| 239 | * Loads process-specifics configuration info with metadata provided | 221 | * Loads process-specifics configuration info with metadata provided |
| 240 | * by an executable. | 222 | * by an executable. |
| 241 | * | 223 | * |
| 242 | * @param metadata The provided metadata to load process specific info. | 224 | * @param metadata The provided metadata to load process specific info from. |
| 243 | */ | 225 | * |
| 244 | void LoadFromMetadata(const FileSys::ProgramMetadata& metadata); | 226 | * @returns RESULT_SUCCESS if all relevant metadata was able to be |
| 245 | 227 | * loaded and parsed. Otherwise, an error code is returned. | |
| 246 | /** | ||
| 247 | * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them | ||
| 248 | * to this process. | ||
| 249 | */ | 228 | */ |
| 250 | void ParseKernelCaps(const u32* kernel_caps, std::size_t len); | 229 | ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); |
| 251 | 230 | ||
| 252 | /** | 231 | /** |
| 253 | * Applies address space changes and launches the process main thread. | 232 | * Applies address space changes and launches the process main thread. |
| @@ -308,22 +287,8 @@ private: | |||
| 308 | /// Resource limit descriptor for this process | 287 | /// Resource limit descriptor for this process |
| 309 | SharedPtr<ResourceLimit> resource_limit; | 288 | SharedPtr<ResourceLimit> resource_limit; |
| 310 | 289 | ||
| 311 | /// The process may only call SVCs which have the corresponding bit set. | ||
| 312 | std::bitset<0x80> svc_access_mask; | ||
| 313 | /// Maximum size of the handle table for the process. | ||
| 314 | u32 handle_table_size = 0x200; | ||
| 315 | /// Special memory ranges mapped into this processes address space. This is used to give | ||
| 316 | /// processes access to specific I/O regions and device memory. | ||
| 317 | boost::container::static_vector<AddressMapping, 8> address_mappings; | ||
| 318 | ProcessFlags flags; | ||
| 319 | /// Kernel compatibility version for this process | ||
| 320 | u16 kernel_version = 0; | ||
| 321 | /// The default CPU for this process, threads are scheduled on this cpu by default. | 290 | /// The default CPU for this process, threads are scheduled on this cpu by default. |
| 322 | u8 ideal_processor = 0; | 291 | u8 ideal_processor = 0; |
| 323 | /// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse | ||
| 324 | /// this value from the process header. | ||
| 325 | u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; | ||
| 326 | u32 allowed_thread_priority_mask = 0xFFFFFFFF; | ||
| 327 | u32 is_virtual_address_memory_enabled = 0; | 292 | u32 is_virtual_address_memory_enabled = 0; |
| 328 | 293 | ||
| 329 | /// The Thread Local Storage area is allocated as processes create threads, | 294 | /// The Thread Local Storage area is allocated as processes create threads, |
| @@ -333,6 +298,9 @@ private: | |||
| 333 | /// This vector will grow as more pages are allocated for new threads. | 298 | /// This vector will grow as more pages are allocated for new threads. |
| 334 | std::vector<std::bitset<8>> tls_slots; | 299 | std::vector<std::bitset<8>> tls_slots; |
| 335 | 300 | ||
| 301 | /// Contains the parsed process capability descriptors. | ||
| 302 | ProcessCapabilities capabilities; | ||
| 303 | |||
| 336 | /// Whether or not this process is AArch64, or AArch32. | 304 | /// Whether or not this process is AArch64, or AArch32. |
| 337 | /// By default, we currently assume this is true, unless otherwise | 305 | /// By default, we currently assume this is true, unless otherwise |
| 338 | /// specified by metadata provided to the process during loading. | 306 | /// specified by metadata provided to the process during loading. |
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp new file mode 100644 index 000000000..3a2164b25 --- /dev/null +++ b/src/core/hle/kernel/process_capability.cpp | |||
| @@ -0,0 +1,355 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/bit_util.h" | ||
| 6 | #include "core/hle/kernel/errors.h" | ||
| 7 | #include "core/hle/kernel/handle_table.h" | ||
| 8 | #include "core/hle/kernel/process_capability.h" | ||
| 9 | #include "core/hle/kernel/vm_manager.h" | ||
| 10 | |||
| 11 | namespace Kernel { | ||
| 12 | namespace { | ||
| 13 | |||
| 14 | // clang-format off | ||
| 15 | |||
| 16 | // Shift offsets for kernel capability types. | ||
| 17 | enum : u32 { | ||
| 18 | CapabilityOffset_PriorityAndCoreNum = 3, | ||
| 19 | CapabilityOffset_Syscall = 4, | ||
| 20 | CapabilityOffset_MapPhysical = 6, | ||
| 21 | CapabilityOffset_MapIO = 7, | ||
| 22 | CapabilityOffset_Interrupt = 11, | ||
| 23 | CapabilityOffset_ProgramType = 13, | ||
| 24 | CapabilityOffset_KernelVersion = 14, | ||
| 25 | CapabilityOffset_HandleTableSize = 15, | ||
| 26 | CapabilityOffset_Debug = 16, | ||
| 27 | }; | ||
| 28 | |||
| 29 | // Combined mask of all parameters that may be initialized only once. | ||
| 30 | constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) | | ||
| 31 | (1U << CapabilityOffset_ProgramType) | | ||
| 32 | (1U << CapabilityOffset_KernelVersion) | | ||
| 33 | (1U << CapabilityOffset_HandleTableSize) | | ||
| 34 | (1U << CapabilityOffset_Debug); | ||
| 35 | |||
| 36 | // Packed kernel version indicating 10.4.0 | ||
| 37 | constexpr u32 PackedKernelVersion = 0x520000; | ||
| 38 | |||
| 39 | // Indicates possible types of capabilities that can be specified. | ||
| 40 | enum class CapabilityType : u32 { | ||
| 41 | Unset = 0U, | ||
| 42 | PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1, | ||
| 43 | Syscall = (1U << CapabilityOffset_Syscall) - 1, | ||
| 44 | MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1, | ||
| 45 | MapIO = (1U << CapabilityOffset_MapIO) - 1, | ||
| 46 | Interrupt = (1U << CapabilityOffset_Interrupt) - 1, | ||
| 47 | ProgramType = (1U << CapabilityOffset_ProgramType) - 1, | ||
| 48 | KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1, | ||
| 49 | HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1, | ||
| 50 | Debug = (1U << CapabilityOffset_Debug) - 1, | ||
| 51 | Ignorable = 0xFFFFFFFFU, | ||
| 52 | }; | ||
| 53 | |||
| 54 | // clang-format on | ||
| 55 | |||
| 56 | constexpr CapabilityType GetCapabilityType(u32 value) { | ||
| 57 | return static_cast<CapabilityType>((~value & (value + 1)) - 1); | ||
| 58 | } | ||
| 59 | |||
| 60 | u32 GetFlagBitOffset(CapabilityType type) { | ||
| 61 | const auto value = static_cast<u32>(type); | ||
| 62 | return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value)); | ||
| 63 | } | ||
| 64 | |||
| 65 | } // Anonymous namespace | ||
| 66 | |||
| 67 | ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities, | ||
| 68 | std::size_t num_capabilities, | ||
| 69 | VMManager& vm_manager) { | ||
| 70 | Clear(); | ||
| 71 | |||
| 72 | // Allow all cores and priorities. | ||
| 73 | core_mask = 0xF; | ||
| 74 | priority_mask = 0xFFFFFFFFFFFFFFFF; | ||
| 75 | kernel_version = PackedKernelVersion; | ||
| 76 | |||
| 77 | return ParseCapabilities(capabilities, num_capabilities, vm_manager); | ||
| 78 | } | ||
| 79 | |||
| 80 | ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities, | ||
| 81 | std::size_t num_capabilities, | ||
| 82 | VMManager& vm_manager) { | ||
| 83 | Clear(); | ||
| 84 | |||
| 85 | return ParseCapabilities(capabilities, num_capabilities, vm_manager); | ||
| 86 | } | ||
| 87 | |||
| 88 | void ProcessCapabilities::InitializeForMetadatalessProcess() { | ||
| 89 | // Allow all cores and priorities | ||
| 90 | core_mask = 0xF; | ||
| 91 | priority_mask = 0xFFFFFFFFFFFFFFFF; | ||
| 92 | kernel_version = PackedKernelVersion; | ||
| 93 | |||
| 94 | // Allow all system calls and interrupts. | ||
| 95 | svc_capabilities.set(); | ||
| 96 | interrupt_capabilities.set(); | ||
| 97 | |||
| 98 | // Allow using the maximum possible amount of handles | ||
| 99 | handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT); | ||
| 100 | |||
| 101 | // Allow all debugging capabilities. | ||
| 102 | is_debuggable = true; | ||
| 103 | can_force_debug = true; | ||
| 104 | } | ||
| 105 | |||
| 106 | ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities, | ||
| 107 | std::size_t num_capabilities, | ||
| 108 | VMManager& vm_manager) { | ||
| 109 | u32 set_flags = 0; | ||
| 110 | u32 set_svc_bits = 0; | ||
| 111 | |||
| 112 | for (std::size_t i = 0; i < num_capabilities; ++i) { | ||
| 113 | const u32 descriptor = capabilities[i]; | ||
| 114 | const auto type = GetCapabilityType(descriptor); | ||
| 115 | |||
| 116 | if (type == CapabilityType::MapPhysical) { | ||
| 117 | i++; | ||
| 118 | |||
| 119 | // The MapPhysical type uses two descriptor flags for its parameters. | ||
| 120 | // If there's only one, then there's a problem. | ||
| 121 | if (i >= num_capabilities) { | ||
| 122 | return ERR_INVALID_COMBINATION; | ||
| 123 | } | ||
| 124 | |||
| 125 | const auto size_flags = capabilities[i]; | ||
| 126 | if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) { | ||
| 127 | return ERR_INVALID_COMBINATION; | ||
| 128 | } | ||
| 129 | |||
| 130 | const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager); | ||
| 131 | if (result.IsError()) { | ||
| 132 | return result; | ||
| 133 | } | ||
| 134 | } else { | ||
| 135 | const auto result = | ||
| 136 | ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager); | ||
| 137 | if (result.IsError()) { | ||
| 138 | return result; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | return RESULT_SUCCESS; | ||
| 144 | } | ||
| 145 | |||
| 146 | ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, | ||
| 147 | u32 flag, VMManager& vm_manager) { | ||
| 148 | const auto type = GetCapabilityType(flag); | ||
| 149 | |||
| 150 | if (type == CapabilityType::Unset) { | ||
| 151 | return ERR_INVALID_CAPABILITY_DESCRIPTOR; | ||
| 152 | } | ||
| 153 | |||
| 154 | // Bail early on ignorable entries, as one would expect, | ||
| 155 | // ignorable descriptors can be ignored. | ||
| 156 | if (type == CapabilityType::Ignorable) { | ||
| 157 | return RESULT_SUCCESS; | ||
| 158 | } | ||
| 159 | |||
| 160 | // Ensure that the give flag hasn't already been initialized before. | ||
| 161 | // If it has been, then bail. | ||
| 162 | const u32 flag_length = GetFlagBitOffset(type); | ||
| 163 | const u32 set_flag = 1U << flag_length; | ||
| 164 | if ((set_flag & set_flags & InitializeOnceMask) != 0) { | ||
| 165 | return ERR_INVALID_COMBINATION; | ||
| 166 | } | ||
| 167 | set_flags |= set_flag; | ||
| 168 | |||
| 169 | switch (type) { | ||
| 170 | case CapabilityType::PriorityAndCoreNum: | ||
| 171 | return HandlePriorityCoreNumFlags(flag); | ||
| 172 | case CapabilityType::Syscall: | ||
| 173 | return HandleSyscallFlags(set_svc_bits, flag); | ||
| 174 | case CapabilityType::MapIO: | ||
| 175 | return HandleMapIOFlags(flag, vm_manager); | ||
| 176 | case CapabilityType::Interrupt: | ||
| 177 | return HandleInterruptFlags(flag); | ||
| 178 | case CapabilityType::ProgramType: | ||
| 179 | return HandleProgramTypeFlags(flag); | ||
| 180 | case CapabilityType::KernelVersion: | ||
| 181 | return HandleKernelVersionFlags(flag); | ||
| 182 | case CapabilityType::HandleTableSize: | ||
| 183 | return HandleHandleTableFlags(flag); | ||
| 184 | case CapabilityType::Debug: | ||
| 185 | return HandleDebugFlags(flag); | ||
| 186 | default: | ||
| 187 | break; | ||
| 188 | } | ||
| 189 | |||
| 190 | return ERR_INVALID_CAPABILITY_DESCRIPTOR; | ||
| 191 | } | ||
| 192 | |||
| 193 | void ProcessCapabilities::Clear() { | ||
| 194 | svc_capabilities.reset(); | ||
| 195 | interrupt_capabilities.reset(); | ||
| 196 | |||
| 197 | core_mask = 0; | ||
| 198 | priority_mask = 0; | ||
| 199 | |||
| 200 | handle_table_size = 0; | ||
| 201 | kernel_version = 0; | ||
| 202 | |||
| 203 | program_type = ProgramType::SysModule; | ||
| 204 | |||
| 205 | is_debuggable = false; | ||
| 206 | can_force_debug = false; | ||
| 207 | } | ||
| 208 | |||
| 209 | ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) { | ||
| 210 | if (priority_mask != 0 || core_mask != 0) { | ||
| 211 | return ERR_INVALID_CAPABILITY_DESCRIPTOR; | ||
| 212 | } | ||
| 213 | |||
| 214 | const u32 core_num_min = (flags >> 16) & 0xFF; | ||
| 215 | const u32 core_num_max = (flags >> 24) & 0xFF; | ||
| 216 | if (core_num_min > core_num_max) { | ||
| 217 | return ERR_INVALID_COMBINATION; | ||
| 218 | } | ||
| 219 | |||
| 220 | const u32 priority_min = (flags >> 10) & 0x3F; | ||
| 221 | const u32 priority_max = (flags >> 4) & 0x3F; | ||
| 222 | if (priority_min > priority_max) { | ||
| 223 | return ERR_INVALID_COMBINATION; | ||
| 224 | } | ||
| 225 | |||
| 226 | // The switch only has 4 usable cores. | ||
| 227 | if (core_num_max >= 4) { | ||
| 228 | return ERR_INVALID_PROCESSOR_ID; | ||
| 229 | } | ||
| 230 | |||
| 231 | const auto make_mask = [](u64 min, u64 max) { | ||
| 232 | const u64 range = max - min + 1; | ||
| 233 | const u64 mask = (1ULL << range) - 1; | ||
| 234 | |||
| 235 | return mask << min; | ||
| 236 | }; | ||
| 237 | |||
| 238 | core_mask = make_mask(core_num_min, core_num_max); | ||
| 239 | priority_mask = make_mask(priority_min, priority_max); | ||
| 240 | return RESULT_SUCCESS; | ||
| 241 | } | ||
| 242 | |||
| 243 | ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) { | ||
| 244 | const u32 index = flags >> 29; | ||
| 245 | const u32 svc_bit = 1U << index; | ||
| 246 | |||
| 247 | // If we've already set this svc before, bail. | ||
| 248 | if ((set_svc_bits & svc_bit) != 0) { | ||
| 249 | return ERR_INVALID_COMBINATION; | ||
| 250 | } | ||
| 251 | set_svc_bits |= svc_bit; | ||
| 252 | |||
| 253 | const u32 svc_mask = (flags >> 5) & 0xFFFFFF; | ||
| 254 | for (u32 i = 0; i < 24; ++i) { | ||
| 255 | const u32 svc_number = index * 24 + i; | ||
| 256 | |||
| 257 | if ((svc_mask & (1U << i)) == 0) { | ||
| 258 | continue; | ||
| 259 | } | ||
| 260 | |||
| 261 | if (svc_number >= svc_capabilities.size()) { | ||
| 262 | return ERR_OUT_OF_RANGE; | ||
| 263 | } | ||
| 264 | |||
| 265 | svc_capabilities[svc_number] = true; | ||
| 266 | } | ||
| 267 | |||
| 268 | return RESULT_SUCCESS; | ||
| 269 | } | ||
| 270 | |||
| 271 | ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, | ||
| 272 | VMManager& vm_manager) { | ||
| 273 | // TODO(Lioncache): Implement once the memory manager can handle this. | ||
| 274 | return RESULT_SUCCESS; | ||
| 275 | } | ||
| 276 | |||
| 277 | ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) { | ||
| 278 | // TODO(Lioncache): Implement once the memory manager can handle this. | ||
| 279 | return RESULT_SUCCESS; | ||
| 280 | } | ||
| 281 | |||
| 282 | ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) { | ||
| 283 | constexpr u32 interrupt_ignore_value = 0x3FF; | ||
| 284 | const u32 interrupt0 = (flags >> 12) & 0x3FF; | ||
| 285 | const u32 interrupt1 = (flags >> 22) & 0x3FF; | ||
| 286 | |||
| 287 | for (u32 interrupt : {interrupt0, interrupt1}) { | ||
| 288 | if (interrupt == interrupt_ignore_value) { | ||
| 289 | continue; | ||
| 290 | } | ||
| 291 | |||
| 292 | // NOTE: | ||
| 293 | // This should be checking a generic interrupt controller value | ||
| 294 | // as part of the calculation, however, given we don't currently | ||
| 295 | // emulate that, it's sufficient to mark every interrupt as defined. | ||
| 296 | |||
| 297 | if (interrupt >= interrupt_capabilities.size()) { | ||
| 298 | return ERR_OUT_OF_RANGE; | ||
| 299 | } | ||
| 300 | |||
| 301 | interrupt_capabilities[interrupt] = true; | ||
| 302 | } | ||
| 303 | |||
| 304 | return RESULT_SUCCESS; | ||
| 305 | } | ||
| 306 | |||
| 307 | ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) { | ||
| 308 | const u32 reserved = flags >> 17; | ||
| 309 | if (reserved != 0) { | ||
| 310 | return ERR_RESERVED_VALUE; | ||
| 311 | } | ||
| 312 | |||
| 313 | program_type = static_cast<ProgramType>((flags >> 14) & 0b111); | ||
| 314 | return RESULT_SUCCESS; | ||
| 315 | } | ||
| 316 | |||
| 317 | ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) { | ||
| 318 | // Yes, the internal member variable is checked in the actual kernel here. | ||
| 319 | // This might look odd for options that are only allowed to be initialized | ||
| 320 | // just once, however the kernel has a separate initialization function for | ||
| 321 | // kernel processes and userland processes. The kernel variant sets this | ||
| 322 | // member variable ahead of time. | ||
| 323 | |||
| 324 | const u32 major_version = kernel_version >> 19; | ||
| 325 | |||
| 326 | if (major_version != 0 || flags < 0x80000) { | ||
| 327 | return ERR_INVALID_CAPABILITY_DESCRIPTOR; | ||
| 328 | } | ||
| 329 | |||
| 330 | kernel_version = flags; | ||
| 331 | return RESULT_SUCCESS; | ||
| 332 | } | ||
| 333 | |||
| 334 | ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) { | ||
| 335 | const u32 reserved = flags >> 26; | ||
| 336 | if (reserved != 0) { | ||
| 337 | return ERR_RESERVED_VALUE; | ||
| 338 | } | ||
| 339 | |||
| 340 | handle_table_size = (flags >> 16) & 0x3FF; | ||
| 341 | return RESULT_SUCCESS; | ||
| 342 | } | ||
| 343 | |||
| 344 | ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) { | ||
| 345 | const u32 reserved = flags >> 19; | ||
| 346 | if (reserved != 0) { | ||
| 347 | return ERR_RESERVED_VALUE; | ||
| 348 | } | ||
| 349 | |||
| 350 | is_debuggable = (flags & 0x20000) != 0; | ||
| 351 | can_force_debug = (flags & 0x40000) != 0; | ||
| 352 | return RESULT_SUCCESS; | ||
| 353 | } | ||
| 354 | |||
| 355 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h new file mode 100644 index 000000000..fbc8812a3 --- /dev/null +++ b/src/core/hle/kernel/process_capability.h | |||
| @@ -0,0 +1,264 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <bitset> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | union ResultCode; | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | class VMManager; | ||
| 16 | |||
| 17 | /// The possible types of programs that may be indicated | ||
| 18 | /// by the program type capability descriptor. | ||
| 19 | enum class ProgramType { | ||
| 20 | SysModule, | ||
| 21 | Application, | ||
| 22 | Applet, | ||
| 23 | }; | ||
| 24 | |||
| 25 | /// Handles kernel capability descriptors that are provided by | ||
| 26 | /// application metadata. These descriptors provide information | ||
| 27 | /// that alters certain parameters for kernel process instance | ||
| 28 | /// that will run said application (or applet). | ||
| 29 | /// | ||
| 30 | /// Capabilities are a sequence of flag descriptors, that indicate various | ||
| 31 | /// configurations and constraints for a particular process. | ||
| 32 | /// | ||
| 33 | /// Flag types are indicated by a sequence of set low bits. E.g. the | ||
| 34 | /// types are indicated with the low bits as follows (where x indicates "don't care"): | ||
| 35 | /// | ||
| 36 | /// - Priority and core mask : 0bxxxxxxxxxxxx0111 | ||
| 37 | /// - Allowed service call mask: 0bxxxxxxxxxxx01111 | ||
| 38 | /// - Map physical memory : 0bxxxxxxxxx0111111 | ||
| 39 | /// - Map IO memory : 0bxxxxxxxx01111111 | ||
| 40 | /// - Interrupts : 0bxxxx011111111111 | ||
| 41 | /// - Application type : 0bxx01111111111111 | ||
| 42 | /// - Kernel version : 0bx011111111111111 | ||
| 43 | /// - Handle table size : 0b0111111111111111 | ||
| 44 | /// - Debugger flags : 0b1111111111111111 | ||
| 45 | /// | ||
| 46 | /// These are essentially a bit offset subtracted by 1 to create a mask. | ||
| 47 | /// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000) | ||
| 48 | /// subtracted by one (7 -> 0b0111) | ||
| 49 | /// | ||
| 50 | /// An example of a bit layout (using the map physical layout): | ||
| 51 | /// <example> | ||
| 52 | /// The MapPhysical type indicates a sequence entry pair of: | ||
| 53 | /// | ||
| 54 | /// [initial, memory_flags], where: | ||
| 55 | /// | ||
| 56 | /// initial: | ||
| 57 | /// bits: | ||
| 58 | /// 7-24: Starting page to map memory at. | ||
| 59 | /// 25 : Indicates if the memory should be mapped as read only. | ||
| 60 | /// | ||
| 61 | /// memory_flags: | ||
| 62 | /// bits: | ||
| 63 | /// 7-20 : Number of pages to map | ||
| 64 | /// 21-25: Seems to be reserved (still checked against though) | ||
| 65 | /// 26 : Whether or not the memory being mapped is IO memory, or physical memory | ||
| 66 | /// </example> | ||
| 67 | /// | ||
| 68 | class ProcessCapabilities { | ||
| 69 | public: | ||
| 70 | using InterruptCapabilities = std::bitset<1024>; | ||
| 71 | using SyscallCapabilities = std::bitset<128>; | ||
| 72 | |||
| 73 | ProcessCapabilities() = default; | ||
| 74 | ProcessCapabilities(const ProcessCapabilities&) = delete; | ||
| 75 | ProcessCapabilities(ProcessCapabilities&&) = default; | ||
| 76 | |||
| 77 | ProcessCapabilities& operator=(const ProcessCapabilities&) = delete; | ||
| 78 | ProcessCapabilities& operator=(ProcessCapabilities&&) = default; | ||
| 79 | |||
| 80 | /// Initializes this process capabilities instance for a kernel process. | ||
| 81 | /// | ||
| 82 | /// @param capabilities The capabilities to parse | ||
| 83 | /// @param num_capabilities The number of capabilities to parse. | ||
| 84 | /// @param vm_manager The memory manager to use for handling any mapping-related | ||
| 85 | /// operations (such as mapping IO memory, etc). | ||
| 86 | /// | ||
| 87 | /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized, | ||
| 88 | /// otherwise, an error code upon failure. | ||
| 89 | /// | ||
| 90 | ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, | ||
| 91 | VMManager& vm_manager); | ||
| 92 | |||
| 93 | /// Initializes this process capabilities instance for a userland process. | ||
| 94 | /// | ||
| 95 | /// @param capabilities The capabilities to parse. | ||
| 96 | /// @param num_capabilities The total number of capabilities to parse. | ||
| 97 | /// @param vm_manager The memory manager to use for handling any mapping-related | ||
| 98 | /// operations (such as mapping IO memory, etc). | ||
| 99 | /// | ||
| 100 | /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized, | ||
| 101 | /// otherwise, an error code upon failure. | ||
| 102 | /// | ||
| 103 | ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, | ||
| 104 | VMManager& vm_manager); | ||
| 105 | |||
| 106 | /// Initializes this process capabilities instance for a process that does not | ||
| 107 | /// have any metadata to parse. | ||
| 108 | /// | ||
| 109 | /// This is necessary, as we allow running raw executables, and the internal | ||
| 110 | /// kernel process capabilities also determine what CPU cores the process is | ||
| 111 | /// allowed to run on, and what priorities are allowed for threads. It also | ||
| 112 | /// determines the max handle table size, what the program type is, whether or | ||
| 113 | /// not the process can be debugged, or whether it's possible for a process to | ||
| 114 | /// forcibly debug another process. | ||
| 115 | /// | ||
| 116 | /// Given the above, this essentially enables all capabilities across the board | ||
| 117 | /// for the process. It allows the process to: | ||
| 118 | /// | ||
| 119 | /// - Run on any core | ||
| 120 | /// - Use any thread priority | ||
| 121 | /// - Use the maximum amount of handles a process is allowed to. | ||
| 122 | /// - Be debuggable | ||
| 123 | /// - Forcibly debug other processes. | ||
| 124 | /// | ||
| 125 | /// Note that this is not a behavior that the kernel allows a process to do via | ||
| 126 | /// a single function like this. This is yuzu-specific behavior to handle | ||
| 127 | /// executables with no capability descriptors whatsoever to derive behavior from. | ||
| 128 | /// It being yuzu-specific is why this is also not the default behavior and not | ||
| 129 | /// done by default in the constructor. | ||
| 130 | /// | ||
| 131 | void InitializeForMetadatalessProcess(); | ||
| 132 | |||
| 133 | /// Gets the allowable core mask | ||
| 134 | u64 GetCoreMask() const { | ||
| 135 | return core_mask; | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Gets the allowable priority mask | ||
| 139 | u64 GetPriorityMask() const { | ||
| 140 | return priority_mask; | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Gets the SVC access permission bits | ||
| 144 | const SyscallCapabilities& GetServiceCapabilities() const { | ||
| 145 | return svc_capabilities; | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Gets the valid interrupt bits. | ||
| 149 | const InterruptCapabilities& GetInterruptCapabilities() const { | ||
| 150 | return interrupt_capabilities; | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Gets the program type for this process. | ||
| 154 | ProgramType GetProgramType() const { | ||
| 155 | return program_type; | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Gets the number of total allowable handles for the process' handle table. | ||
| 159 | u32 GetHandleTableSize() const { | ||
| 160 | return handle_table_size; | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Gets the kernel version value. | ||
| 164 | u32 GetKernelVersion() const { | ||
| 165 | return kernel_version; | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Whether or not this process can be debugged. | ||
| 169 | bool IsDebuggable() const { | ||
| 170 | return is_debuggable; | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Whether or not this process can forcibly debug another | ||
| 174 | /// process, even if that process is not considered debuggable. | ||
| 175 | bool CanForceDebug() const { | ||
| 176 | return can_force_debug; | ||
| 177 | } | ||
| 178 | |||
| 179 | private: | ||
| 180 | /// Attempts to parse a given sequence of capability descriptors. | ||
| 181 | /// | ||
| 182 | /// @param capabilities The sequence of capability descriptors to parse. | ||
| 183 | /// @param num_capabilities The number of descriptors within the given sequence. | ||
| 184 | /// @param vm_manager The memory manager that will perform any memory | ||
| 185 | /// mapping if necessary. | ||
| 186 | /// | ||
| 187 | /// @return RESULT_SUCCESS if no errors occur, otherwise an error code. | ||
| 188 | /// | ||
| 189 | ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, | ||
| 190 | VMManager& vm_manager); | ||
| 191 | |||
| 192 | /// Attempts to parse a capability descriptor that is only represented by a | ||
| 193 | /// single flag set. | ||
| 194 | /// | ||
| 195 | /// @param set_flags Running set of flags that are used to catch | ||
| 196 | /// flags being initialized more than once when they shouldn't be. | ||
| 197 | /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask. | ||
| 198 | /// @param flag The flag to attempt to parse. | ||
| 199 | /// @param vm_manager The memory manager that will perform any memory | ||
| 200 | /// mapping if necessary. | ||
| 201 | /// | ||
| 202 | /// @return RESULT_SUCCESS if no errors occurred, otherwise an error code. | ||
| 203 | /// | ||
| 204 | ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, | ||
| 205 | VMManager& vm_manager); | ||
| 206 | |||
| 207 | /// Clears the internal state of this process capability instance. Necessary, | ||
| 208 | /// to have a sane starting point due to us allowing running executables without | ||
| 209 | /// configuration metadata. We assume a process is not going to have metadata, | ||
| 210 | /// and if it turns out that the process does, in fact, have metadata, then | ||
| 211 | /// we attempt to parse it. Thus, we need this to reset data members back to | ||
| 212 | /// a good state. | ||
| 213 | /// | ||
| 214 | /// DO NOT ever make this a public member function. This isn't an invariant | ||
| 215 | /// anything external should depend upon (and if anything comes to rely on it, | ||
| 216 | /// you should immediately be questioning the design of that thing, not this | ||
| 217 | /// class. If the kernel itself can run without depending on behavior like that, | ||
| 218 | /// then so can yuzu). | ||
| 219 | /// | ||
| 220 | void Clear(); | ||
| 221 | |||
| 222 | /// Handles flags related to the priority and core number capability flags. | ||
| 223 | ResultCode HandlePriorityCoreNumFlags(u32 flags); | ||
| 224 | |||
| 225 | /// Handles flags related to determining the allowable SVC mask. | ||
| 226 | ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags); | ||
| 227 | |||
| 228 | /// Handles flags related to mapping physical memory pages. | ||
| 229 | ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager); | ||
| 230 | |||
| 231 | /// Handles flags related to mapping IO pages. | ||
| 232 | ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager); | ||
| 233 | |||
| 234 | /// Handles flags related to the interrupt capability flags. | ||
| 235 | ResultCode HandleInterruptFlags(u32 flags); | ||
| 236 | |||
| 237 | /// Handles flags related to the program type. | ||
| 238 | ResultCode HandleProgramTypeFlags(u32 flags); | ||
| 239 | |||
| 240 | /// Handles flags related to the handle table size. | ||
| 241 | ResultCode HandleHandleTableFlags(u32 flags); | ||
| 242 | |||
| 243 | /// Handles flags related to the kernel version capability flags. | ||
| 244 | ResultCode HandleKernelVersionFlags(u32 flags); | ||
| 245 | |||
| 246 | /// Handles flags related to debug-specific capabilities. | ||
| 247 | ResultCode HandleDebugFlags(u32 flags); | ||
| 248 | |||
| 249 | SyscallCapabilities svc_capabilities; | ||
| 250 | InterruptCapabilities interrupt_capabilities; | ||
| 251 | |||
| 252 | u64 core_mask = 0; | ||
| 253 | u64 priority_mask = 0; | ||
| 254 | |||
| 255 | u32 handle_table_size = 0; | ||
| 256 | u32 kernel_version = 0; | ||
| 257 | |||
| 258 | ProgramType program_type = ProgramType::SysModule; | ||
| 259 | |||
| 260 | bool is_debuggable = false; | ||
| 261 | bool can_force_debug = false; | ||
| 262 | }; | ||
| 263 | |||
| 264 | } // namespace Kernel | ||
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index ac04d72d7..07aa7a1cd 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) | |||
| 129 | return ResultStatus::Error32BitISA; | 129 | return ResultStatus::Error32BitISA; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | process.LoadFromMetadata(metadata); | 132 | if (process.LoadFromMetadata(metadata).IsError()) { |
| 133 | return ResultStatus::ErrorUnableToParseKernelMetadata; | ||
| 134 | } | ||
| 135 | |||
| 133 | const FileSys::PatchManager pm(metadata.GetTitleID()); | 136 | const FileSys::PatchManager pm(metadata.GetTitleID()); |
| 134 | 137 | ||
| 135 | // Load NSO modules | 138 | // Load NSO modules |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 9cd0b0ccd..d8cc30959 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) { | |||
| 93 | return "unknown"; | 93 | return "unknown"; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | constexpr std::array<const char*, 60> RESULT_MESSAGES{ | 96 | constexpr std::array<const char*, 62> RESULT_MESSAGES{ |
| 97 | "The operation completed successfully.", | 97 | "The operation completed successfully.", |
| 98 | "The loader requested to load is already loaded.", | 98 | "The loader requested to load is already loaded.", |
| 99 | "The operation is not implemented.", | 99 | "The operation is not implemented.", |
| @@ -103,6 +103,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{ | |||
| 103 | "The NPDM has a bad ACI header,", | 103 | "The NPDM has a bad ACI header,", |
| 104 | "The NPDM file has a bad file access control.", | 104 | "The NPDM file has a bad file access control.", |
| 105 | "The NPDM has a bad file access header.", | 105 | "The NPDM has a bad file access header.", |
| 106 | "The NPDM has bad kernel capability descriptors.", | ||
| 106 | "The PFS/HFS partition has a bad header.", | 107 | "The PFS/HFS partition has a bad header.", |
| 107 | "The PFS/HFS partition has incorrect size as determined by the header.", | 108 | "The PFS/HFS partition has incorrect size as determined by the header.", |
| 108 | "The NCA file has a bad header.", | 109 | "The NCA file has a bad header.", |
| @@ -125,6 +126,7 @@ constexpr std::array<const char*, 60> RESULT_MESSAGES{ | |||
| 125 | "The file could not be found or does not exist.", | 126 | "The file could not be found or does not exist.", |
| 126 | "The game is missing a program metadata file (main.npdm).", | 127 | "The game is missing a program metadata file (main.npdm).", |
| 127 | "The game uses the currently-unimplemented 32-bit architecture.", | 128 | "The game uses the currently-unimplemented 32-bit architecture.", |
| 129 | "Unable to completely parse the kernel metadata when loading the emulated process", | ||
| 128 | "The RomFS could not be found.", | 130 | "The RomFS could not be found.", |
| 129 | "The ELF file has incorrect size as determined by the header.", | 131 | "The ELF file has incorrect size as determined by the header.", |
| 130 | "There was a general error loading the NRO into emulated memory.", | 132 | "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 cfd67adc0..30eacd803 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -71,6 +71,7 @@ enum class ResultStatus : u16 { | |||
| 71 | ErrorBadACIHeader, | 71 | ErrorBadACIHeader, |
| 72 | ErrorBadFileAccessControl, | 72 | ErrorBadFileAccessControl, |
| 73 | ErrorBadFileAccessHeader, | 73 | ErrorBadFileAccessHeader, |
| 74 | ErrorBadKernelCapabilityDescriptors, | ||
| 74 | ErrorBadPFSHeader, | 75 | ErrorBadPFSHeader, |
| 75 | ErrorIncorrectPFSFileSize, | 76 | ErrorIncorrectPFSFileSize, |
| 76 | ErrorBadNCAHeader, | 77 | ErrorBadNCAHeader, |
| @@ -93,6 +94,7 @@ enum class ResultStatus : u16 { | |||
| 93 | ErrorNullFile, | 94 | ErrorNullFile, |
| 94 | ErrorMissingNPDM, | 95 | ErrorMissingNPDM, |
| 95 | Error32BitISA, | 96 | Error32BitISA, |
| 97 | ErrorUnableToParseKernelMetadata, | ||
| 96 | ErrorNoRomFS, | 98 | ErrorNoRomFS, |
| 97 | ErrorIncorrectELFFileSize, | 99 | ErrorIncorrectELFFileSize, |
| 98 | ErrorLoadingNRO, | 100 | ErrorLoadingNRO, |