summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar bunnei2018-12-27 11:15:34 -0500
committerGravatar GitHub2018-12-27 11:15:34 -0500
commit795335af0f37ce25da3c3ca8eeab62c50f87d366 (patch)
tree48bde2f34a4e9c87dc96f83bfbeeeee96b72b9e6 /src/core/hle/kernel
parentMerge pull request #1892 from Tinob/master (diff)
parentkernel/process: Hook up the process capability parser to the process itself (diff)
downloadyuzu-795335af0f37ce25da3c3ca8eeab62c50f87d366.tar.gz
yuzu-795335af0f37ce25da3c3ca8eeab62c50f87d366.tar.xz
yuzu-795335af0f37ce25da3c3ca8eeab62c50f87d366.zip
Merge pull request #1928 from lioncash/caps
kernel: Handle kernel capability descriptors
Diffstat (limited to 'src/core/hle/kernel')
-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
6 files changed, 642 insertions, 123 deletions
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