summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/bit_util.h61
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/file_sys/program_metadata.cpp11
-rw-r--r--src/core/file_sys/program_metadata.h6
-rw-r--r--src/core/hle/kernel/errors.h2
-rw-r--r--src/core/hle/kernel/handle_table.h6
-rw-r--r--src/core/hle/kernel/process.cpp80
-rw-r--r--src/core/hle/kernel/process.h58
-rw-r--r--src/core/hle/kernel/process_capability.cpp355
-rw-r--r--src/core/hle/kernel/process_capability.h264
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp5
-rw-r--r--src/core/loader/loader.cpp4
-rw-r--r--src/core/loader/loader.h2
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
16namespace Common {
17
18/// Gets the size of a specified type T in bits.
19template <typename T>
20constexpr std::size_t BitSize() {
21 return sizeof(T) * CHAR_BIT;
22}
23
24#ifdef _MSC_VER
25inline 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
35inline 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
45inline u32 CountLeadingZeroes32(u32 value) {
46 if (value == 0) {
47 return 32;
48 }
49
50 return __builtin_clz(value);
51}
52
53inline 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
81const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
82 return aci_kernel_capabilities;
83}
84
74void ProgramMetadata::Print() const { 85void 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 */
39class ProgramMetadata { 40class ProgramMetadata {
40public: 41public:
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
13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; 13constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
14constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
14constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; 15constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
15constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; 16constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
16constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; 17constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
@@ -30,6 +31,7 @@ constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
30constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122}; 31constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122};
31constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123}; 32constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
32constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125}; 33constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
34constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126};
33constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132}; 35constexpr 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 */
44class HandleTable final : NonCopyable { 44class HandleTable final : NonCopyable {
45public: 45public:
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
93private: 96private:
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
67void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { 65ResultCode 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
74void 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
146void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { 76void 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
45union 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
11namespace Kernel {
12namespace {
13
14// clang-format off
15
16// Shift offsets for kernel capability types.
17enum : 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.
30constexpr 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
37constexpr u32 PackedKernelVersion = 0x520000;
38
39// Indicates possible types of capabilities that can be specified.
40enum 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
56constexpr CapabilityType GetCapabilityType(u32 value) {
57 return static_cast<CapabilityType>((~value & (value + 1)) - 1);
58}
59
60u32 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
67ResultCode 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
80ResultCode 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
88void 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
106ResultCode 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
146ResultCode 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
193void 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
209ResultCode 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
243ResultCode 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
271ResultCode 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
277ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) {
278 // TODO(Lioncache): Implement once the memory manager can handle this.
279 return RESULT_SUCCESS;
280}
281
282ResultCode 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
307ResultCode 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
317ResultCode 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
334ResultCode 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
344ResultCode 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
11union ResultCode;
12
13namespace Kernel {
14
15class VMManager;
16
17/// The possible types of programs that may be indicated
18/// by the program type capability descriptor.
19enum 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///
68class ProcessCapabilities {
69public:
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
179private:
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
96constexpr std::array<const char*, 60> RESULT_MESSAGES{ 96constexpr 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,