summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Liam2023-12-08 21:10:42 -0500
committerGravatar Liam2023-12-09 15:50:34 -0500
commit7ba4a8f4a305a8f5136cd745b73c30becdf7c975 (patch)
tree88298437cb75a1c383393bbda37fd605c30c1db6
parentservice: populate pid and handle table from client (diff)
downloadyuzu-7ba4a8f4a305a8f5136cd745b73c30becdf7c975.tar.gz
yuzu-7ba4a8f4a305a8f5136cd745b73c30becdf7c975.tar.xz
yuzu-7ba4a8f4a305a8f5136cd745b73c30becdf7c975.zip
ro: add separate ro service
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/hle/service/ldr/ldr.cpp634
-rw-r--r--src/core/hle/service/ro/ro.cpp709
-rw-r--r--src/core/hle/service/ro/ro.h14
-rw-r--r--src/core/hle/service/ro/ro_nro_utils.cpp185
-rw-r--r--src/core/hle/service/ro/ro_nro_utils.h26
-rw-r--r--src/core/hle/service/ro/ro_results.h24
-rw-r--r--src/core/hle/service/ro/ro_types.h181
-rw-r--r--src/core/hle/service/service.cpp2
9 files changed, 1147 insertions, 634 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b483fd975..12e0534c4 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -766,6 +766,12 @@ add_library(core STATIC
766 hle/service/kernel_helpers.h 766 hle/service/kernel_helpers.h
767 hle/service/mutex.cpp 767 hle/service/mutex.cpp
768 hle/service/mutex.h 768 hle/service/mutex.h
769 hle/service/ro/ro_nro_utils.cpp
770 hle/service/ro/ro_nro_utils.h
771 hle/service/ro/ro_results.h
772 hle/service/ro/ro_types.h
773 hle/service/ro/ro.cpp
774 hle/service/ro/ro.h
769 hle/service/server_manager.cpp 775 hle/service/server_manager.cpp
770 hle/service/server_manager.h 776 hle/service/server_manager.h
771 hle/service/service.cpp 777 hle/service/service.cpp
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 97b6a9385..ba58b3a09 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -1,117 +1,12 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <memory>
5#include <fmt/format.h>
6#include <mbedtls/sha256.h>
7
8#include "common/alignment.h"
9#include "common/hex_util.h"
10#include "common/scope_exit.h"
11#include "core/core.h"
12#include "core/hle/kernel/k_page_table.h"
13#include "core/hle/kernel/svc_results.h"
14#include "core/hle/kernel/svc_types.h"
15#include "core/hle/service/ipc_helpers.h"
16#include "core/hle/service/ldr/ldr.h" 4#include "core/hle/service/ldr/ldr.h"
17#include "core/hle/service/server_manager.h" 5#include "core/hle/service/server_manager.h"
18#include "core/hle/service/service.h" 6#include "core/hle/service/service.h"
19#include "core/loader/nro.h"
20#include "core/memory.h"
21 7
22namespace Service::LDR { 8namespace Service::LDR {
23 9
24constexpr Result ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
25
26[[maybe_unused]] constexpr Result ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
27constexpr Result ERROR_INVALID_NRO{ErrorModule::Loader, 52};
28constexpr Result ERROR_INVALID_NRR{ErrorModule::Loader, 53};
29constexpr Result ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
30constexpr Result ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
31constexpr Result ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
32constexpr Result ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
33constexpr Result ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
34constexpr Result ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
35constexpr Result ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
36[[maybe_unused]] constexpr Result ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
37constexpr Result ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
38
39constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
40constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200};
41
42constexpr std::size_t TEXT_INDEX{0};
43constexpr std::size_t RO_INDEX{1};
44constexpr std::size_t DATA_INDEX{2};
45
46struct NRRCertification {
47 u64_le application_id_mask;
48 u64_le application_id_pattern;
49 INSERT_PADDING_BYTES(0x10);
50 std::array<u8, 0x100> public_key; // Also known as modulus
51 std::array<u8, 0x100> signature;
52};
53static_assert(sizeof(NRRCertification) == 0x220, "NRRCertification has invalid size.");
54
55struct NRRHeader {
56 u32_le magic;
57 u32_le certification_signature_key_generation; // 9.0.0+
58 INSERT_PADDING_WORDS(2);
59 NRRCertification certification;
60 std::array<u8, 0x100> signature;
61 u64_le application_id;
62 u32_le size;
63 u8 nrr_kind; // 7.0.0+
64 INSERT_PADDING_BYTES(3);
65 u32_le hash_offset;
66 u32_le hash_count;
67 INSERT_PADDING_WORDS(2);
68};
69static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size.");
70
71struct SegmentHeader {
72 u32_le memory_offset;
73 u32_le memory_size;
74};
75static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size.");
76
77struct NROHeader {
78 // Switchbrew calls this "Start" (0x10)
79 INSERT_PADDING_WORDS(1);
80 u32_le mod_offset;
81 INSERT_PADDING_WORDS(2);
82
83 // Switchbrew calls this "Header" (0x70)
84 u32_le magic;
85 u32_le version;
86 u32_le nro_size;
87 u32_le flags;
88 // .text, .ro, .data
89 std::array<SegmentHeader, 3> segment_headers;
90 u32_le bss_size;
91 INSERT_PADDING_WORDS(1);
92 std::array<u8, 0x20> build_id;
93 u32_le dso_handle_offset;
94 INSERT_PADDING_WORDS(1);
95 // .apiInfo, .dynstr, .dynsym
96 std::array<SegmentHeader, 3> segment_headers_2;
97};
98static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
99
100using SHA256Hash = std::array<u8, 0x20>;
101
102struct NROInfo {
103 SHA256Hash hash{};
104 VAddr nro_address{};
105 std::size_t nro_size{};
106 VAddr bss_address{};
107 std::size_t bss_size{};
108 std::size_t text_size{};
109 std::size_t ro_size{};
110 std::size_t data_size{};
111 VAddr src_addr{};
112};
113static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size.");
114
115class DebugMonitor final : public ServiceFramework<DebugMonitor> { 10class DebugMonitor final : public ServiceFramework<DebugMonitor> {
116public: 11public:
117 explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "ldr:dmnt"} { 12 explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "ldr:dmnt"} {
@@ -158,541 +53,12 @@ public:
158 } 53 }
159}; 54};
160 55
161class RelocatableObject final : public ServiceFramework<RelocatableObject> {
162public:
163 explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} {
164 // clang-format off
165 static const FunctionInfo functions[] = {
166 {0, &RelocatableObject::LoadModule, "LoadModule"},
167 {1, &RelocatableObject::UnloadModule, "UnloadModule"},
168 {2, &RelocatableObject::RegisterModuleInfo, "RegisterModuleInfo"},
169 {3, &RelocatableObject::UnregisterModuleInfo, "UnregisterModuleInfo"},
170 {4, &RelocatableObject::Initialize, "Initialize"},
171 {10, nullptr, "RegisterModuleInfo2"},
172 };
173 // clang-format on
174
175 RegisterHandlers(functions);
176 }
177
178 void RegisterModuleInfo(HLERequestContext& ctx) {
179 struct Parameters {
180 u64_le process_id;
181 u64_le nrr_address;
182 u64_le nrr_size;
183 };
184
185 IPC::RequestParser rp{ctx};
186 const auto [process_id, nrr_address, nrr_size] = rp.PopRaw<Parameters>();
187
188 LOG_DEBUG(Service_LDR,
189 "called with process_id={:016X}, nrr_address={:016X}, nrr_size={:016X}",
190 process_id, nrr_address, nrr_size);
191
192 if (!initialized) {
193 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
194 IPC::ResponseBuilder rb{ctx, 2};
195 rb.Push(ERROR_NOT_INITIALIZED);
196 return;
197 }
198
199 if (nrr.size() >= MAXIMUM_LOADED_RO) {
200 LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
201 "(0x40)! Failing...");
202 IPC::ResponseBuilder rb{ctx, 2};
203 rb.Push(ERROR_MAXIMUM_NRR);
204 return;
205 }
206
207 // NRR Address does not fall on 0x1000 byte boundary
208 if (!Common::Is4KBAligned(nrr_address)) {
209 LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!",
210 nrr_address);
211 IPC::ResponseBuilder rb{ctx, 2};
212 rb.Push(ERROR_INVALID_ALIGNMENT);
213 return;
214 }
215
216 // NRR Size is zero or causes overflow
217 if (nrr_address + nrr_size <= nrr_address || nrr_size == 0 ||
218 !Common::Is4KBAligned(nrr_size)) {
219 LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
220 nrr_address, nrr_size);
221 IPC::ResponseBuilder rb{ctx, 2};
222 rb.Push(ERROR_INVALID_SIZE);
223 return;
224 }
225
226 // Read NRR data from memory
227 std::vector<u8> nrr_data(nrr_size);
228 system.ApplicationMemory().ReadBlock(nrr_address, nrr_data.data(), nrr_size);
229 NRRHeader header;
230 std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
231
232 if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
233 LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
234 IPC::ResponseBuilder rb{ctx, 2};
235 rb.Push(ERROR_INVALID_NRR);
236 return;
237 }
238
239 if (header.size != nrr_size) {
240 LOG_ERROR(Service_LDR,
241 "NRR header reported size did not match LoadNrr parameter size! "
242 "(header_size={:016X}, loadnrr_size={:016X})",
243 header.size, nrr_size);
244 IPC::ResponseBuilder rb{ctx, 2};
245 rb.Push(ERROR_INVALID_SIZE);
246 return;
247 }
248
249 if (system.GetApplicationProcessProgramID() != header.application_id) {
250 LOG_ERROR(Service_LDR,
251 "Attempting to load NRR with title ID other than current process. (actual "
252 "{:016X})!",
253 header.application_id);
254 IPC::ResponseBuilder rb{ctx, 2};
255 rb.Push(ERROR_INVALID_NRR);
256 return;
257 }
258
259 std::vector<SHA256Hash> hashes;
260
261 // Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
262 for (std::size_t i = header.hash_offset;
263 i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
264 SHA256Hash hash;
265 std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
266 hashes.emplace_back(hash);
267 }
268
269 nrr.insert_or_assign(nrr_address, std::move(hashes));
270
271 IPC::ResponseBuilder rb{ctx, 2};
272 rb.Push(ResultSuccess);
273 }
274
275 void UnregisterModuleInfo(HLERequestContext& ctx) {
276 IPC::RequestParser rp{ctx};
277 const auto pid = rp.Pop<u64>();
278 const auto nrr_address = rp.Pop<VAddr>();
279
280 LOG_DEBUG(Service_LDR, "called with pid={}, nrr_address={:016X}", pid, nrr_address);
281
282 nrr.erase(nrr_address);
283
284 IPC::ResponseBuilder rb{ctx, 2};
285
286 rb.Push(ResultSuccess);
287 }
288
289 bool ValidateRegionForMap(Kernel::KProcessPageTable& page_table, VAddr start,
290 std::size_t size) const {
291 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
292
293 Kernel::KMemoryInfo start_info;
294 Kernel::Svc::PageInfo page_info;
295 R_ASSERT(
296 page_table.QueryInfo(std::addressof(start_info), std::addressof(page_info), start - 1));
297
298 if (start_info.GetState() != Kernel::KMemoryState::Free) {
299 return {};
300 }
301
302 if (start_info.GetAddress() > (start - padding_size)) {
303 return {};
304 }
305
306 Kernel::KMemoryInfo end_info;
307 R_ASSERT(page_table.QueryInfo(std::addressof(end_info), std::addressof(page_info),
308 start + size));
309
310 if (end_info.GetState() != Kernel::KMemoryState::Free) {
311 return {};
312 }
313
314 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
315 }
316
317 Result GetAvailableMapRegion(Kernel::KProcessPageTable& page_table, u64 size, VAddr& out_addr) {
318 size = Common::AlignUp(size, Kernel::PageSize);
319 size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
320
321 const auto is_region_available = [&](VAddr addr) {
322 const auto end_addr = addr + size;
323 while (addr < end_addr) {
324 if (system.ApplicationMemory().IsValidVirtualAddress(addr)) {
325 return false;
326 }
327
328 if (!page_table.Contains(out_addr, size)) {
329 return false;
330 }
331
332 if (page_table.IsInHeapRegion(out_addr, size)) {
333 return false;
334 }
335
336 if (page_table.IsInAliasRegion(out_addr, size)) {
337 return false;
338 }
339
340 addr += Kernel::PageSize;
341 }
342 return true;
343 };
344
345 bool succeeded = false;
346 const auto map_region_end =
347 GetInteger(page_table.GetAliasCodeRegionStart()) + page_table.GetAliasCodeRegionSize();
348 while (current_map_addr < map_region_end) {
349 if (is_region_available(current_map_addr)) {
350 succeeded = true;
351 break;
352 }
353 current_map_addr += 0x100000;
354 }
355
356 if (!succeeded) {
357 ASSERT_MSG(false, "Out of address space!");
358 return Kernel::ResultOutOfMemory;
359 }
360
361 out_addr = current_map_addr;
362 current_map_addr += size;
363
364 return ResultSuccess;
365 }
366
367 Result MapProcessCodeMemory(VAddr* out_map_location, Kernel::KProcess* process, VAddr base_addr,
368 u64 size) {
369 auto& page_table{process->GetPageTable()};
370 VAddr addr{};
371
372 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
373 R_TRY(GetAvailableMapRegion(page_table, size, addr));
374
375 const Result result{page_table.MapCodeMemory(addr, base_addr, size)};
376 if (result == Kernel::ResultInvalidCurrentMemory) {
377 continue;
378 }
379
380 R_TRY(result);
381
382 if (ValidateRegionForMap(page_table, addr, size)) {
383 *out_map_location = addr;
384 return ResultSuccess;
385 }
386 }
387
388 return ERROR_INSUFFICIENT_ADDRESS_SPACE;
389 }
390
391 Result MapNro(VAddr* out_map_location, Kernel::KProcess* process, VAddr nro_addr,
392 std::size_t nro_size, VAddr bss_addr, std::size_t bss_size, std::size_t size) {
393 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
394 auto& page_table{process->GetPageTable()};
395 VAddr addr{};
396
397 R_TRY(MapProcessCodeMemory(&addr, process, nro_addr, nro_size));
398
399 if (bss_size) {
400 auto block_guard = detail::ScopeExit([&] {
401 page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
402 page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
403 });
404
405 const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
406
407 if (result == Kernel::ResultInvalidCurrentMemory) {
408 continue;
409 }
410
411 if (result.IsError()) {
412 return result;
413 }
414
415 block_guard.Cancel();
416 }
417
418 if (ValidateRegionForMap(page_table, addr, size)) {
419 *out_map_location = addr;
420 return ResultSuccess;
421 }
422 }
423
424 return ERROR_INSUFFICIENT_ADDRESS_SPACE;
425 }
426
427 Result LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr,
428 VAddr start) const {
429 const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset};
430 const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset};
431 const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset};
432 const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size};
433 const VAddr bss_end_addr{
434 Common::AlignUp(bss_start + nro_header.bss_size, Kernel::PageSize)};
435
436 const auto CopyCode = [this](VAddr src_addr, VAddr dst_addr, u64 size) {
437 system.ApplicationMemory().CopyBlock(dst_addr, src_addr, size);
438 };
439 CopyCode(nro_addr + nro_header.segment_headers[TEXT_INDEX].memory_offset, text_start,
440 nro_header.segment_headers[TEXT_INDEX].memory_size);
441 CopyCode(nro_addr + nro_header.segment_headers[RO_INDEX].memory_offset, ro_start,
442 nro_header.segment_headers[RO_INDEX].memory_size);
443 CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start,
444 nro_header.segment_headers[DATA_INDEX].memory_size);
445
446 R_TRY(process->GetPageTable().SetProcessMemoryPermission(
447 text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute));
448 R_TRY(process->GetPageTable().SetProcessMemoryPermission(
449 ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read));
450
451 return process->GetPageTable().SetProcessMemoryPermission(
452 data_start, bss_end_addr - data_start, Kernel::Svc::MemoryPermission::ReadWrite);
453 }
454
455 void LoadModule(HLERequestContext& ctx) {
456 struct Parameters {
457 u64_le process_id;
458 u64_le image_address;
459 u64_le image_size;
460 u64_le bss_address;
461 u64_le bss_size;
462 };
463
464 IPC::RequestParser rp{ctx};
465 const auto [process_id, nro_address, nro_size, bss_address, bss_size] =
466 rp.PopRaw<Parameters>();
467
468 LOG_DEBUG(Service_LDR,
469 "called with pid={:016X}, nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, "
470 "bss_size={:016X}",
471 process_id, nro_address, nro_size, bss_address, bss_size);
472
473 if (!initialized) {
474 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
475 IPC::ResponseBuilder rb{ctx, 2};
476 rb.Push(ERROR_NOT_INITIALIZED);
477 return;
478 }
479
480 if (nro.size() >= MAXIMUM_LOADED_RO) {
481 LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
482 "(0x40)! Failing...");
483 IPC::ResponseBuilder rb{ctx, 2};
484 rb.Push(ERROR_MAXIMUM_NRO);
485 return;
486 }
487
488 // NRO Address does not fall on 0x1000 byte boundary
489 if (!Common::Is4KBAligned(nro_address)) {
490 LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!",
491 nro_address);
492 IPC::ResponseBuilder rb{ctx, 2};
493 rb.Push(ERROR_INVALID_ALIGNMENT);
494 return;
495 }
496
497 // NRO Size or BSS Size is zero or causes overflow
498 const auto nro_size_valid =
499 nro_size != 0 && nro_address + nro_size > nro_address && Common::Is4KBAligned(nro_size);
500 const auto bss_size_valid = nro_size + bss_size >= nro_size &&
501 (bss_size == 0 || bss_address + bss_size > bss_address);
502
503 if (!nro_size_valid || !bss_size_valid) {
504 LOG_ERROR(Service_LDR,
505 "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
506 "bss_address={:016X}, bss_size={:016X})",
507 nro_address, nro_size, bss_address, bss_size);
508 IPC::ResponseBuilder rb{ctx, 2};
509 rb.Push(ERROR_INVALID_SIZE);
510 return;
511 }
512
513 // Read NRO data from memory
514 std::vector<u8> nro_data(nro_size);
515 system.ApplicationMemory().ReadBlock(nro_address, nro_data.data(), nro_size);
516
517 SHA256Hash hash{};
518 mbedtls_sha256_ret(nro_data.data(), nro_data.size(), hash.data(), 0);
519
520 // NRO Hash is already loaded
521 if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
522 return info.second.hash == hash;
523 })) {
524 LOG_ERROR(Service_LDR, "NRO is already loaded!");
525 IPC::ResponseBuilder rb{ctx, 2};
526 rb.Push(ERROR_ALREADY_LOADED);
527 return;
528 }
529
530 // NRO Hash is not in any loaded NRR
531 if (!IsValidNROHash(hash)) {
532 LOG_ERROR(Service_LDR,
533 "NRO hash is not present in any currently loaded NRRs (hash={})!",
534 Common::HexToString(hash));
535 IPC::ResponseBuilder rb{ctx, 2};
536 rb.Push(ERROR_MISSING_NRR_HASH);
537 return;
538 }
539
540 // Load and validate the NRO header
541 NROHeader header{};
542 std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
543 if (!IsValidNRO(header, nro_size, bss_size)) {
544 LOG_ERROR(Service_LDR, "NRO was invalid!");
545 IPC::ResponseBuilder rb{ctx, 2};
546 rb.Push(ERROR_INVALID_NRO);
547 return;
548 }
549
550 // Map memory for the NRO
551 VAddr map_location{};
552 const auto map_result{MapNro(&map_location, system.ApplicationProcess(), nro_address,
553 nro_size, bss_address, bss_size, nro_size + bss_size)};
554 if (map_result != ResultSuccess) {
555 IPC::ResponseBuilder rb{ctx, 2};
556 rb.Push(map_result);
557 }
558
559 // Load the NRO into the mapped memory
560 if (const auto result{
561 LoadNro(system.ApplicationProcess(), header, nro_address, map_location)};
562 result.IsError()) {
563 IPC::ResponseBuilder rb{ctx, 2};
564 rb.Push(result);
565 }
566
567 // Track the loaded NRO
568 nro.insert_or_assign(map_location,
569 NROInfo{hash, map_location, nro_size, bss_address, bss_size,
570 header.segment_headers[TEXT_INDEX].memory_size,
571 header.segment_headers[RO_INDEX].memory_size,
572 header.segment_headers[DATA_INDEX].memory_size, nro_address});
573
574 IPC::ResponseBuilder rb{ctx, 4};
575 rb.Push(ResultSuccess);
576 rb.Push(map_location);
577 }
578
579 Result UnmapNro(const NROInfo& info) {
580 // Each region must be unmapped separately to validate memory state
581 auto& page_table{system.ApplicationProcess()->GetPageTable()};
582
583 if (info.bss_size != 0) {
584 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size +
585 info.data_size,
586 info.bss_address, info.bss_size));
587 }
588
589 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
590 info.src_addr + info.text_size + info.ro_size,
591 info.data_size));
592 R_TRY(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
593 info.src_addr + info.text_size, info.ro_size));
594 R_TRY(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
595 return ResultSuccess;
596 }
597
598 void UnloadModule(HLERequestContext& ctx) {
599 if (!initialized) {
600 LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
601 IPC::ResponseBuilder rb{ctx, 2};
602 rb.Push(ERROR_NOT_INITIALIZED);
603 return;
604 }
605
606 struct Parameters {
607 u64_le process_id;
608 u64_le nro_address;
609 };
610
611 IPC::RequestParser rp{ctx};
612 const auto [process_id, nro_address] = rp.PopRaw<Parameters>();
613 LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nro_address=0x{:016X}", process_id,
614 nro_address);
615
616 if (!Common::Is4KBAligned(nro_address)) {
617 LOG_ERROR(Service_LDR, "NRO address has invalid alignment (nro_address=0x{:016X})",
618 nro_address);
619 IPC::ResponseBuilder rb{ctx, 2};
620 rb.Push(ERROR_INVALID_ALIGNMENT);
621 return;
622 }
623
624 const auto iter = nro.find(nro_address);
625 if (iter == nro.end()) {
626 LOG_ERROR(Service_LDR,
627 "The NRO attempting to be unmapped was not mapped or has an invalid address "
628 "(nro_address=0x{:016X})!",
629 nro_address);
630 IPC::ResponseBuilder rb{ctx, 2};
631 rb.Push(ERROR_INVALID_NRO_ADDRESS);
632 return;
633 }
634
635 const auto result{UnmapNro(iter->second)};
636
637 nro.erase(iter);
638
639 IPC::ResponseBuilder rb{ctx, 2};
640
641 rb.Push(result);
642 }
643
644 void Initialize(HLERequestContext& ctx) {
645 LOG_WARNING(Service_LDR, "(STUBBED) called");
646
647 initialized = true;
648 current_map_addr =
649 GetInteger(system.ApplicationProcess()->GetPageTable().GetAliasCodeRegionStart());
650
651 IPC::ResponseBuilder rb{ctx, 2};
652 rb.Push(ResultSuccess);
653 }
654
655private:
656 bool initialized{};
657
658 std::map<VAddr, NROInfo> nro;
659 std::map<VAddr, std::vector<SHA256Hash>> nrr;
660 VAddr current_map_addr{};
661
662 bool IsValidNROHash(const SHA256Hash& hash) const {
663 return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
664 return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
665 });
666 }
667
668 static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
669 return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
670 header.nro_size == nro_size && header.bss_size == bss_size &&
671
672 header.segment_headers[RO_INDEX].memory_offset ==
673 header.segment_headers[TEXT_INDEX].memory_offset +
674 header.segment_headers[TEXT_INDEX].memory_size &&
675
676 header.segment_headers[DATA_INDEX].memory_offset ==
677 header.segment_headers[RO_INDEX].memory_offset +
678 header.segment_headers[RO_INDEX].memory_size &&
679
680 nro_size == header.segment_headers[DATA_INDEX].memory_offset +
681 header.segment_headers[DATA_INDEX].memory_size &&
682
683 Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size) &&
684 Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size) &&
685 Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size);
686 }
687};
688
689void LoopProcess(Core::System& system) { 56void LoopProcess(Core::System& system) {
690 auto server_manager = std::make_unique<ServerManager>(system); 57 auto server_manager = std::make_unique<ServerManager>(system);
691 58
692 server_manager->RegisterNamedService("ldr:dmnt", std::make_shared<DebugMonitor>(system)); 59 server_manager->RegisterNamedService("ldr:dmnt", std::make_shared<DebugMonitor>(system));
693 server_manager->RegisterNamedService("ldr:pm", std::make_shared<ProcessManager>(system)); 60 server_manager->RegisterNamedService("ldr:pm", std::make_shared<ProcessManager>(system));
694 server_manager->RegisterNamedService("ldr:shel", std::make_shared<Shell>(system)); 61 server_manager->RegisterNamedService("ldr:shel", std::make_shared<Shell>(system));
695 server_manager->RegisterNamedService("ldr:ro", std::make_shared<RelocatableObject>(system));
696 62
697 ServerManager::RunServer(std::move(server_manager)); 63 ServerManager::RunServer(std::move(server_manager));
698} 64}
diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp
new file mode 100644
index 000000000..17110d3f1
--- /dev/null
+++ b/src/core/hle/service/ro/ro.cpp
@@ -0,0 +1,709 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <mbedtls/sha256.h>
5
6#include "common/scope_exit.h"
7#include "core/hle/kernel/k_process.h"
8
9#include "core/hle/service/ipc_helpers.h"
10#include "core/hle/service/ro/ro.h"
11#include "core/hle/service/ro/ro_nro_utils.h"
12#include "core/hle/service/ro/ro_results.h"
13#include "core/hle/service/ro/ro_types.h"
14#include "core/hle/service/server_manager.h"
15#include "core/hle/service/service.h"
16
17namespace Service::RO {
18
19namespace {
20
21// Convenience definitions.
22constexpr size_t MaxSessions = 0x3;
23constexpr size_t MaxNrrInfos = 0x40;
24constexpr size_t MaxNroInfos = 0x40;
25
26constexpr u64 InvalidProcessId = 0xffffffffffffffffULL;
27constexpr u64 InvalidContextId = 0xffffffffffffffffULL;
28
29// Types.
30using Sha256Hash = std::array<u8, 32>;
31
32struct NroInfo {
33 u64 base_address;
34 u64 nro_heap_address;
35 u64 nro_heap_size;
36 u64 bss_heap_address;
37 u64 bss_heap_size;
38 u64 code_size;
39 u64 rw_size;
40 ModuleId module_id;
41};
42
43struct NrrInfo {
44 u64 nrr_heap_address;
45 u64 nrr_heap_size;
46
47 // Verification.
48 std::vector<Sha256Hash> hashes;
49};
50
51struct ProcessContext {
52 constexpr ProcessContext() = default;
53
54 void Initialize(Kernel::KProcess* process, u64 process_id) {
55 ASSERT(!m_in_use);
56
57 m_nro_in_use = {};
58 m_nrr_in_use = {};
59 m_nro_infos = {};
60 m_nrr_infos = {};
61
62 m_process = process;
63 m_process_id = process_id;
64 m_in_use = true;
65
66 if (m_process) {
67 m_process->Open();
68 }
69 }
70
71 void Finalize() {
72 ASSERT(m_in_use);
73
74 if (m_process) {
75 m_process->Close();
76 }
77
78 m_nro_in_use = {};
79 m_nrr_in_use = {};
80 m_nro_infos = {};
81 m_nrr_infos = {};
82
83 m_process = nullptr;
84 m_process_id = InvalidProcessId;
85 m_in_use = false;
86 }
87
88 Kernel::KProcess* GetProcess() const {
89 return m_process;
90 }
91
92 u64 GetProcessId() const {
93 return m_process_id;
94 }
95
96 bool IsFree() const {
97 return !m_in_use;
98 }
99
100 u64 GetProgramId(Kernel::KProcess* other_process) const {
101 // Automatically select a handle, allowing for override.
102 if (other_process) {
103 return other_process->GetProgramId();
104 } else if (m_process) {
105 return m_process->GetProgramId();
106 } else {
107 return 0;
108 }
109 }
110
111 Result GetNrrInfoByAddress(NrrInfo** out, u64 nrr_heap_address) {
112 for (size_t i = 0; i < MaxNrrInfos; i++) {
113 if (m_nrr_in_use[i] && m_nrr_infos[i].nrr_heap_address == nrr_heap_address) {
114 if (out != nullptr) {
115 *out = std::addressof(m_nrr_infos[i]);
116 }
117 R_SUCCEED();
118 }
119 }
120 R_THROW(RO::ResultNotRegistered);
121 }
122
123 Result GetFreeNrrInfo(NrrInfo** out) {
124 for (size_t i = 0; i < MaxNrrInfos; i++) {
125 if (!m_nrr_in_use[i]) {
126 if (out != nullptr) {
127 *out = std::addressof(m_nrr_infos[i]);
128 }
129 R_SUCCEED();
130 }
131 }
132 R_THROW(RO::ResultTooManyNrr);
133 }
134
135 Result GetNroInfoByAddress(NroInfo** out, u64 nro_address) {
136 for (size_t i = 0; i < MaxNroInfos; i++) {
137 if (m_nro_in_use[i] && m_nro_infos[i].base_address == nro_address) {
138 if (out != nullptr) {
139 *out = std::addressof(m_nro_infos[i]);
140 }
141 R_SUCCEED();
142 }
143 }
144 R_THROW(RO::ResultNotLoaded);
145 }
146
147 Result GetNroInfoByModuleId(NroInfo** out, const ModuleId* module_id) {
148 for (size_t i = 0; i < MaxNroInfos; i++) {
149 if (m_nro_in_use[i] && std::memcmp(std::addressof(m_nro_infos[i].module_id), module_id,
150 sizeof(*module_id)) == 0) {
151 if (out != nullptr) {
152 *out = std::addressof(m_nro_infos[i]);
153 }
154 R_SUCCEED();
155 }
156 }
157 R_THROW(RO::ResultNotLoaded);
158 }
159
160 Result GetFreeNroInfo(NroInfo** out) {
161 for (size_t i = 0; i < MaxNroInfos; i++) {
162 if (!m_nro_in_use[i]) {
163 if (out != nullptr) {
164 *out = std::addressof(m_nro_infos[i]);
165 }
166 R_SUCCEED();
167 }
168 }
169 R_THROW(RO::ResultTooManyNro);
170 }
171
172 Result ValidateHasNroHash(u64 base_address, const NroHeader* nro_header) const {
173 // Calculate hash.
174 Sha256Hash hash;
175 {
176 const u64 size = nro_header->GetSize();
177
178 std::vector<u8> nro_data(size);
179 m_process->GetMemory().ReadBlock(base_address, nro_data.data(), size);
180
181 mbedtls_sha256_ret(nro_data.data(), size, hash.data(), 0);
182 }
183
184 for (size_t i = 0; i < MaxNrrInfos; i++) {
185 // Ensure we only check NRRs that are used.
186 if (!m_nrr_in_use[i]) {
187 continue;
188 }
189
190 // Locate the hash within the hash list.
191 const auto hash_it = std::ranges::find(m_nrr_infos[i].hashes, hash);
192 if (hash_it == m_nrr_infos[i].hashes.end()) {
193 continue;
194 }
195
196 // The hash is valid!
197 R_SUCCEED();
198 }
199
200 R_THROW(RO::ResultNotAuthorized);
201 }
202
203 Result ValidateNro(ModuleId* out_module_id, u64* out_rx_size, u64* out_ro_size,
204 u64* out_rw_size, u64 base_address, u64 expected_nro_size,
205 u64 expected_bss_size) {
206 // Ensure we have a process to work on.
207 R_UNLESS(m_process != nullptr, RO::ResultInvalidProcess);
208
209 // Read the NRO header.
210 NroHeader header{};
211 m_process->GetMemory().ReadBlock(base_address, std::addressof(header), sizeof(header));
212
213 // Validate header.
214 R_UNLESS(header.IsMagicValid(), RO::ResultInvalidNro);
215
216 // Read sizes from header.
217 const u64 nro_size = header.GetSize();
218 const u64 text_ofs = header.GetTextOffset();
219 const u64 text_size = header.GetTextSize();
220 const u64 ro_ofs = header.GetRoOffset();
221 const u64 ro_size = header.GetRoSize();
222 const u64 rw_ofs = header.GetRwOffset();
223 const u64 rw_size = header.GetRwSize();
224 const u64 bss_size = header.GetBssSize();
225
226 // Validate sizes meet expected.
227 R_UNLESS(nro_size == expected_nro_size, RO::ResultInvalidNro);
228 R_UNLESS(bss_size == expected_bss_size, RO::ResultInvalidNro);
229
230 // Validate all sizes are aligned.
231 R_UNLESS(Common::IsAligned(text_size, Core::Memory::YUZU_PAGESIZE), RO::ResultInvalidNro);
232 R_UNLESS(Common::IsAligned(ro_size, Core::Memory::YUZU_PAGESIZE), RO::ResultInvalidNro);
233 R_UNLESS(Common::IsAligned(rw_size, Core::Memory::YUZU_PAGESIZE), RO::ResultInvalidNro);
234 R_UNLESS(Common::IsAligned(bss_size, Core::Memory::YUZU_PAGESIZE), RO::ResultInvalidNro);
235
236 // Validate sections are in order.
237 R_UNLESS(text_ofs <= ro_ofs, RO::ResultInvalidNro);
238 R_UNLESS(ro_ofs <= rw_ofs, RO::ResultInvalidNro);
239
240 // Validate sections are sequential and contiguous.
241 R_UNLESS(text_ofs == 0, RO::ResultInvalidNro);
242 R_UNLESS(text_ofs + text_size == ro_ofs, RO::ResultInvalidNro);
243 R_UNLESS(ro_ofs + ro_size == rw_ofs, RO::ResultInvalidNro);
244 R_UNLESS(rw_ofs + rw_size == nro_size, RO::ResultInvalidNro);
245
246 // Verify NRO hash.
247 R_TRY(this->ValidateHasNroHash(base_address, std::addressof(header)));
248
249 // Check if NRO has already been loaded.
250 const ModuleId* module_id = header.GetModuleId();
251 R_UNLESS(R_FAILED(this->GetNroInfoByModuleId(nullptr, module_id)), RO::ResultAlreadyLoaded);
252
253 // Apply patches to NRO.
254 // LocateAndApplyIpsPatchesToModule(module_id, static_cast<u8*>(mapped_memory), nro_size);
255
256 // Copy to output.
257 *out_module_id = *module_id;
258 *out_rx_size = text_size;
259 *out_ro_size = ro_size;
260 *out_rw_size = rw_size;
261 R_SUCCEED();
262 }
263
264 void SetNrrInfoInUse(const NrrInfo* info, bool in_use) {
265 ASSERT(std::addressof(m_nrr_infos[0]) <= info &&
266 info <= std::addressof(m_nrr_infos[MaxNrrInfos - 1]));
267 const size_t index = info - std::addressof(m_nrr_infos[0]);
268 m_nrr_in_use[index] = in_use;
269 }
270
271 void SetNroInfoInUse(const NroInfo* info, bool in_use) {
272 ASSERT(std::addressof(m_nro_infos[0]) <= info &&
273 info <= std::addressof(m_nro_infos[MaxNroInfos - 1]));
274 const size_t index = info - std::addressof(m_nro_infos[0]);
275 m_nro_in_use[index] = in_use;
276 }
277
278private:
279 std::array<bool, MaxNroInfos> m_nro_in_use{};
280 std::array<bool, MaxNrrInfos> m_nrr_in_use{};
281 std::array<NroInfo, MaxNroInfos> m_nro_infos{};
282 std::array<NrrInfo, MaxNrrInfos> m_nrr_infos{};
283 Kernel::KProcess* m_process{};
284 u64 m_process_id{InvalidProcessId};
285 bool m_in_use{};
286};
287
288Result ValidateAddressAndNonZeroSize(u64 address, u64 size) {
289 R_UNLESS(Common::IsAligned(address, Core::Memory::YUZU_PAGESIZE), RO::ResultInvalidAddress);
290 R_UNLESS(size != 0, RO::ResultInvalidSize);
291 R_UNLESS(Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE), RO::ResultInvalidSize);
292 R_UNLESS(address < address + size, RO::ResultInvalidSize);
293 R_SUCCEED();
294}
295
296Result ValidateAddressAndSize(u64 address, u64 size) {
297 R_UNLESS(Common::IsAligned(address, Core::Memory::YUZU_PAGESIZE), RO::ResultInvalidAddress);
298 R_UNLESS(Common::IsAligned(size, Core::Memory::YUZU_PAGESIZE), RO::ResultInvalidSize);
299 R_UNLESS(size == 0 || address < address + size, RO::ResultInvalidSize);
300 R_SUCCEED();
301}
302
303class RoContext {
304public:
305 explicit RoContext() = default;
306
307 Result RegisterProcess(size_t* out_context_id, Kernel::KProcess* process, u64 process_id) {
308 // Validate process id.
309 R_UNLESS(process->GetProcessId() == process_id, RO::ResultInvalidProcess);
310
311 // Check if a process context already exists.
312 R_UNLESS(this->GetContextByProcessId(process_id) == nullptr, RO::ResultInvalidSession);
313
314 // Allocate a context to manage the process handle.
315 *out_context_id = this->AllocateContext(process, process_id);
316
317 R_SUCCEED();
318 }
319
320 Result ValidateProcess(size_t context_id, u64 process_id) {
321 const ProcessContext* ctx = this->GetContextById(context_id);
322 R_UNLESS(ctx != nullptr, RO::ResultInvalidProcess);
323 R_UNLESS(ctx->GetProcessId() == process_id, RO::ResultInvalidProcess);
324 R_SUCCEED();
325 }
326
327 void UnregisterProcess(size_t context_id) {
328 this->FreeContext(context_id);
329 }
330
331 Result RegisterModuleInfo(size_t context_id, u64 nrr_address, u64 nrr_size, NrrKind nrr_kind,
332 bool enforce_nrr_kind) {
333 // Get context.
334 ProcessContext* context = this->GetContextById(context_id);
335 ASSERT(context != nullptr);
336
337 // Validate address/size.
338 R_TRY(ValidateAddressAndNonZeroSize(nrr_address, nrr_size));
339
340 // Check we have space for a new NRR.
341 NrrInfo* nrr_info = nullptr;
342 R_TRY(context->GetFreeNrrInfo(std::addressof(nrr_info)));
343
344 // Ensure we have a valid process to read from.
345 Kernel::KProcess* process = context->GetProcess();
346 R_UNLESS(process != nullptr, RO::ResultInvalidProcess);
347
348 // Read NRR.
349 NrrHeader header{};
350 process->GetMemory().ReadBlock(nrr_address, std::addressof(header), sizeof(header));
351
352 // Set NRR info.
353 context->SetNrrInfoInUse(nrr_info, true);
354 nrr_info->nrr_heap_address = nrr_address;
355 nrr_info->nrr_heap_size = nrr_size;
356
357 // Read NRR hash list.
358 nrr_info->hashes.resize(header.GetNumHashes());
359 process->GetMemory().ReadBlock(nrr_address + header.GetHashesOffset(),
360 nrr_info->hashes.data(),
361 sizeof(Sha256Hash) * header.GetNumHashes());
362
363 R_SUCCEED();
364 }
365
366 Result UnregisterModuleInfo(size_t context_id, u64 nrr_address) {
367 // Get context.
368 ProcessContext* context = this->GetContextById(context_id);
369 ASSERT(context != nullptr);
370
371 // Validate address.
372 R_UNLESS(Common::IsAligned(nrr_address, Core::Memory::YUZU_PAGESIZE),
373 RO::ResultInvalidAddress);
374
375 // Check the NRR is loaded.
376 NrrInfo* nrr_info = nullptr;
377 R_TRY(context->GetNrrInfoByAddress(std::addressof(nrr_info), nrr_address));
378
379 // Nintendo does this unconditionally, whether or not the actual unmap succeeds.
380 context->SetNrrInfoInUse(nrr_info, false);
381 *nrr_info = {};
382
383 R_SUCCEED();
384 }
385
386 Result MapManualLoadModuleMemory(u64* out_address, size_t context_id, u64 nro_address,
387 u64 nro_size, u64 bss_address, u64 bss_size) {
388 // Get context.
389 ProcessContext* context = this->GetContextById(context_id);
390 ASSERT(context != nullptr);
391
392 // Validate address/size.
393 R_TRY(ValidateAddressAndNonZeroSize(nro_address, nro_size));
394 R_TRY(ValidateAddressAndSize(bss_address, bss_size));
395
396 const u64 total_size = nro_size + bss_size;
397 R_UNLESS(total_size >= nro_size, RO::ResultInvalidSize);
398 R_UNLESS(total_size >= bss_size, RO::ResultInvalidSize);
399
400 // Check we have space for a new NRO.
401 NroInfo* nro_info = nullptr;
402 R_TRY(context->GetFreeNroInfo(std::addressof(nro_info)));
403 nro_info->nro_heap_address = nro_address;
404 nro_info->nro_heap_size = nro_size;
405 nro_info->bss_heap_address = bss_address;
406 nro_info->bss_heap_size = bss_size;
407
408 // Map the NRO.
409 R_TRY(MapNro(std::addressof(nro_info->base_address), context->GetProcess(), nro_address,
410 nro_size, bss_address, bss_size, generate_random));
411 ON_RESULT_FAILURE {
412 UnmapNro(context->GetProcess(), nro_info->base_address, nro_address, nro_size,
413 bss_address, bss_size);
414 };
415
416 // Validate the NRO (parsing region extents).
417 u64 rx_size = 0, ro_size = 0, rw_size = 0;
418 R_TRY(context->ValidateNro(std::addressof(nro_info->module_id), std::addressof(rx_size),
419 std::addressof(ro_size), std::addressof(rw_size),
420 nro_info->base_address, nro_size, bss_size));
421
422 // Set NRO perms.
423 R_TRY(SetNroPerms(context->GetProcess(), nro_info->base_address, rx_size, ro_size,
424 rw_size + bss_size));
425
426 context->SetNroInfoInUse(nro_info, true);
427 nro_info->code_size = rx_size + ro_size;
428 nro_info->rw_size = rw_size;
429 *out_address = nro_info->base_address;
430 R_SUCCEED();
431 }
432
433 Result UnmapManualLoadModuleMemory(size_t context_id, u64 nro_address) {
434 // Get context.
435 ProcessContext* context = this->GetContextById(context_id);
436 ASSERT(context != nullptr);
437
438 // Validate address.
439 R_UNLESS(Common::IsAligned(nro_address, Core::Memory::YUZU_PAGESIZE),
440 RO::ResultInvalidAddress);
441
442 // Check the NRO is loaded.
443 NroInfo* nro_info = nullptr;
444 R_TRY(context->GetNroInfoByAddress(std::addressof(nro_info), nro_address));
445
446 // Unmap.
447 const NroInfo nro_backup = *nro_info;
448 {
449 // Nintendo does this unconditionally, whether or not the actual unmap succeeds.
450 context->SetNroInfoInUse(nro_info, false);
451 std::memset(nro_info, 0, sizeof(*nro_info));
452 }
453 R_RETURN(UnmapNro(context->GetProcess(), nro_backup.base_address,
454 nro_backup.nro_heap_address, nro_backup.code_size + nro_backup.rw_size,
455 nro_backup.bss_heap_address, nro_backup.bss_heap_size));
456 }
457
458private:
459 std::array<ProcessContext, MaxSessions> process_contexts;
460 std::mt19937_64 generate_random;
461
462 // Context Helpers.
463 ProcessContext* GetContextById(size_t context_id) {
464 if (context_id == InvalidContextId) {
465 return nullptr;
466 }
467
468 ASSERT(context_id < process_contexts.size());
469 return std::addressof(process_contexts[context_id]);
470 }
471
472 ProcessContext* GetContextByProcessId(u64 process_id) {
473 for (size_t i = 0; i < MaxSessions; i++) {
474 if (process_contexts[i].GetProcessId() == process_id) {
475 return std::addressof(process_contexts[i]);
476 }
477 }
478 return nullptr;
479 }
480
481 size_t AllocateContext(Kernel::KProcess* process, u64 process_id) {
482 // Find a free process context.
483 for (size_t i = 0; i < MaxSessions; i++) {
484 ProcessContext* context = std::addressof(process_contexts[i]);
485
486 if (context->IsFree()) {
487 context->Initialize(process, process_id);
488 return i;
489 }
490 }
491
492 // Failure to find a free context is actually an abort condition.
493 UNREACHABLE();
494 }
495
496 void FreeContext(size_t context_id) {
497 if (ProcessContext* context = GetContextById(context_id); context != nullptr) {
498 context->Finalize();
499 }
500 }
501};
502
503class RoInterface {
504public:
505 explicit RoInterface(std::shared_ptr<RoContext> ro, NrrKind nrr_kind)
506 : m_ro(ro), m_context_id(InvalidContextId), m_nrr_kind(nrr_kind) {}
507 ~RoInterface() {
508 m_ro->UnregisterProcess(m_context_id);
509 }
510
511 Result MapManualLoadModuleMemory(u64* out_load_address, u64 client_pid, u64 nro_address,
512 u64 nro_size, u64 bss_address, u64 bss_size) {
513 R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
514 R_RETURN(m_ro->MapManualLoadModuleMemory(out_load_address, m_context_id, nro_address,
515 nro_size, bss_address, bss_size));
516 }
517
518 Result UnmapManualLoadModuleMemory(u64 client_pid, u64 nro_address) {
519 R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
520 R_RETURN(m_ro->UnmapManualLoadModuleMemory(m_context_id, nro_address));
521 }
522
523 Result RegisterModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size) {
524 R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
525 R_RETURN(
526 m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, NrrKind::User, true));
527 }
528
529 Result UnregisterModuleInfo(u64 client_pid, u64 nrr_address) {
530 R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
531 R_RETURN(m_ro->UnregisterModuleInfo(m_context_id, nrr_address));
532 }
533
534 Result RegisterProcessHandle(u64 client_pid, Kernel::KProcess* process) {
535 // Register the process.
536 R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process, client_pid));
537 }
538
539 Result RegisterProcessModuleInfo(u64 client_pid, u64 nrr_address, u64 nrr_size,
540 Kernel::KProcess* process) {
541 // Validate the process.
542 R_TRY(m_ro->ValidateProcess(m_context_id, client_pid));
543
544 // Register the module.
545 R_RETURN(m_ro->RegisterModuleInfo(m_context_id, nrr_address, nrr_size, m_nrr_kind,
546 m_nrr_kind == NrrKind::JitPlugin));
547 }
548
549private:
550 std::shared_ptr<RoContext> m_ro{};
551 size_t m_context_id{};
552 NrrKind m_nrr_kind{};
553};
554
555class IRoInterface : public ServiceFramework<IRoInterface> {
556public:
557 explicit IRoInterface(Core::System& system_, const char* name_, std::shared_ptr<RoContext> ro,
558 NrrKind nrr_kind)
559 : ServiceFramework{system_, name_}, interface {
560 ro, nrr_kind
561 } {
562 // clang-format off
563 static const FunctionInfo functions[] = {
564 {0, &IRoInterface::MapManualLoadModuleMemory, "MapManualLoadModuleMemory"},
565 {1, &IRoInterface::UnmapManualLoadModuleMemory, "UnmapManualLoadModuleMemory"},
566 {2, &IRoInterface::RegisterModuleInfo, "RegisterModuleInfo"},
567 {3, &IRoInterface::UnregisterModuleInfo, "UnregisterModuleInfo"},
568 {4, &IRoInterface::RegisterProcessHandle, "RegisterProcessHandle"},
569 {10, &IRoInterface::RegisterProcessModuleInfo, "RegisterProcessModuleInfo"},
570 };
571 // clang-format on
572
573 RegisterHandlers(functions);
574 }
575
576private:
577 void MapManualLoadModuleMemory(HLERequestContext& ctx) {
578 LOG_DEBUG(Service_LDR, "(called)");
579
580 struct InputParameters {
581 u64 client_pid;
582 u64 nro_address;
583 u64 nro_size;
584 u64 bss_address;
585 u64 bss_size;
586 };
587
588 IPC::RequestParser rp{ctx};
589 auto params = rp.PopRaw<InputParameters>();
590
591 u64 load_address = 0;
592 auto result = interface.MapManualLoadModuleMemory(&load_address, ctx.GetPID(),
593 params.nro_address, params.nro_size,
594 params.bss_address, params.bss_size);
595
596 IPC::ResponseBuilder rb{ctx, 4};
597 rb.Push(result);
598 rb.Push(load_address);
599 }
600
601 void UnmapManualLoadModuleMemory(HLERequestContext& ctx) {
602 LOG_DEBUG(Service_LDR, "(called)");
603
604 struct InputParameters {
605 u64 client_pid;
606 u64 nro_address;
607 };
608
609 IPC::RequestParser rp{ctx};
610 auto params = rp.PopRaw<InputParameters>();
611 auto result = interface.UnmapManualLoadModuleMemory(ctx.GetPID(), params.nro_address);
612
613 IPC::ResponseBuilder rb{ctx, 2};
614 rb.Push(result);
615 }
616
617 void RegisterModuleInfo(HLERequestContext& ctx) {
618 LOG_DEBUG(Service_LDR, "(called)");
619
620 struct InputParameters {
621 u64 client_pid;
622 u64 nrr_address;
623 u64 nrr_size;
624 };
625
626 IPC::RequestParser rp{ctx};
627 auto params = rp.PopRaw<InputParameters>();
628 auto result =
629 interface.RegisterModuleInfo(ctx.GetPID(), params.nrr_address, params.nrr_size);
630
631 IPC::ResponseBuilder rb{ctx, 2};
632 rb.Push(result);
633 }
634
635 void UnregisterModuleInfo(HLERequestContext& ctx) {
636 LOG_DEBUG(Service_LDR, "(called)");
637
638 struct InputParameters {
639 u64 client_pid;
640 u64 nrr_address;
641 };
642
643 IPC::RequestParser rp{ctx};
644 auto params = rp.PopRaw<InputParameters>();
645 auto result = interface.UnregisterModuleInfo(ctx.GetPID(), params.nrr_address);
646
647 IPC::ResponseBuilder rb{ctx, 2};
648 rb.Push(result);
649 }
650
651 void RegisterProcessHandle(HLERequestContext& ctx) {
652 LOG_DEBUG(Service_LDR, "(called)");
653
654 auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
655 auto client_pid = ctx.GetPID();
656 auto result = interface.RegisterProcessHandle(client_pid,
657 process_h->DynamicCast<Kernel::KProcess*>());
658
659 IPC::ResponseBuilder rb{ctx, 2};
660 rb.Push(result);
661 }
662
663 void RegisterProcessModuleInfo(HLERequestContext& ctx) {
664 LOG_DEBUG(Service_LDR, "(called)");
665
666 struct InputParameters {
667 u64 client_pid;
668 u64 nrr_address;
669 u64 nrr_size;
670 };
671
672 IPC::RequestParser rp{ctx};
673 auto params = rp.PopRaw<InputParameters>();
674 auto process_h = ctx.GetClientHandleTable().GetObject(ctx.GetCopyHandle(0));
675
676 auto client_pid = ctx.GetPID();
677 auto result =
678 interface.RegisterProcessModuleInfo(client_pid, params.nrr_address, params.nrr_size,
679 process_h->DynamicCast<Kernel::KProcess*>());
680
681 IPC::ResponseBuilder rb{ctx, 2};
682 rb.Push(result);
683 }
684
685 RoInterface interface;
686};
687
688} // namespace
689
690void LoopProcess(Core::System& system) {
691 auto server_manager = std::make_unique<ServerManager>(system);
692
693 auto ro = std::make_shared<RoContext>();
694
695 const auto RoInterfaceFactoryForUser = [&, ro] {
696 return std::make_shared<IRoInterface>(system, "ldr:ro", ro, NrrKind::User);
697 };
698
699 const auto RoInterfaceFactoryForJitPlugin = [&, ro] {
700 return std::make_shared<IRoInterface>(system, "ro:1", ro, NrrKind::JitPlugin);
701 };
702
703 server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));
704 server_manager->RegisterNamedService("ro:1", std::move(RoInterfaceFactoryForJitPlugin));
705
706 ServerManager::RunServer(std::move(server_manager));
707}
708
709} // namespace Service::RO
diff --git a/src/core/hle/service/ro/ro.h b/src/core/hle/service/ro/ro.h
new file mode 100644
index 000000000..74dc08536
--- /dev/null
+++ b/src/core/hle/service/ro/ro.h
@@ -0,0 +1,14 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace Core {
7class System;
8}
9
10namespace Service::RO {
11
12void LoopProcess(Core::System& system);
13
14} // namespace Service::RO
diff --git a/src/core/hle/service/ro/ro_nro_utils.cpp b/src/core/hle/service/ro/ro_nro_utils.cpp
new file mode 100644
index 000000000..268c7f93e
--- /dev/null
+++ b/src/core/hle/service/ro/ro_nro_utils.cpp
@@ -0,0 +1,185 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_process.h"
5#include "core/hle/service/ro/ro_nro_utils.h"
6#include "core/hle/service/ro/ro_results.h"
7
8namespace Service::RO {
9
10namespace {
11
12struct ProcessMemoryRegion {
13 u64 address;
14 u64 size;
15};
16
17size_t GetTotalProcessMemoryRegionSize(const ProcessMemoryRegion* regions, size_t num_regions) {
18 size_t total = 0;
19
20 for (size_t i = 0; i < num_regions; ++i) {
21 total += regions[i].size;
22 }
23
24 return total;
25}
26
27size_t SetupNroProcessMemoryRegions(ProcessMemoryRegion* regions, u64 nro_heap_address,
28 u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
29 // Reset region count.
30 size_t num_regions = 0;
31
32 // We always want a region for the nro.
33 regions[num_regions++] = {nro_heap_address, nro_heap_size};
34
35 // If we have bss, create a region for bss.
36 if (bss_heap_size > 0) {
37 regions[num_regions++] = {bss_heap_address, bss_heap_size};
38 }
39
40 return num_regions;
41}
42
43Result SetProcessMemoryPermission(Kernel::KProcess* process, u64 address, u64 size,
44 Kernel::Svc::MemoryPermission permission) {
45 auto& page_table = process->GetPageTable();
46
47 // Set permission.
48 R_RETURN(page_table.SetProcessMemoryPermission(address, size, permission));
49}
50
51Result UnmapProcessCodeMemory(Kernel::KProcess* process, u64 process_code_address,
52 const ProcessMemoryRegion* regions, size_t num_regions) {
53 // Get the total process memory region size.
54 const size_t total_size = GetTotalProcessMemoryRegionSize(regions, num_regions);
55
56 auto& page_table = process->GetPageTable();
57
58 // Unmap each region in order.
59 size_t cur_offset = total_size;
60 for (size_t i = 0; i < num_regions; ++i) {
61 // We want to unmap in reverse order.
62 const auto& cur_region = regions[num_regions - 1 - i];
63
64 // Subtract to update the current offset.
65 cur_offset -= cur_region.size;
66
67 // Unmap.
68 R_TRY(page_table.UnmapCodeMemory(process_code_address + cur_offset, cur_region.address,
69 cur_region.size));
70 }
71
72 R_SUCCEED();
73}
74
75Result EnsureGuardPages(Kernel::KProcessPageTable& page_table, u64 map_address, u64 map_size) {
76 Kernel::KMemoryInfo memory_info;
77 Kernel::Svc::PageInfo page_info;
78
79 // Ensure page before mapping is unmapped.
80 R_TRY(page_table.QueryInfo(std::addressof(memory_info), std::addressof(page_info),
81 map_address - 1));
82 R_UNLESS(memory_info.GetSvcState() == Kernel::Svc::MemoryState::Free,
83 Kernel::ResultInvalidState);
84
85 // Ensure page after mapping is unmapped.
86 R_TRY(page_table.QueryInfo(std::addressof(memory_info), std::addressof(page_info),
87 map_address + map_size));
88 R_UNLESS(memory_info.GetSvcState() == Kernel::Svc::MemoryState::Free,
89 Kernel::ResultInvalidState);
90
91 // Successfully verified guard pages.
92 R_SUCCEED();
93}
94
95Result MapProcessCodeMemory(u64* out, Kernel::KProcess* process, const ProcessMemoryRegion* regions,
96 size_t num_regions, std::mt19937_64& generate_random) {
97 auto& page_table = process->GetPageTable();
98 const u64 alias_code_start =
99 GetInteger(page_table.GetAliasCodeRegionStart()) / Kernel::PageSize;
100 const u64 alias_code_size = page_table.GetAliasCodeRegionSize() / Kernel::PageSize;
101
102 for (size_t trial = 0; trial < 64; trial++) {
103 // Generate a new trial address.
104 const u64 mapped_address =
105 (alias_code_start + (generate_random() % alias_code_size)) * Kernel::PageSize;
106
107 const auto MapRegions = [&] {
108 // Map the regions in order.
109 u64 mapped_size = 0;
110 for (size_t i = 0; i < num_regions; ++i) {
111 // If we fail, unmap up to where we've mapped.
112 ON_RESULT_FAILURE {
113 R_ASSERT(UnmapProcessCodeMemory(process, mapped_address, regions, i));
114 };
115
116 // Map the current region.
117 R_TRY(page_table.MapCodeMemory(mapped_address + mapped_size, regions[i].address,
118 regions[i].size));
119
120 mapped_size += regions[i].size;
121 }
122
123 // If we fail, unmap all mapped regions.
124 ON_RESULT_FAILURE {
125 R_ASSERT(UnmapProcessCodeMemory(process, mapped_address, regions, num_regions));
126 };
127
128 // Ensure guard pages.
129 R_RETURN(EnsureGuardPages(page_table, mapped_address, mapped_size));
130 };
131
132 if (R_SUCCEEDED(MapRegions())) {
133 // Set the output address.
134 *out = mapped_address;
135 R_SUCCEED();
136 }
137 }
138
139 // We failed to map anything.
140 R_THROW(RO::ResultOutOfAddressSpace);
141}
142
143} // namespace
144
145Result MapNro(u64* out_base_address, Kernel::KProcess* process, u64 nro_heap_address,
146 u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size,
147 std::mt19937_64& generate_random) {
148 // Set up the process memory regions.
149 std::array<ProcessMemoryRegion, 2> regions{};
150 const size_t num_regions = SetupNroProcessMemoryRegions(
151 regions.data(), nro_heap_address, nro_heap_size, bss_heap_address, bss_heap_size);
152
153 // Re-map the nro/bss as code memory in the destination process.
154 R_RETURN(MapProcessCodeMemory(out_base_address, process, regions.data(), num_regions,
155 generate_random));
156}
157
158Result SetNroPerms(Kernel::KProcess* process, u64 base_address, u64 rx_size, u64 ro_size,
159 u64 rw_size) {
160 const u64 rx_offset = 0;
161 const u64 ro_offset = rx_offset + rx_size;
162 const u64 rw_offset = ro_offset + ro_size;
163
164 R_TRY(SetProcessMemoryPermission(process, base_address + rx_offset, rx_size,
165 Kernel::Svc::MemoryPermission::ReadExecute));
166 R_TRY(SetProcessMemoryPermission(process, base_address + ro_offset, ro_size,
167 Kernel::Svc::MemoryPermission::Read));
168 R_TRY(SetProcessMemoryPermission(process, base_address + rw_offset, rw_size,
169 Kernel::Svc::MemoryPermission::ReadWrite));
170
171 R_SUCCEED();
172}
173
174Result UnmapNro(Kernel::KProcess* process, u64 base_address, u64 nro_heap_address,
175 u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
176 // Set up the process memory regions.
177 std::array<ProcessMemoryRegion, 2> regions{};
178 const size_t num_regions = SetupNroProcessMemoryRegions(
179 regions.data(), nro_heap_address, nro_heap_size, bss_heap_address, bss_heap_size);
180
181 // Unmap the nro/bss.
182 R_RETURN(UnmapProcessCodeMemory(process, base_address, regions.data(), num_regions));
183}
184
185} // namespace Service::RO
diff --git a/src/core/hle/service/ro/ro_nro_utils.h b/src/core/hle/service/ro/ro_nro_utils.h
new file mode 100644
index 000000000..f7083a1ba
--- /dev/null
+++ b/src/core/hle/service/ro/ro_nro_utils.h
@@ -0,0 +1,26 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <random>
7
8#include "common/common_types.h"
9
10namespace Kernel {
11class KProcess;
12}
13
14union Result;
15
16namespace Service::RO {
17
18Result MapNro(u64* out_base_address, Kernel::KProcess* process, u64 nro_heap_address,
19 u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size,
20 std::mt19937_64& generate_random);
21Result SetNroPerms(Kernel::KProcess* process, u64 base_address, u64 rx_size, u64 ro_size,
22 u64 rw_size);
23Result UnmapNro(Kernel::KProcess* process, u64 base_address, u64 nro_heap_address,
24 u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
25
26} // namespace Service::RO
diff --git a/src/core/hle/service/ro/ro_results.h b/src/core/hle/service/ro/ro_results.h
new file mode 100644
index 000000000..00f05c5a5
--- /dev/null
+++ b/src/core/hle/service/ro/ro_results.h
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/result.h"
5
6namespace Service::RO {
7
8constexpr Result ResultOutOfAddressSpace{ErrorModule::RO, 2};
9constexpr Result ResultAlreadyLoaded{ErrorModule::RO, 3};
10constexpr Result ResultInvalidNro{ErrorModule::RO, 4};
11constexpr Result ResultInvalidNrr{ErrorModule::RO, 6};
12constexpr Result ResultTooManyNro{ErrorModule::RO, 7};
13constexpr Result ResultTooManyNrr{ErrorModule::RO, 8};
14constexpr Result ResultNotAuthorized{ErrorModule::RO, 9};
15constexpr Result ResultInvalidNrrKind{ErrorModule::RO, 10};
16constexpr Result ResultInternalError{ErrorModule::RO, 1023};
17constexpr Result ResultInvalidAddress{ErrorModule::RO, 1025};
18constexpr Result ResultInvalidSize{ErrorModule::RO, 1026};
19constexpr Result ResultNotLoaded{ErrorModule::RO, 1028};
20constexpr Result ResultNotRegistered{ErrorModule::RO, 1029};
21constexpr Result ResultInvalidSession{ErrorModule::RO, 1030};
22constexpr Result ResultInvalidProcess{ErrorModule::RO, 1031};
23
24} // namespace Service::RO
diff --git a/src/core/hle/service/ro/ro_types.h b/src/core/hle/service/ro/ro_types.h
new file mode 100644
index 000000000..624d52ee5
--- /dev/null
+++ b/src/core/hle/service/ro/ro_types.h
@@ -0,0 +1,181 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "common/common_funcs.h"
6#include "common/common_types.h"
7
8namespace Service::RO {
9
10enum class NrrKind : u8 {
11 User = 0,
12 JitPlugin = 1,
13 Count,
14};
15
16static constexpr size_t ModuleIdSize = 0x20;
17struct ModuleId {
18 std::array<u8, ModuleIdSize> data;
19};
20static_assert(sizeof(ModuleId) == ModuleIdSize);
21
22struct NrrCertification {
23 static constexpr size_t RsaKeySize = 0x100;
24 static constexpr size_t SignedSize = 0x120;
25
26 u64 program_id_mask;
27 u64 program_id_pattern;
28 std::array<u8, 0x10> reserved_10;
29 std::array<u8, RsaKeySize> modulus;
30 std::array<u8, RsaKeySize> signature;
31};
32static_assert(sizeof(NrrCertification) ==
33 NrrCertification::RsaKeySize + NrrCertification::SignedSize);
34
35class NrrHeader {
36public:
37 static constexpr u32 Magic = Common::MakeMagic('N', 'R', 'R', '0');
38
39public:
40 bool IsMagicValid() const {
41 return m_magic == Magic;
42 }
43
44 bool IsProgramIdValid() const {
45 return (m_program_id & m_certification.program_id_mask) ==
46 m_certification.program_id_pattern;
47 }
48
49 NrrKind GetNrrKind() const {
50 const NrrKind kind = static_cast<NrrKind>(m_nrr_kind);
51 ASSERT(kind < NrrKind::Count);
52 return kind;
53 }
54
55 u64 GetProgramId() const {
56 return m_program_id;
57 }
58
59 u32 GetSize() const {
60 return m_size;
61 }
62
63 u32 GetNumHashes() const {
64 return m_num_hashes;
65 }
66
67 size_t GetHashesOffset() const {
68 return m_hashes_offset;
69 }
70
71 u32 GetKeyGeneration() const {
72 return m_key_generation;
73 }
74
75 const u8* GetCertificationSignature() const {
76 return m_certification.signature.data();
77 }
78
79 const u8* GetCertificationSignedArea() const {
80 return reinterpret_cast<const u8*>(std::addressof(m_certification));
81 }
82
83 const u8* GetCertificationModulus() const {
84 return m_certification.modulus.data();
85 }
86
87 const u8* GetSignature() const {
88 return m_signature.data();
89 }
90
91 size_t GetSignedAreaSize() const {
92 return m_size - GetSignedAreaOffset();
93 }
94
95 static constexpr size_t GetSignedAreaOffset() {
96 return offsetof(NrrHeader, m_program_id);
97 }
98
99private:
100 u32 m_magic;
101 u32 m_key_generation;
102 INSERT_PADDING_BYTES_NOINIT(8);
103 NrrCertification m_certification;
104 std::array<u8, 0x100> m_signature;
105 u64 m_program_id;
106 u32 m_size;
107 u8 m_nrr_kind; // 7.0.0+
108 INSERT_PADDING_BYTES_NOINIT(3);
109 u32 m_hashes_offset;
110 u32 m_num_hashes;
111 INSERT_PADDING_BYTES_NOINIT(8);
112};
113static_assert(sizeof(NrrHeader) == 0x350, "NrrHeader has wrong size");
114
115class NroHeader {
116public:
117 static constexpr u32 Magic = Common::MakeMagic('N', 'R', 'O', '0');
118
119public:
120 bool IsMagicValid() const {
121 return m_magic == Magic;
122 }
123
124 u32 GetSize() const {
125 return m_size;
126 }
127
128 u32 GetTextOffset() const {
129 return m_text_offset;
130 }
131
132 u32 GetTextSize() const {
133 return m_text_size;
134 }
135
136 u32 GetRoOffset() const {
137 return m_ro_offset;
138 }
139
140 u32 GetRoSize() const {
141 return m_ro_size;
142 }
143
144 u32 GetRwOffset() const {
145 return m_rw_offset;
146 }
147
148 u32 GetRwSize() const {
149 return m_rw_size;
150 }
151
152 u32 GetBssSize() const {
153 return m_bss_size;
154 }
155
156 const ModuleId* GetModuleId() const {
157 return std::addressof(m_module_id);
158 }
159
160private:
161 u32 m_entrypoint_insn;
162 u32 m_mod_offset;
163 INSERT_PADDING_BYTES_NOINIT(0x8);
164 u32 m_magic;
165 INSERT_PADDING_BYTES_NOINIT(0x4);
166 u32 m_size;
167 INSERT_PADDING_BYTES_NOINIT(0x4);
168 u32 m_text_offset;
169 u32 m_text_size;
170 u32 m_ro_offset;
171 u32 m_ro_size;
172 u32 m_rw_offset;
173 u32 m_rw_size;
174 u32 m_bss_size;
175 INSERT_PADDING_BYTES_NOINIT(0x4);
176 ModuleId m_module_id;
177 INSERT_PADDING_BYTES_NOINIT(0x20);
178};
179static_assert(sizeof(NroHeader) == 0x80, "NroHeader has wrong size");
180
181} // namespace Service::RO
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 0ad607391..00531b021 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -59,6 +59,7 @@
59#include "core/hle/service/prepo/prepo.h" 59#include "core/hle/service/prepo/prepo.h"
60#include "core/hle/service/psc/psc.h" 60#include "core/hle/service/psc/psc.h"
61#include "core/hle/service/ptm/ptm.h" 61#include "core/hle/service/ptm/ptm.h"
62#include "core/hle/service/ro/ro.h"
62#include "core/hle/service/service.h" 63#include "core/hle/service/service.h"
63#include "core/hle/service/set/settings.h" 64#include "core/hle/service/set/settings.h"
64#include "core/hle/service/sm/sm.h" 65#include "core/hle/service/sm/sm.h"
@@ -270,6 +271,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
270 kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); }); 271 kernel.RunOnGuestCoreProcess("ProcessManager", [&] { PM::LoopProcess(system); });
271 kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); }); 272 kernel.RunOnGuestCoreProcess("psc", [&] { PSC::LoopProcess(system); });
272 kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); }); 273 kernel.RunOnGuestCoreProcess("ptm", [&] { PTM::LoopProcess(system); });
274 kernel.RunOnGuestCoreProcess("ro", [&] { RO::LoopProcess(system); });
273 kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); }); 275 kernel.RunOnGuestCoreProcess("settings", [&] { Set::LoopProcess(system); });
274 kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); }); 276 kernel.RunOnGuestCoreProcess("spl", [&] { SPL::LoopProcess(system); });
275 kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); }); 277 kernel.RunOnGuestCoreProcess("ssl", [&] { SSL::LoopProcess(system); });