summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/settings.cpp8
-rw-r--r--src/core/CMakeLists.txt12
-rw-r--r--src/core/hle/kernel/k_client_port.cpp69
-rw-r--r--src/core/hle/kernel/k_client_port.h2
-rw-r--r--src/core/hle/kernel/k_client_session.cpp24
-rw-r--r--src/core/hle/kernel/k_client_session.h18
-rw-r--r--src/core/hle/kernel/k_light_client_session.cpp31
-rw-r--r--src/core/hle/kernel/k_light_client_session.h39
-rw-r--r--src/core/hle/kernel/k_light_server_session.cpp247
-rw-r--r--src/core/hle/kernel/k_light_server_session.h49
-rw-r--r--src/core/hle/kernel/k_light_session.cpp81
-rw-r--r--src/core/hle/kernel/k_light_session.h86
-rw-r--r--src/core/hle/kernel/k_port.cpp9
-rw-r--r--src/core/hle/kernel/k_port.h2
-rw-r--r--src/core/hle/kernel/k_server_port.cpp56
-rw-r--r--src/core/hle/kernel/k_server_port.h5
-rw-r--r--src/core/hle/kernel/k_server_session.cpp8
-rw-r--r--src/core/hle/kernel/k_session.h4
-rw-r--r--src/core/hle/kernel/k_thread.h7
-rw-r--r--src/core/hle/kernel/kernel.cpp4
-rw-r--r--src/core/hle/kernel/physical_core.cpp2
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp296
-rw-r--r--src/core/hle/kernel/svc/svc_light_ipc.cpp29
-rw-r--r--src/core/hle/kernel/svc/svc_port.cpp71
-rw-r--r--src/core/hle/kernel/svc/svc_session.cpp40
-rw-r--r--src/core/hle/service/hle_ipc.cpp15
-rw-r--r--src/core/hle/service/hle_ipc.h15
-rw-r--r--src/core/hle/service/ldr/ldr.cpp634
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp1
-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/server_manager.cpp32
-rw-r--r--src/core/hle/service/server_manager.h14
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/sm/sm.cpp33
-rw-r--r--src/core/hle/service/sm/sm.h14
40 files changed, 2293 insertions, 805 deletions
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 4666bd0a0..88f509ba7 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -160,12 +160,16 @@ static bool is_nce_enabled = false;
160 160
161void SetNceEnabled(bool is_39bit) { 161void SetNceEnabled(bool is_39bit) {
162 const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce; 162 const bool is_nce_selected = values.cpu_backend.GetValue() == CpuBackend::Nce;
163 is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit; 163 if (is_nce_selected && !IsFastmemEnabled()) {
164 if (is_nce_selected && !is_nce_enabled) { 164 LOG_WARNING(Common, "Fastmem is required to natively execute code in a performant manner, "
165 "falling back to Dynarmic");
166 }
167 if (is_nce_selected && !is_39bit) {
165 LOG_WARNING( 168 LOG_WARNING(
166 Common, 169 Common,
167 "Program does not utilize 39-bit address space, unable to natively execute code"); 170 "Program does not utilize 39-bit address space, unable to natively execute code");
168 } 171 }
172 is_nce_enabled = IsFastmemEnabled() && is_nce_selected && is_39bit;
169} 173}
170 174
171bool IsNceEnabled() { 175bool IsNceEnabled() {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b483fd975..7b9ed856f 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -251,10 +251,16 @@ add_library(core STATIC
251 hle/kernel/k_hardware_timer.h 251 hle/kernel/k_hardware_timer.h
252 hle/kernel/k_interrupt_manager.cpp 252 hle/kernel/k_interrupt_manager.cpp
253 hle/kernel/k_interrupt_manager.h 253 hle/kernel/k_interrupt_manager.h
254 hle/kernel/k_light_client_session.cpp
255 hle/kernel/k_light_client_session.h
254 hle/kernel/k_light_condition_variable.cpp 256 hle/kernel/k_light_condition_variable.cpp
255 hle/kernel/k_light_condition_variable.h 257 hle/kernel/k_light_condition_variable.h
256 hle/kernel/k_light_lock.cpp 258 hle/kernel/k_light_lock.cpp
257 hle/kernel/k_light_lock.h 259 hle/kernel/k_light_lock.h
260 hle/kernel/k_light_server_session.cpp
261 hle/kernel/k_light_server_session.h
262 hle/kernel/k_light_session.cpp
263 hle/kernel/k_light_session.h
258 hle/kernel/k_memory_block.h 264 hle/kernel/k_memory_block.h
259 hle/kernel/k_memory_block_manager.cpp 265 hle/kernel/k_memory_block_manager.cpp
260 hle/kernel/k_memory_block_manager.h 266 hle/kernel/k_memory_block_manager.h
@@ -766,6 +772,12 @@ add_library(core STATIC
766 hle/service/kernel_helpers.h 772 hle/service/kernel_helpers.h
767 hle/service/mutex.cpp 773 hle/service/mutex.cpp
768 hle/service/mutex.h 774 hle/service/mutex.h
775 hle/service/ro/ro_nro_utils.cpp
776 hle/service/ro/ro_nro_utils.h
777 hle/service/ro/ro_results.h
778 hle/service/ro/ro_types.h
779 hle/service/ro/ro.cpp
780 hle/service/ro/ro.h
769 hle/service/server_manager.cpp 781 hle/service/server_manager.cpp
770 hle/service/server_manager.h 782 hle/service/server_manager.h
771 hle/service/service.cpp 783 hle/service/service.cpp
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index 40e09e532..11b1b977e 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -3,6 +3,7 @@
3 3
4#include "common/scope_exit.h" 4#include "common/scope_exit.h"
5#include "core/hle/kernel/k_client_port.h" 5#include "core/hle/kernel/k_client_port.h"
6#include "core/hle/kernel/k_light_session.h"
6#include "core/hle/kernel/k_port.h" 7#include "core/hle/kernel/k_port.h"
7#include "core/hle/kernel/k_scheduler.h" 8#include "core/hle/kernel/k_scheduler.h"
8#include "core/hle/kernel/k_scoped_resource_reservation.h" 9#include "core/hle/kernel/k_scoped_resource_reservation.h"
@@ -63,6 +64,7 @@ Result KClientPort::CreateSession(KClientSession** out) {
63 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); 64 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
64 65
65 // Allocate a session normally. 66 // Allocate a session normally.
67 // TODO: Dynamic resource limits
66 session = KSession::Create(m_kernel); 68 session = KSession::Create(m_kernel);
67 69
68 // Check that we successfully created a session. 70 // Check that we successfully created a session.
@@ -119,4 +121,71 @@ Result KClientPort::CreateSession(KClientSession** out) {
119 R_SUCCEED(); 121 R_SUCCEED();
120} 122}
121 123
124Result KClientPort::CreateLightSession(KLightClientSession** out) {
125 // Declare the session we're going to allocate.
126 KLightSession* session{};
127
128 // Reserve a new session from the resource limit.
129 KScopedResourceReservation session_reservation(GetCurrentProcessPointer(m_kernel),
130 Svc::LimitableResource::SessionCountMax);
131 R_UNLESS(session_reservation.Succeeded(), ResultLimitReached);
132
133 // Allocate a session normally.
134 // TODO: Dynamic resource limits
135 session = KLightSession::Create(m_kernel);
136
137 // Check that we successfully created a session.
138 R_UNLESS(session != nullptr, ResultOutOfResource);
139
140 // Update the session counts.
141 {
142 ON_RESULT_FAILURE {
143 session->Close();
144 };
145
146 // Atomically increment the number of sessions.
147 s32 new_sessions;
148 {
149 const auto max = m_max_sessions;
150 auto cur_sessions = m_num_sessions.load(std::memory_order_acquire);
151 do {
152 R_UNLESS(cur_sessions < max, ResultOutOfSessions);
153 new_sessions = cur_sessions + 1;
154 } while (!m_num_sessions.compare_exchange_weak(cur_sessions, new_sessions,
155 std::memory_order_relaxed));
156 }
157
158 // Atomically update the peak session tracking.
159 {
160 auto peak = m_peak_sessions.load(std::memory_order_acquire);
161 do {
162 if (peak >= new_sessions) {
163 break;
164 }
165 } while (!m_peak_sessions.compare_exchange_weak(peak, new_sessions,
166 std::memory_order_relaxed));
167 }
168 }
169
170 // Initialize the session.
171 session->Initialize(this, m_parent->GetName());
172
173 // Commit the session reservation.
174 session_reservation.Commit();
175
176 // Register the session.
177 KLightSession::Register(m_kernel, session);
178 ON_RESULT_FAILURE {
179 session->GetClientSession().Close();
180 session->GetServerSession().Close();
181 };
182
183 // Enqueue the session with our parent.
184 R_TRY(m_parent->EnqueueSession(std::addressof(session->GetServerSession())));
185
186 // We succeeded, so set the output.
187 *out = std::addressof(session->GetClientSession());
188 R_SUCCEED();
189}
190
122} // namespace Kernel 191} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index 23db06ddf..28b332608 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -11,6 +11,7 @@
11 11
12namespace Kernel { 12namespace Kernel {
13 13
14class KLightClientSession;
14class KClientSession; 15class KClientSession;
15class KernelCore; 16class KernelCore;
16class KPort; 17class KPort;
@@ -51,6 +52,7 @@ public:
51 bool IsSignaled() const override; 52 bool IsSignaled() const override;
52 53
53 Result CreateSession(KClientSession** out); 54 Result CreateSession(KClientSession** out);
55 Result CreateLightSession(KLightClientSession** out);
54 56
55private: 57private:
56 std::atomic<s32> m_num_sessions{}; 58 std::atomic<s32> m_num_sessions{};
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index 72b66270d..472e8571c 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -10,9 +10,7 @@
10 10
11namespace Kernel { 11namespace Kernel {
12 12
13static constexpr u32 MessageBufferSize = 0x100; 13KClientSession::KClientSession(KernelCore& kernel) : KAutoObject{kernel} {}
14
15KClientSession::KClientSession(KernelCore& kernel) : KAutoObjectWithSlabHeapAndContainer{kernel} {}
16KClientSession::~KClientSession() = default; 14KClientSession::~KClientSession() = default;
17 15
18void KClientSession::Destroy() { 16void KClientSession::Destroy() {
@@ -22,18 +20,30 @@ void KClientSession::Destroy() {
22 20
23void KClientSession::OnServerClosed() {} 21void KClientSession::OnServerClosed() {}
24 22
25Result KClientSession::SendSyncRequest() { 23Result KClientSession::SendSyncRequest(uintptr_t address, size_t size) {
24 // Create a session request.
25 KSessionRequest* request = KSessionRequest::Create(m_kernel);
26 R_UNLESS(request != nullptr, ResultOutOfResource);
27 SCOPE_EXIT({ request->Close(); });
28
29 // Initialize the request.
30 request->Initialize(nullptr, address, size);
31
32 // Send the request.
33 R_RETURN(m_parent->OnRequest(request));
34}
35
36Result KClientSession::SendAsyncRequest(KEvent* event, uintptr_t address, size_t size) {
26 // Create a session request. 37 // Create a session request.
27 KSessionRequest* request = KSessionRequest::Create(m_kernel); 38 KSessionRequest* request = KSessionRequest::Create(m_kernel);
28 R_UNLESS(request != nullptr, ResultOutOfResource); 39 R_UNLESS(request != nullptr, ResultOutOfResource);
29 SCOPE_EXIT({ request->Close(); }); 40 SCOPE_EXIT({ request->Close(); });
30 41
31 // Initialize the request. 42 // Initialize the request.
32 request->Initialize(nullptr, GetInteger(GetCurrentThread(m_kernel).GetTlsAddress()), 43 request->Initialize(event, address, size);
33 MessageBufferSize);
34 44
35 // Send the request. 45 // Send the request.
36 R_RETURN(m_parent->GetServerSession().OnRequest(request)); 46 R_RETURN(m_parent->OnRequest(request));
37} 47}
38 48
39} // namespace Kernel 49} // namespace Kernel
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
index 9b62e55e4..a39213e17 100644
--- a/src/core/hle/kernel/k_client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -9,24 +9,12 @@
9#include "core/hle/kernel/slab_helpers.h" 9#include "core/hle/kernel/slab_helpers.h"
10#include "core/hle/result.h" 10#include "core/hle/result.h"
11 11
12union Result;
13
14namespace Core::Memory {
15class Memory;
16}
17
18namespace Core::Timing {
19class CoreTiming;
20}
21
22namespace Kernel { 12namespace Kernel {
23 13
24class KernelCore; 14class KernelCore;
25class KSession; 15class KSession;
26class KThread;
27 16
28class KClientSession final 17class KClientSession final : public KAutoObject {
29 : public KAutoObjectWithSlabHeapAndContainer<KClientSession, KAutoObjectWithList> {
30 KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject); 18 KERNEL_AUTOOBJECT_TRAITS(KClientSession, KAutoObject);
31 19
32public: 20public:
@@ -39,13 +27,13 @@ public:
39 } 27 }
40 28
41 void Destroy() override; 29 void Destroy() override;
42 static void PostDestroy(uintptr_t arg) {}
43 30
44 KSession* GetParent() const { 31 KSession* GetParent() const {
45 return m_parent; 32 return m_parent;
46 } 33 }
47 34
48 Result SendSyncRequest(); 35 Result SendSyncRequest(uintptr_t address, size_t size);
36 Result SendAsyncRequest(KEvent* event, uintptr_t address, size_t size);
49 37
50 void OnServerClosed(); 38 void OnServerClosed();
51 39
diff --git a/src/core/hle/kernel/k_light_client_session.cpp b/src/core/hle/kernel/k_light_client_session.cpp
new file mode 100644
index 000000000..8ce3e1ae4
--- /dev/null
+++ b/src/core/hle/kernel/k_light_client_session.cpp
@@ -0,0 +1,31 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_light_client_session.h"
5#include "core/hle/kernel/k_light_session.h"
6#include "core/hle/kernel/k_thread.h"
7
8namespace Kernel {
9
10KLightClientSession::KLightClientSession(KernelCore& kernel) : KAutoObject(kernel) {}
11
12KLightClientSession::~KLightClientSession() = default;
13
14void KLightClientSession::Destroy() {
15 m_parent->OnClientClosed();
16}
17
18void KLightClientSession::OnServerClosed() {}
19
20Result KLightClientSession::SendSyncRequest(u32* data) {
21 // Get the request thread.
22 KThread* cur_thread = GetCurrentThreadPointer(m_kernel);
23
24 // Set the light data.
25 cur_thread->SetLightSessionData(data);
26
27 // Send the request.
28 R_RETURN(m_parent->OnRequest(cur_thread));
29}
30
31} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_client_session.h b/src/core/hle/kernel/k_light_client_session.h
new file mode 100644
index 000000000..881a15cbd
--- /dev/null
+++ b/src/core/hle/kernel/k_light_client_session.h
@@ -0,0 +1,39 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_auto_object.h"
7#include "core/hle/result.h"
8
9namespace Kernel {
10
11class KLightSession;
12
13class KLightClientSession final : public KAutoObject {
14 KERNEL_AUTOOBJECT_TRAITS(KLightClientSession, KAutoObject);
15
16public:
17 explicit KLightClientSession(KernelCore& kernel);
18 ~KLightClientSession();
19
20 void Initialize(KLightSession* parent) {
21 // Set member variables.
22 m_parent = parent;
23 }
24
25 virtual void Destroy() override;
26
27 const KLightSession* GetParent() const {
28 return m_parent;
29 }
30
31 Result SendSyncRequest(u32* data);
32
33 void OnServerClosed();
34
35private:
36 KLightSession* m_parent;
37};
38
39} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_server_session.cpp b/src/core/hle/kernel/k_light_server_session.cpp
new file mode 100644
index 000000000..e5ceb01f2
--- /dev/null
+++ b/src/core/hle/kernel/k_light_server_session.cpp
@@ -0,0 +1,247 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_light_server_session.h"
5#include "core/hle/kernel/k_light_session.h"
6#include "core/hle/kernel/k_thread.h"
7#include "core/hle/kernel/k_thread_queue.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel {
11
12namespace {
13
14constexpr u64 InvalidThreadId = std::numeric_limits<u64>::max();
15
16class ThreadQueueImplForKLightServerSessionRequest final : public KThreadQueue {
17private:
18 KThread::WaiterList* m_wait_list;
19
20public:
21 ThreadQueueImplForKLightServerSessionRequest(KernelCore& kernel, KThread::WaiterList* wl)
22 : KThreadQueue(kernel), m_wait_list(wl) {}
23
24 virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
25 // Remove the thread from our wait list.
26 m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
27
28 // Invoke the base end wait handler.
29 KThreadQueue::EndWait(waiting_thread, wait_result);
30 }
31
32 virtual void CancelWait(KThread* waiting_thread, Result wait_result,
33 bool cancel_timer_task) override {
34 // Remove the thread from our wait list.
35 m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
36
37 // Invoke the base cancel wait handler.
38 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
39 }
40};
41
42class ThreadQueueImplForKLightServerSessionReceive final : public KThreadQueue {
43private:
44 KThread** m_server_thread;
45
46public:
47 ThreadQueueImplForKLightServerSessionReceive(KernelCore& kernel, KThread** st)
48 : KThreadQueue(kernel), m_server_thread(st) {}
49
50 virtual void EndWait(KThread* waiting_thread, Result wait_result) override {
51 // Clear the server thread.
52 *m_server_thread = nullptr;
53
54 // Set the waiting thread as not cancelable.
55 waiting_thread->ClearCancellable();
56
57 // Invoke the base end wait handler.
58 KThreadQueue::EndWait(waiting_thread, wait_result);
59 }
60
61 virtual void CancelWait(KThread* waiting_thread, Result wait_result,
62 bool cancel_timer_task) override {
63 // Clear the server thread.
64 *m_server_thread = nullptr;
65
66 // Set the waiting thread as not cancelable.
67 waiting_thread->ClearCancellable();
68
69 // Invoke the base cancel wait handler.
70 KThreadQueue::CancelWait(waiting_thread, wait_result, cancel_timer_task);
71 }
72};
73
74} // namespace
75
76KLightServerSession::KLightServerSession(KernelCore& kernel) : KAutoObject(kernel) {}
77KLightServerSession::~KLightServerSession() = default;
78
79void KLightServerSession::Destroy() {
80 this->CleanupRequests();
81
82 m_parent->OnServerClosed();
83}
84
85void KLightServerSession::OnClientClosed() {
86 this->CleanupRequests();
87}
88
89Result KLightServerSession::OnRequest(KThread* request_thread) {
90 ThreadQueueImplForKLightServerSessionRequest wait_queue(m_kernel,
91 std::addressof(m_request_list));
92
93 // Send the request.
94 {
95 // Lock the scheduler.
96 KScopedSchedulerLock sl(m_kernel);
97
98 // Check that the server isn't closed.
99 R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
100
101 // Check that the request thread isn't terminating.
102 R_UNLESS(!request_thread->IsTerminationRequested(), ResultTerminationRequested);
103
104 // Add the request thread to our list.
105 m_request_list.push_back(*request_thread);
106
107 // Begin waiting on the request.
108 request_thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
109 request_thread->BeginWait(std::addressof(wait_queue));
110
111 // If we have a server thread, end its wait.
112 if (m_server_thread != nullptr) {
113 m_server_thread->EndWait(ResultSuccess);
114 }
115 }
116
117 // NOTE: Nintendo returns GetCurrentThread().GetWaitResult() here.
118 // This is technically incorrect, although it doesn't cause problems in practice
119 // because this is only ever called with request_thread = GetCurrentThreadPointer().
120 R_RETURN(request_thread->GetWaitResult());
121}
122
123Result KLightServerSession::ReplyAndReceive(u32* data) {
124 // Set the server context.
125 GetCurrentThread(m_kernel).SetLightSessionData(data);
126
127 // Reply, if we need to.
128 if (data[0] & KLightSession::ReplyFlag) {
129 KScopedSchedulerLock sl(m_kernel);
130
131 // Check that we're open.
132 R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
133 R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
134
135 // Check that we have a request to reply to.
136 R_UNLESS(m_current_request != nullptr, ResultInvalidState);
137
138 // Check that the server thread id is correct.
139 R_UNLESS(m_server_thread_id == GetCurrentThread(m_kernel).GetId(), ResultInvalidState);
140
141 // If we can reply, do so.
142 if (!m_current_request->IsTerminationRequested()) {
143 std::memcpy(m_current_request->GetLightSessionData(),
144 GetCurrentThread(m_kernel).GetLightSessionData(), KLightSession::DataSize);
145 m_current_request->EndWait(ResultSuccess);
146 }
147
148 // Close our current request.
149 m_current_request->Close();
150
151 // Clear our current request.
152 m_current_request = nullptr;
153 m_server_thread_id = InvalidThreadId;
154 }
155
156 // Create the wait queue for our receive.
157 ThreadQueueImplForKLightServerSessionReceive wait_queue(m_kernel,
158 std::addressof(m_server_thread));
159
160 // Receive.
161 while (true) {
162 // Try to receive a request.
163 {
164 KScopedSchedulerLock sl(m_kernel);
165
166 // Check that we aren't already receiving.
167 R_UNLESS(m_server_thread == nullptr, ResultInvalidState);
168 R_UNLESS(m_server_thread_id == InvalidThreadId, ResultInvalidState);
169
170 // Check that we're open.
171 R_UNLESS(!m_parent->IsClientClosed(), ResultSessionClosed);
172 R_UNLESS(!m_parent->IsServerClosed(), ResultSessionClosed);
173
174 // Check that we're not terminating.
175 R_UNLESS(!GetCurrentThread(m_kernel).IsTerminationRequested(),
176 ResultTerminationRequested);
177
178 // If we have a request available, use it.
179 if (auto head = m_request_list.begin(); head != m_request_list.end()) {
180 // Set our current request.
181 m_current_request = std::addressof(*head);
182 m_current_request->Open();
183
184 // Set our server thread id.
185 m_server_thread_id = GetCurrentThread(m_kernel).GetId();
186
187 // Copy the client request data.
188 std::memcpy(GetCurrentThread(m_kernel).GetLightSessionData(),
189 m_current_request->GetLightSessionData(), KLightSession::DataSize);
190
191 // We successfully received.
192 R_SUCCEED();
193 }
194
195 // We need to wait for a request to come in.
196
197 // Check if we were cancelled.
198 if (GetCurrentThread(m_kernel).IsWaitCancelled()) {
199 GetCurrentThread(m_kernel).ClearWaitCancelled();
200 R_THROW(ResultCancelled);
201 }
202
203 // Mark ourselves as cancellable.
204 GetCurrentThread(m_kernel).SetCancellable();
205
206 // Wait for a request to come in.
207 m_server_thread = GetCurrentThreadPointer(m_kernel);
208 GetCurrentThread(m_kernel).SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::IPC);
209 GetCurrentThread(m_kernel).BeginWait(std::addressof(wait_queue));
210 }
211
212 // We waited to receive a request; if our wait failed, return the failing result.
213 R_TRY(GetCurrentThread(m_kernel).GetWaitResult());
214 }
215}
216
217void KLightServerSession::CleanupRequests() {
218 // Cleanup all pending requests.
219 {
220 KScopedSchedulerLock sl(m_kernel);
221
222 // Handle the current request.
223 if (m_current_request != nullptr) {
224 // Reply to the current request.
225 if (!m_current_request->IsTerminationRequested()) {
226 m_current_request->EndWait(ResultSessionClosed);
227 }
228
229 // Clear our current request.
230 m_current_request->Close();
231 m_current_request = nullptr;
232 m_server_thread_id = InvalidThreadId;
233 }
234
235 // Reply to all other requests.
236 for (auto& thread : m_request_list) {
237 thread.EndWait(ResultSessionClosed);
238 }
239
240 // Wait up our server thread, if we have one.
241 if (m_server_thread != nullptr) {
242 m_server_thread->EndWait(ResultSessionClosed);
243 }
244 }
245}
246
247} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_server_session.h b/src/core/hle/kernel/k_light_server_session.h
new file mode 100644
index 000000000..8eca3eab6
--- /dev/null
+++ b/src/core/hle/kernel/k_light_server_session.h
@@ -0,0 +1,49 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_auto_object.h"
7#include "core/hle/kernel/k_thread.h"
8#include "core/hle/result.h"
9
10namespace Kernel {
11
12class KLightSession;
13
14class KLightServerSession final : public KAutoObject,
15 public Common::IntrusiveListBaseNode<KLightServerSession> {
16 KERNEL_AUTOOBJECT_TRAITS(KLightServerSession, KAutoObject);
17
18private:
19 KLightSession* m_parent{};
20 KThread::WaiterList m_request_list{};
21 KThread* m_current_request{};
22 u64 m_server_thread_id{std::numeric_limits<u64>::max()};
23 KThread* m_server_thread{};
24
25public:
26 explicit KLightServerSession(KernelCore& kernel);
27 ~KLightServerSession();
28
29 void Initialize(KLightSession* parent) {
30 // Set member variables. */
31 m_parent = parent;
32 }
33
34 virtual void Destroy() override;
35
36 constexpr const KLightSession* GetParent() const {
37 return m_parent;
38 }
39
40 Result OnRequest(KThread* request_thread);
41 Result ReplyAndReceive(u32* data);
42
43 void OnClientClosed();
44
45private:
46 void CleanupRequests();
47};
48
49} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_session.cpp b/src/core/hle/kernel/k_light_session.cpp
new file mode 100644
index 000000000..d8b1e6958
--- /dev/null
+++ b/src/core/hle/kernel/k_light_session.cpp
@@ -0,0 +1,81 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/k_client_port.h"
5#include "core/hle/kernel/k_light_client_session.h"
6#include "core/hle/kernel/k_light_server_session.h"
7#include "core/hle/kernel/k_light_session.h"
8#include "core/hle/kernel/k_process.h"
9
10namespace Kernel {
11
12KLightSession::KLightSession(KernelCore& kernel)
13 : KAutoObjectWithSlabHeapAndContainer(kernel), m_server(kernel), m_client(kernel) {}
14KLightSession::~KLightSession() = default;
15
16void KLightSession::Initialize(KClientPort* client_port, uintptr_t name) {
17 // Increment reference count.
18 // Because reference count is one on creation, this will result
19 // in a reference count of two. Thus, when both server and client are closed
20 // this object will be destroyed.
21 this->Open();
22
23 // Create our sub sessions.
24 KAutoObject::Create(std::addressof(m_server));
25 KAutoObject::Create(std::addressof(m_client));
26
27 // Initialize our sub sessions.
28 m_server.Initialize(this);
29 m_client.Initialize(this);
30
31 // Set state and name.
32 m_state = State::Normal;
33 m_name = name;
34
35 // Set our owner process.
36 m_process = GetCurrentProcessPointer(m_kernel);
37 m_process->Open();
38
39 // Set our port.
40 m_port = client_port;
41 if (m_port != nullptr) {
42 m_port->Open();
43 }
44
45 // Mark initialized.
46 m_initialized = true;
47}
48
49void KLightSession::Finalize() {
50 if (m_port != nullptr) {
51 m_port->OnSessionFinalized();
52 m_port->Close();
53 }
54}
55
56void KLightSession::OnServerClosed() {
57 if (m_state == State::Normal) {
58 m_state = State::ServerClosed;
59 m_client.OnServerClosed();
60 }
61
62 this->Close();
63}
64
65void KLightSession::OnClientClosed() {
66 if (m_state == State::Normal) {
67 m_state = State::ClientClosed;
68 m_server.OnClientClosed();
69 }
70
71 this->Close();
72}
73
74void KLightSession::PostDestroy(uintptr_t arg) {
75 // Release the session count resource the owner process holds.
76 KProcess* owner = reinterpret_cast<KProcess*>(arg);
77 owner->ReleaseResource(Svc::LimitableResource::SessionCountMax, 1);
78 owner->Close();
79}
80
81} // namespace Kernel
diff --git a/src/core/hle/kernel/k_light_session.h b/src/core/hle/kernel/k_light_session.h
new file mode 100644
index 000000000..f78d8e689
--- /dev/null
+++ b/src/core/hle/kernel/k_light_session.h
@@ -0,0 +1,86 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/kernel/k_light_client_session.h"
7#include "core/hle/kernel/k_light_server_session.h"
8#include "core/hle/kernel/slab_helpers.h"
9#include "core/hle/result.h"
10
11namespace Kernel {
12
13class KClientPort;
14class KProcess;
15
16// TODO: SupportDynamicExpansion for SlabHeap
17class KLightSession final
18 : public KAutoObjectWithSlabHeapAndContainer<KLightSession, KAutoObjectWithList> {
19 KERNEL_AUTOOBJECT_TRAITS(KLightSession, KAutoObject);
20
21private:
22 enum class State : u8 {
23 Invalid = 0,
24 Normal = 1,
25 ClientClosed = 2,
26 ServerClosed = 3,
27 };
28
29public:
30 static constexpr size_t DataSize = sizeof(u32) * 7;
31 static constexpr u32 ReplyFlag = (1U << 31);
32
33private:
34 KLightServerSession m_server;
35 KLightClientSession m_client;
36 State m_state{State::Invalid};
37 KClientPort* m_port{};
38 uintptr_t m_name{};
39 KProcess* m_process{};
40 bool m_initialized{};
41
42public:
43 explicit KLightSession(KernelCore& kernel);
44 ~KLightSession();
45
46 void Initialize(KClientPort* client_port, uintptr_t name);
47 void Finalize() override;
48
49 bool IsInitialized() const override {
50 return m_initialized;
51 }
52 uintptr_t GetPostDestroyArgument() const override {
53 return reinterpret_cast<uintptr_t>(m_process);
54 }
55
56 static void PostDestroy(uintptr_t arg);
57
58 void OnServerClosed();
59 void OnClientClosed();
60
61 bool IsServerClosed() const {
62 return m_state != State::Normal;
63 }
64 bool IsClientClosed() const {
65 return m_state != State::Normal;
66 }
67
68 Result OnRequest(KThread* request_thread) {
69 R_RETURN(m_server.OnRequest(request_thread));
70 }
71
72 KLightClientSession& GetClientSession() {
73 return m_client;
74 }
75 KLightServerSession& GetServerSession() {
76 return m_server;
77 }
78 const KLightClientSession& GetClientSession() const {
79 return m_client;
80 }
81 const KLightServerSession& GetServerSession() const {
82 return m_server;
83 }
84};
85
86} // namespace Kernel
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index 1621ca1d3..e5f5d8028 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -58,4 +58,13 @@ Result KPort::EnqueueSession(KServerSession* session) {
58 R_SUCCEED(); 58 R_SUCCEED();
59} 59}
60 60
61Result KPort::EnqueueSession(KLightServerSession* session) {
62 KScopedSchedulerLock sl{m_kernel};
63
64 R_UNLESS(m_state == State::Normal, ResultPortClosed);
65
66 m_server.EnqueueSession(session);
67 R_SUCCEED();
68}
69
61} // namespace Kernel 70} // namespace Kernel
diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h
index 991be27ab..26f5f14ef 100644
--- a/src/core/hle/kernel/k_port.h
+++ b/src/core/hle/kernel/k_port.h
@@ -13,6 +13,7 @@
13 13
14namespace Kernel { 14namespace Kernel {
15 15
16class KLightServerSession;
16class KServerSession; 17class KServerSession;
17 18
18class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> { 19class KPort final : public KAutoObjectWithSlabHeapAndContainer<KPort, KAutoObjectWithList> {
@@ -38,6 +39,7 @@ public:
38 bool IsServerClosed() const; 39 bool IsServerClosed() const;
39 40
40 Result EnqueueSession(KServerSession* session); 41 Result EnqueueSession(KServerSession* session);
42 Result EnqueueSession(KLightServerSession* session);
41 43
42 KClientPort& GetClientPort() { 44 KClientPort& GetClientPort() {
43 return m_client; 45 return m_client;
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index a29d34bc1..bb6632f58 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -27,12 +27,14 @@ bool KServerPort::IsLight() const {
27void KServerPort::CleanupSessions() { 27void KServerPort::CleanupSessions() {
28 // Ensure our preconditions are met. 28 // Ensure our preconditions are met.
29 if (this->IsLight()) { 29 if (this->IsLight()) {
30 UNIMPLEMENTED(); 30 ASSERT(m_session_list.empty());
31 } else {
32 ASSERT(m_light_session_list.empty());
31 } 33 }
32 34
33 // Cleanup the session list. 35 // Cleanup the session list.
34 while (true) { 36 while (true) {
35 // Get the last session in the list 37 // Get the last session in the list.
36 KServerSession* session = nullptr; 38 KServerSession* session = nullptr;
37 { 39 {
38 KScopedSchedulerLock sl{m_kernel}; 40 KScopedSchedulerLock sl{m_kernel};
@@ -49,6 +51,26 @@ void KServerPort::CleanupSessions() {
49 break; 51 break;
50 } 52 }
51 } 53 }
54
55 // Cleanup the light session list.
56 while (true) {
57 // Get the last session in the list.
58 KLightServerSession* session = nullptr;
59 {
60 KScopedSchedulerLock sl{m_kernel};
61 if (!m_light_session_list.empty()) {
62 session = std::addressof(m_light_session_list.front());
63 m_light_session_list.pop_front();
64 }
65 }
66
67 // Close the session.
68 if (session != nullptr) {
69 session->Close();
70 } else {
71 break;
72 }
73 }
52} 74}
53 75
54void KServerPort::Destroy() { 76void KServerPort::Destroy() {
@@ -64,8 +86,7 @@ void KServerPort::Destroy() {
64 86
65bool KServerPort::IsSignaled() const { 87bool KServerPort::IsSignaled() const {
66 if (this->IsLight()) { 88 if (this->IsLight()) {
67 UNIMPLEMENTED(); 89 return !m_light_session_list.empty();
68 return false;
69 } else { 90 } else {
70 return !m_session_list.empty(); 91 return !m_session_list.empty();
71 } 92 }
@@ -83,6 +104,18 @@ void KServerPort::EnqueueSession(KServerSession* session) {
83 } 104 }
84} 105}
85 106
107void KServerPort::EnqueueSession(KLightServerSession* session) {
108 ASSERT(this->IsLight());
109
110 KScopedSchedulerLock sl{m_kernel};
111
112 // Add the session to our queue.
113 m_light_session_list.push_back(*session);
114 if (m_light_session_list.size() == 1) {
115 this->NotifyAvailable();
116 }
117}
118
86KServerSession* KServerPort::AcceptSession() { 119KServerSession* KServerPort::AcceptSession() {
87 ASSERT(!this->IsLight()); 120 ASSERT(!this->IsLight());
88 121
@@ -98,4 +131,19 @@ KServerSession* KServerPort::AcceptSession() {
98 return session; 131 return session;
99} 132}
100 133
134KLightServerSession* KServerPort::AcceptLightSession() {
135 ASSERT(this->IsLight());
136
137 KScopedSchedulerLock sl{m_kernel};
138
139 // Return the first session in the list.
140 if (m_light_session_list.empty()) {
141 return nullptr;
142 }
143
144 KLightServerSession* session = std::addressof(m_light_session_list.front());
145 m_light_session_list.pop_front();
146 return session;
147}
148
101} // namespace Kernel 149} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index 625280290..72fdb6734 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -9,6 +9,7 @@
9 9
10#include "common/intrusive_list.h" 10#include "common/intrusive_list.h"
11 11
12#include "core/hle/kernel/k_light_server_session.h"
12#include "core/hle/kernel/k_server_session.h" 13#include "core/hle/kernel/k_server_session.h"
13#include "core/hle/kernel/k_synchronization_object.h" 14#include "core/hle/kernel/k_synchronization_object.h"
14 15
@@ -28,8 +29,10 @@ public:
28 void Initialize(KPort* parent); 29 void Initialize(KPort* parent);
29 30
30 void EnqueueSession(KServerSession* session); 31 void EnqueueSession(KServerSession* session);
32 void EnqueueSession(KLightServerSession* session);
31 33
32 KServerSession* AcceptSession(); 34 KServerSession* AcceptSession();
35 KLightServerSession* AcceptLightSession();
33 36
34 const KPort* GetParent() const { 37 const KPort* GetParent() const {
35 return m_parent; 38 return m_parent;
@@ -43,10 +46,12 @@ public:
43 46
44private: 47private:
45 using SessionList = Common::IntrusiveListBaseTraits<KServerSession>::ListType; 48 using SessionList = Common::IntrusiveListBaseTraits<KServerSession>::ListType;
49 using LightSessionList = Common::IntrusiveListBaseTraits<KLightServerSession>::ListType;
46 50
47 void CleanupSessions(); 51 void CleanupSessions();
48 52
49 SessionList m_session_list{}; 53 SessionList m_session_list{};
54 LightSessionList m_light_session_list{};
50 KPort* m_parent{}; 55 KPort* m_parent{};
51}; 56};
52 57
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 3ea653163..e33a88e24 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -453,6 +453,11 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
453 size_t client_buffer_size = request->GetSize(); 453 size_t client_buffer_size = request->GetSize();
454 // bool recv_list_broken = false; 454 // bool recv_list_broken = false;
455 455
456 if (!client_message) {
457 client_message = GetInteger(client_thread->GetTlsAddress());
458 client_buffer_size = MessageBufferSize;
459 }
460
456 // Receive the message. 461 // Receive the message.
457 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; 462 Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()};
458 if (out_context != nullptr) { 463 if (out_context != nullptr) {
@@ -462,8 +467,7 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext
462 std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread); 467 std::make_shared<Service::HLERequestContext>(m_kernel, memory, this, client_thread);
463 (*out_context)->SetSessionRequestManager(manager); 468 (*out_context)->SetSessionRequestManager(manager);
464 (*out_context) 469 (*out_context)
465 ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(), 470 ->PopulateFromIncomingCommandBuffer(*client_thread->GetOwnerProcess(), cmd_buf);
466 cmd_buf);
467 } else { 471 } else {
468 KThread* server_thread = GetCurrentThreadPointer(m_kernel); 472 KThread* server_thread = GetCurrentThreadPointer(m_kernel);
469 KProcess& src_process = *client_thread->GetOwnerProcess(); 473 KProcess& src_process = *client_thread->GetOwnerProcess();
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index f69bab088..3f4dd5989 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/src/core/hle/kernel/k_session.h
@@ -46,6 +46,10 @@ public:
46 return this->GetState() != State::Normal; 46 return this->GetState() != State::Normal;
47 } 47 }
48 48
49 Result OnRequest(KSessionRequest* request) {
50 R_RETURN(m_server.OnRequest(request));
51 }
52
49 KClientSession& GetClientSession() { 53 KClientSession& GetClientSession() {
50 return m_client; 54 return m_client;
51 } 55 }
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 390db2409..e9925d231 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -385,6 +385,13 @@ public:
385 m_cancellable = false; 385 m_cancellable = false;
386 } 386 }
387 387
388 u32* GetLightSessionData() const {
389 return m_light_ipc_data;
390 }
391 void SetLightSessionData(u32* data) {
392 m_light_ipc_data = data;
393 }
394
388 bool IsTerminationRequested() const { 395 bool IsTerminationRequested() const {
389 return m_termination_requested || GetRawState() == ThreadState::Terminated; 396 return m_termination_requested || GetRawState() == ThreadState::Terminated;
390 } 397 }
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 032c4e093..8cb05ca0b 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1340,6 +1340,7 @@ struct KernelCore::SlabHeapContainer {
1340 KSlabHeap<KProcess> process; 1340 KSlabHeap<KProcess> process;
1341 KSlabHeap<KResourceLimit> resource_limit; 1341 KSlabHeap<KResourceLimit> resource_limit;
1342 KSlabHeap<KSession> session; 1342 KSlabHeap<KSession> session;
1343 KSlabHeap<KLightSession> light_session;
1343 KSlabHeap<KSharedMemory> shared_memory; 1344 KSlabHeap<KSharedMemory> shared_memory;
1344 KSlabHeap<KSharedMemoryInfo> shared_memory_info; 1345 KSlabHeap<KSharedMemoryInfo> shared_memory_info;
1345 KSlabHeap<KThread> thread; 1346 KSlabHeap<KThread> thread;
@@ -1370,6 +1371,8 @@ KSlabHeap<T>& KernelCore::SlabHeap() {
1370 return slab_heap_container->resource_limit; 1371 return slab_heap_container->resource_limit;
1371 } else if constexpr (std::is_same_v<T, KSession>) { 1372 } else if constexpr (std::is_same_v<T, KSession>) {
1372 return slab_heap_container->session; 1373 return slab_heap_container->session;
1374 } else if constexpr (std::is_same_v<T, KLightSession>) {
1375 return slab_heap_container->light_session;
1373 } else if constexpr (std::is_same_v<T, KSharedMemory>) { 1376 } else if constexpr (std::is_same_v<T, KSharedMemory>) {
1374 return slab_heap_container->shared_memory; 1377 return slab_heap_container->shared_memory;
1375 } else if constexpr (std::is_same_v<T, KSharedMemoryInfo>) { 1378 } else if constexpr (std::is_same_v<T, KSharedMemoryInfo>) {
@@ -1407,6 +1410,7 @@ template KSlabHeap<KPort>& KernelCore::SlabHeap();
1407template KSlabHeap<KProcess>& KernelCore::SlabHeap(); 1410template KSlabHeap<KProcess>& KernelCore::SlabHeap();
1408template KSlabHeap<KResourceLimit>& KernelCore::SlabHeap(); 1411template KSlabHeap<KResourceLimit>& KernelCore::SlabHeap();
1409template KSlabHeap<KSession>& KernelCore::SlabHeap(); 1412template KSlabHeap<KSession>& KernelCore::SlabHeap();
1413template KSlabHeap<KLightSession>& KernelCore::SlabHeap();
1410template KSlabHeap<KSharedMemory>& KernelCore::SlabHeap(); 1414template KSlabHeap<KSharedMemory>& KernelCore::SlabHeap();
1411template KSlabHeap<KSharedMemoryInfo>& KernelCore::SlabHeap(); 1415template KSlabHeap<KSharedMemoryInfo>& KernelCore::SlabHeap();
1412template KSlabHeap<KThread>& KernelCore::SlabHeap(); 1416template KSlabHeap<KThread>& KernelCore::SlabHeap();
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 7fa8e2a85..0f45a3249 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -139,7 +139,7 @@ void PhysicalCore::RunThread(Kernel::KThread* thread) {
139 } 139 }
140 140
141 // Handle external interrupt sources. 141 // Handle external interrupt sources.
142 if (interrupt || !m_is_single_core) { 142 if (interrupt || m_is_single_core) {
143 return; 143 return;
144 } 144 }
145 } 145 }
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
index 6b5e1cb8d..47a3e7bb0 100644
--- a/src/core/hle/kernel/svc/svc_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -7,59 +7,127 @@
7#include "core/hle/kernel/k_client_session.h" 7#include "core/hle/kernel/k_client_session.h"
8#include "core/hle/kernel/k_hardware_timer.h" 8#include "core/hle/kernel/k_hardware_timer.h"
9#include "core/hle/kernel/k_process.h" 9#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/k_scoped_resource_reservation.h"
10#include "core/hle/kernel/k_server_session.h" 11#include "core/hle/kernel/k_server_session.h"
12#include "core/hle/kernel/k_session.h"
11#include "core/hle/kernel/svc.h" 13#include "core/hle/kernel/svc.h"
12#include "core/hle/kernel/svc_results.h" 14#include "core/hle/kernel/svc_results.h"
13 15
14namespace Kernel::Svc { 16namespace Kernel::Svc {
15 17
16/// Makes a blocking IPC call to a service. 18namespace {
17Result SendSyncRequest(Core::System& system, Handle handle) { 19
18 // Get the client session from its handle. 20Result SendSyncRequestImpl(KernelCore& kernel, uintptr_t message, size_t buffer_size,
21 Handle session_handle) {
22 // Get the client session.
19 KScopedAutoObject session = 23 KScopedAutoObject session =
20 GetCurrentProcess(system.Kernel()).GetHandleTable().GetObject<KClientSession>(handle); 24 GetCurrentProcess(kernel).GetHandleTable().GetObject<KClientSession>(session_handle);
21 R_UNLESS(session.IsNotNull(), ResultInvalidHandle); 25 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
22 26
23 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}", handle); 27 // Get the parent, and persist a reference to it until we're done.
28 KScopedAutoObject parent = session->GetParent();
29 ASSERT(parent.IsNotNull());
24 30
25 R_RETURN(session->SendSyncRequest()); 31 // Send the request.
32 R_RETURN(session->SendSyncRequest(message, buffer_size));
26} 33}
27 34
28Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer, 35Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
29 uint64_t message_buffer_size, Handle session_handle) { 36 size_t buffer_size, KPhysicalAddress message_paddr,
30 UNIMPLEMENTED(); 37 KSynchronizationObject** objs, int32_t num_objects, Handle reply_target,
31 R_THROW(ResultNotImplemented); 38 int64_t timeout_ns) {
32} 39 // Reply to the target, if one is specified.
40 if (reply_target != InvalidHandle) {
41 KScopedAutoObject session =
42 GetCurrentProcess(kernel).GetHandleTable().GetObject<KServerSession>(reply_target);
43 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
33 44
34Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle, 45 // If we fail to reply, we want to set the output index to -1.
35 uint64_t message_buffer, uint64_t message_buffer_size, 46 ON_RESULT_FAILURE {
36 Handle session_handle) { 47 *out_index = -1;
37 UNIMPLEMENTED(); 48 };
38 R_THROW(ResultNotImplemented); 49
50 // Send the reply.
51 R_TRY(session->SendReply());
52 // R_TRY(session->SendReply(message, buffer_size, message_paddr));
53 }
54
55 // Receive a message.
56 {
57 // Convert the timeout from nanoseconds to ticks.
58 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
59 s64 timeout;
60 if (timeout_ns > 0) {
61 const s64 offset_tick(timeout_ns);
62 if (offset_tick > 0) {
63 timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
64 if (timeout <= 0) {
65 timeout = std::numeric_limits<s64>::max();
66 }
67 } else {
68 timeout = std::numeric_limits<s64>::max();
69 }
70 } else {
71 timeout = timeout_ns;
72 }
73
74 // Wait for a message.
75 while (true) {
76 // Wait for an object.
77 s32 index;
78 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs,
79 num_objects, timeout);
80 if (ResultTimedOut == result) {
81 R_THROW(result);
82 }
83
84 // Receive the request.
85 if (R_SUCCEEDED(result)) {
86 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
87 if (session != nullptr) {
88 // result = session->ReceiveRequest(message, buffer_size, message_paddr);
89 result = session->ReceiveRequest();
90 if (ResultNotFound == result) {
91 continue;
92 }
93 }
94 }
95
96 *out_index = index;
97 R_RETURN(result);
98 }
99 }
39} 100}
40 101
41Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles, 102Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t message,
42 Handle reply_target, s64 timeout_ns) { 103 size_t buffer_size, KPhysicalAddress message_paddr,
104 KProcessAddress user_handles, int32_t num_handles, Handle reply_target,
105 int64_t timeout_ns) {
43 // Ensure number of handles is valid. 106 // Ensure number of handles is valid.
44 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange); 107 R_UNLESS(0 <= num_handles && num_handles <= Svc::ArgumentHandleCountMax, ResultOutOfRange);
45 108
46 // Get the synchronization context. 109 // Get the synchronization context.
47 auto& kernel = system.Kernel(); 110 auto& process = GetCurrentProcess(kernel);
48 auto& handle_table = GetCurrentProcess(kernel).GetHandleTable(); 111 auto& thread = GetCurrentThread(kernel);
49 auto objs = GetCurrentThread(kernel).GetSynchronizationObjectBuffer(); 112 auto& handle_table = process.GetHandleTable();
50 auto handles = GetCurrentThread(kernel).GetHandleBuffer(); 113 KSynchronizationObject** objs = thread.GetSynchronizationObjectBuffer().data();
114 Handle* handles = thread.GetHandleBuffer().data();
51 115
52 // Copy user handles. 116 // Copy user handles.
53 if (num_handles > 0) { 117 if (num_handles > 0) {
54 // Get the handles. 118 // Ensure that we can try to get the handles.
55 R_UNLESS(GetCurrentMemory(kernel).ReadBlock(handles_addr, handles.data(), 119 R_UNLESS(process.GetPageTable().Contains(user_handles, num_handles * sizeof(Handle)),
56 sizeof(Handle) * num_handles),
57 ResultInvalidPointer); 120 ResultInvalidPointer);
58 121
122 // Get the handles
123 R_UNLESS(
124 GetCurrentMemory(kernel).ReadBlock(user_handles, handles, sizeof(Handle) * num_handles),
125 ResultInvalidPointer);
126
59 // Convert the handles to objects. 127 // Convert the handles to objects.
60 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>( 128 R_UNLESS(
61 objs.data(), handles.data(), num_handles), 129 handle_table.GetMultipleObjects<KSynchronizationObject>(objs, handles, num_handles),
62 ResultInvalidHandle); 130 ResultInvalidHandle);
63 } 131 }
64 132
65 // Ensure handles are closed when we're done. 133 // Ensure handles are closed when we're done.
@@ -69,69 +137,135 @@ Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_ad
69 } 137 }
70 }); 138 });
71 139
72 // Reply to the target, if one is specified. 140 R_RETURN(ReplyAndReceiveImpl(kernel, out_index, message, buffer_size, message_paddr, objs,
73 if (reply_target != InvalidHandle) { 141 num_handles, reply_target, timeout_ns));
74 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target); 142}
75 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
76 143
77 // If we fail to reply, we want to set the output index to -1. 144} // namespace
145
146/// Makes a blocking IPC call to a service.
147Result SendSyncRequest(Core::System& system, Handle session_handle) {
148 R_RETURN(SendSyncRequestImpl(system.Kernel(), 0, 0, session_handle));
149}
150
151Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message, uint64_t buffer_size,
152 Handle session_handle) {
153 auto& kernel = system.Kernel();
154
155 // Validate that the message buffer is page aligned and does not overflow.
156 R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
157 R_UNLESS(buffer_size > 0, ResultInvalidSize);
158 R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
159 R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
160
161 // Get the process page table.
162 auto& page_table = GetCurrentProcess(kernel).GetPageTable();
163
164 // Lock the message buffer.
165 R_TRY(page_table.LockForIpcUserBuffer(nullptr, message, buffer_size));
166
167 {
168 // If we fail to send the message, unlock the message buffer.
78 ON_RESULT_FAILURE { 169 ON_RESULT_FAILURE {
79 *out_index = -1; 170 page_table.UnlockForIpcUserBuffer(message, buffer_size);
80 }; 171 };
81 172
82 // Send the reply. 173 // Send the request.
83 R_TRY(session->SendReply()); 174 ASSERT(message != 0);
175 R_TRY(SendSyncRequestImpl(kernel, message, buffer_size, session_handle));
84 } 176 }
85 177
86 // Convert the timeout from nanoseconds to ticks. 178 // We successfully processed, so try to unlock the message buffer.
87 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... 179 R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
88 s64 timeout; 180}
89 if (timeout_ns > 0) {
90 const s64 offset_tick(timeout_ns);
91 if (offset_tick > 0) {
92 timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
93 if (timeout <= 0) {
94 timeout = std::numeric_limits<s64>::max();
95 }
96 } else {
97 timeout = std::numeric_limits<s64>::max();
98 }
99 } else {
100 timeout = timeout_ns;
101 }
102 181
103 // Wait for a message. 182Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
104 while (true) { 183 uint64_t message, uint64_t buffer_size,
105 // Wait for an object. 184 Handle session_handle) {
106 s32 index; 185 // Get the process and handle table.
107 Result result = KSynchronizationObject::Wait(kernel, std::addressof(index), objs.data(), 186 auto& process = GetCurrentProcess(system.Kernel());
108 num_handles, timeout); 187 auto& handle_table = process.GetHandleTable();
109 if (result == ResultTimedOut) {
110 R_RETURN(result);
111 }
112 188
113 // Receive the request. 189 // Reserve a new event from the process resource limit.
114 if (R_SUCCEEDED(result)) { 190 KScopedResourceReservation event_reservation(std::addressof(process),
115 KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); 191 Svc::LimitableResource::EventCountMax);
116 if (session != nullptr) { 192 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
117 result = session->ReceiveRequest();
118 if (result == ResultNotFound) {
119 continue;
120 }
121 }
122 }
123 193
124 *out_index = index; 194 // Get the client session.
125 R_RETURN(result); 195 KScopedAutoObject session = process.GetHandleTable().GetObject<KClientSession>(session_handle);
126 } 196 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
197
198 // Get the parent, and persist a reference to it until we're done.
199 KScopedAutoObject parent = session->GetParent();
200 ASSERT(parent.IsNotNull());
201
202 // Create a new event.
203 KEvent* event = KEvent::Create(system.Kernel());
204 R_UNLESS(event != nullptr, ResultOutOfResource);
205
206 // Initialize the event.
207 event->Initialize(std::addressof(process));
208
209 // Commit our reservation.
210 event_reservation.Commit();
211
212 // At end of scope, kill the standing references to the sub events.
213 SCOPE_EXIT({
214 event->GetReadableEvent().Close();
215 event->Close();
216 });
217
218 // Register the event.
219 KEvent::Register(system.Kernel(), event);
220
221 // Add the readable event to the handle table.
222 R_TRY(handle_table.Add(out_event_handle, std::addressof(event->GetReadableEvent())));
223
224 // Ensure that if we fail to send the request, we close the readable handle.
225 ON_RESULT_FAILURE {
226 handle_table.Remove(*out_event_handle);
227 };
228
229 // Send the async request.
230 R_RETURN(session->SendAsyncRequest(event, message, buffer_size));
127} 231}
128 232
129Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, 233Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles, s32 num_handles,
130 uint64_t message_buffer, uint64_t message_buffer_size, 234 Handle reply_target, s64 timeout_ns) {
131 uint64_t handles, int32_t num_handles, Handle reply_target, 235 R_RETURN(ReplyAndReceiveImpl(system.Kernel(), out_index, 0, 0, 0, handles, num_handles,
132 int64_t timeout_ns) { 236 reply_target, timeout_ns));
133 UNIMPLEMENTED(); 237}
134 R_THROW(ResultNotImplemented); 238
239Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message,
240 uint64_t buffer_size, uint64_t handles, int32_t num_handles,
241 Handle reply_target, int64_t timeout_ns) {
242 // Validate that the message buffer is page aligned and does not overflow.
243 R_UNLESS(Common::IsAligned(message, PageSize), ResultInvalidAddress);
244 R_UNLESS(buffer_size > 0, ResultInvalidSize);
245 R_UNLESS(Common::IsAligned(buffer_size, PageSize), ResultInvalidSize);
246 R_UNLESS(message < message + buffer_size, ResultInvalidCurrentMemory);
247
248 // Get the process page table.
249 auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable();
250
251 // Lock the message buffer, getting its physical address.
252 KPhysicalAddress message_paddr;
253 R_TRY(page_table.LockForIpcUserBuffer(std::addressof(message_paddr), message, buffer_size));
254
255 {
256 // If we fail to send the message, unlock the message buffer.
257 ON_RESULT_FAILURE {
258 page_table.UnlockForIpcUserBuffer(message, buffer_size);
259 };
260
261 // Reply/Receive the request.
262 ASSERT(message != 0);
263 R_TRY(ReplyAndReceiveImpl(system.Kernel(), out_index, message, buffer_size, message_paddr,
264 handles, num_handles, reply_target, timeout_ns));
265 }
266
267 // We successfully processed, so try to unlock the message buffer.
268 R_RETURN(page_table.UnlockForIpcUserBuffer(message, buffer_size));
135} 269}
136 270
137Result SendSyncRequest64(Core::System& system, Handle session_handle) { 271Result SendSyncRequest64(Core::System& system, Handle session_handle) {
diff --git a/src/core/hle/kernel/svc/svc_light_ipc.cpp b/src/core/hle/kernel/svc/svc_light_ipc.cpp
index d757d5af2..4772cbda1 100644
--- a/src/core/hle/kernel/svc/svc_light_ipc.cpp
+++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp
@@ -1,21 +1,40 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 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 "core/arm/arm_interface.h"
5#include "core/core.h" 4#include "core/core.h"
5#include "core/hle/kernel/k_light_client_session.h"
6#include "core/hle/kernel/k_light_server_session.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_thread.h"
6#include "core/hle/kernel/svc.h" 9#include "core/hle/kernel/svc.h"
7#include "core/hle/kernel/svc_results.h" 10#include "core/hle/kernel/svc_results.h"
8 11
9namespace Kernel::Svc { 12namespace Kernel::Svc {
10 13
11Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) { 14Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) {
12 UNIMPLEMENTED(); 15 // Get the light client session from its handle.
13 R_THROW(ResultNotImplemented); 16 KScopedAutoObject session = GetCurrentProcess(system.Kernel())
17 .GetHandleTable()
18 .GetObject<KLightClientSession>(session_handle);
19 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
20
21 // Send the request.
22 R_TRY(session->SendSyncRequest(args));
23
24 R_SUCCEED();
14} 25}
15 26
16Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) { 27Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) {
17 UNIMPLEMENTED(); 28 // Get the light server session from its handle.
18 R_THROW(ResultNotImplemented); 29 KScopedAutoObject session = GetCurrentProcess(system.Kernel())
30 .GetHandleTable()
31 .GetObject<KLightServerSession>(session_handle);
32 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
33
34 // Handle the request.
35 R_TRY(session->ReplyAndReceive(args));
36
37 R_SUCCEED();
19} 38}
20 39
21Result SendSyncRequestLight64(Core::System& system, Handle session_handle, u32* args) { 40Result SendSyncRequestLight64(Core::System& system, Handle session_handle, u32* args) {
diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp
index abba757c7..737749f7d 100644
--- a/src/core/hle/kernel/svc/svc_port.cpp
+++ b/src/core/hle/kernel/svc/svc_port.cpp
@@ -5,6 +5,7 @@
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/kernel/k_client_port.h" 6#include "core/hle/kernel/k_client_port.h"
7#include "core/hle/kernel/k_client_session.h" 7#include "core/hle/kernel/k_client_session.h"
8#include "core/hle/kernel/k_light_client_session.h"
8#include "core/hle/kernel/k_object_name.h" 9#include "core/hle/kernel/k_object_name.h"
9#include "core/hle/kernel/k_port.h" 10#include "core/hle/kernel/k_port.h"
10#include "core/hle/kernel/k_process.h" 11#include "core/hle/kernel/k_process.h"
@@ -51,13 +52,73 @@ Result ConnectToNamedPort(Core::System& system, Handle* out, u64 user_name) {
51 52
52Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client, 53Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
53 int32_t max_sessions, bool is_light, uint64_t name) { 54 int32_t max_sessions, bool is_light, uint64_t name) {
54 UNIMPLEMENTED(); 55 auto& kernel = system.Kernel();
55 R_THROW(ResultNotImplemented); 56
57 // Ensure max sessions is valid.
58 R_UNLESS(max_sessions > 0, ResultOutOfRange);
59
60 // Get the current handle table.
61 auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
62
63 // Create a new port.
64 KPort* port = KPort::Create(kernel);
65 R_UNLESS(port != nullptr, ResultOutOfResource);
66
67 // Initialize the port.
68 port->Initialize(max_sessions, is_light, name);
69
70 // Ensure that we clean up the port (and its only references are handle table) on function end.
71 SCOPE_EXIT({
72 port->GetServerPort().Close();
73 port->GetClientPort().Close();
74 });
75
76 // Register the port.
77 KPort::Register(kernel, port);
78
79 // Add the client to the handle table.
80 R_TRY(handle_table.Add(out_client, std::addressof(port->GetClientPort())));
81
82 // Ensure that we maintain a clean handle state on exit.
83 ON_RESULT_FAILURE {
84 handle_table.Remove(*out_client);
85 };
86
87 // Add the server to the handle table.
88 R_RETURN(handle_table.Add(out_server, std::addressof(port->GetServerPort())));
56} 89}
57 90
58Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port) { 91Result ConnectToPort(Core::System& system, Handle* out, Handle port) {
59 UNIMPLEMENTED(); 92 // Get the current handle table.
60 R_THROW(ResultNotImplemented); 93 auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
94
95 // Get the client port.
96 KScopedAutoObject client_port = handle_table.GetObject<KClientPort>(port);
97 R_UNLESS(client_port.IsNotNull(), ResultInvalidHandle);
98
99 // Reserve a handle for the port.
100 // NOTE: Nintendo really does write directly to the output handle here.
101 R_TRY(handle_table.Reserve(out));
102 ON_RESULT_FAILURE {
103 handle_table.Unreserve(*out);
104 };
105
106 // Create the session.
107 KAutoObject* session;
108 if (client_port->IsLight()) {
109 R_TRY(client_port->CreateLightSession(
110 reinterpret_cast<KLightClientSession**>(std::addressof(session))));
111 } else {
112 R_TRY(client_port->CreateSession(
113 reinterpret_cast<KClientSession**>(std::addressof(session))));
114 }
115
116 // Register the session.
117 handle_table.Register(*out, session);
118 session->Close();
119
120 // We succeeded.
121 R_SUCCEED();
61} 122}
62 123
63Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name, 124Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name,
diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp
index 01b8a52ad..2f5905f32 100644
--- a/src/core/hle/kernel/svc/svc_session.cpp
+++ b/src/core/hle/kernel/svc/svc_session.cpp
@@ -3,8 +3,10 @@
3 3
4#include "common/scope_exit.h" 4#include "common/scope_exit.h"
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/kernel/k_light_session.h"
6#include "core/hle/kernel/k_process.h" 7#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scoped_resource_reservation.h" 8#include "core/hle/kernel/k_scoped_resource_reservation.h"
9#include "core/hle/kernel/k_server_port.h"
8#include "core/hle/kernel/k_session.h" 10#include "core/hle/kernel/k_session.h"
9#include "core/hle/kernel/svc.h" 11#include "core/hle/kernel/svc.h"
10 12
@@ -20,7 +22,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
20 T* session; 22 T* session;
21 23
22 // Reserve a new session from the process resource limit. 24 // Reserve a new session from the process resource limit.
23 // FIXME: LimitableResource_SessionCountMax 25 // TODO: Dynamic resource limits
24 KScopedResourceReservation session_reservation(std::addressof(process), 26 KScopedResourceReservation session_reservation(std::addressof(process),
25 LimitableResource::SessionCountMax); 27 LimitableResource::SessionCountMax);
26 if (session_reservation.Succeeded()) { 28 if (session_reservation.Succeeded()) {
@@ -92,16 +94,42 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien
92Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, bool is_light, 94Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, bool is_light,
93 u64 name) { 95 u64 name) {
94 if (is_light) { 96 if (is_light) {
95 // return CreateSession<KLightSession>(system, out_server, out_client, name); 97 R_RETURN(CreateSession<KLightSession>(system, out_server, out_client, name));
96 R_THROW(ResultNotImplemented);
97 } else { 98 } else {
98 R_RETURN(CreateSession<KSession>(system, out_server, out_client, name)); 99 R_RETURN(CreateSession<KSession>(system, out_server, out_client, name));
99 } 100 }
100} 101}
101 102
102Result AcceptSession(Core::System& system, Handle* out_handle, Handle port_handle) { 103Result AcceptSession(Core::System& system, Handle* out, Handle port_handle) {
103 UNIMPLEMENTED(); 104 // Get the current handle table.
104 R_THROW(ResultNotImplemented); 105 auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
106
107 // Get the server port.
108 KScopedAutoObject port = handle_table.GetObject<KServerPort>(port_handle);
109 R_UNLESS(port.IsNotNull(), ResultInvalidHandle);
110
111 // Reserve an entry for the new session.
112 R_TRY(handle_table.Reserve(out));
113 ON_RESULT_FAILURE {
114 handle_table.Unreserve(*out);
115 };
116
117 // Accept the session.
118 KAutoObject* session;
119 if (port->IsLight()) {
120 session = port->AcceptLightSession();
121 } else {
122 session = port->AcceptSession();
123 }
124
125 // Ensure we accepted successfully.
126 R_UNLESS(session != nullptr, ResultNotFound);
127
128 // Register the session.
129 handle_table.Register(*out, session);
130 session->Close();
131
132 R_SUCCEED();
105} 133}
106 134
107Result CreateSession64(Core::System& system, Handle* out_server_session_handle, 135Result CreateSession64(Core::System& system, Handle* out_server_session_handle,
diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp
index ff374ae39..38955932c 100644
--- a/src/core/hle/service/hle_ipc.cpp
+++ b/src/core/hle/service/hle_ipc.cpp
@@ -146,8 +146,10 @@ HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::
146 146
147HLERequestContext::~HLERequestContext() = default; 147HLERequestContext::~HLERequestContext() = default;
148 148
149void HLERequestContext::ParseCommandBuffer(const Kernel::KHandleTable& handle_table, 149void HLERequestContext::ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf,
150 u32_le* src_cmdbuf, bool incoming) { 150 bool incoming) {
151 client_handle_table = &process.GetHandleTable();
152
151 IPC::RequestParser rp(src_cmdbuf); 153 IPC::RequestParser rp(src_cmdbuf);
152 command_header = rp.PopRaw<IPC::CommandHeader>(); 154 command_header = rp.PopRaw<IPC::CommandHeader>();
153 155
@@ -160,7 +162,8 @@ void HLERequestContext::ParseCommandBuffer(const Kernel::KHandleTable& handle_ta
160 if (command_header->enable_handle_descriptor) { 162 if (command_header->enable_handle_descriptor) {
161 handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>(); 163 handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
162 if (handle_descriptor_header->send_current_pid) { 164 if (handle_descriptor_header->send_current_pid) {
163 pid = rp.Pop<u64>(); 165 pid = process.GetProcessId();
166 rp.Skip(2, false);
164 } 167 }
165 if (incoming) { 168 if (incoming) {
166 // Populate the object lists with the data in the IPC request. 169 // Populate the object lists with the data in the IPC request.
@@ -267,9 +270,9 @@ void HLERequestContext::ParseCommandBuffer(const Kernel::KHandleTable& handle_ta
267 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part. 270 rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
268} 271}
269 272
270Result HLERequestContext::PopulateFromIncomingCommandBuffer( 273Result HLERequestContext::PopulateFromIncomingCommandBuffer(Kernel::KProcess& process,
271 const Kernel::KHandleTable& handle_table, u32_le* src_cmdbuf) { 274 u32_le* src_cmdbuf) {
272 ParseCommandBuffer(handle_table, src_cmdbuf, true); 275 ParseCommandBuffer(process, src_cmdbuf, true);
273 276
274 if (command_header->IsCloseCommand()) { 277 if (command_header->IsCloseCommand()) {
275 // Close does not populate the rest of the IPC header 278 // Close does not populate the rest of the IPC header
diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h
index ad5259a5c..18d464c63 100644
--- a/src/core/hle/service/hle_ipc.h
+++ b/src/core/hle/service/hle_ipc.h
@@ -38,6 +38,7 @@ namespace Kernel {
38class KAutoObject; 38class KAutoObject;
39class KernelCore; 39class KernelCore;
40class KHandleTable; 40class KHandleTable;
41class KProcess;
41class KServerSession; 42class KServerSession;
42class KThread; 43class KThread;
43} // namespace Kernel 44} // namespace Kernel
@@ -75,6 +76,7 @@ protected:
75 76
76using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>; 77using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
77using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; 78using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
79using SessionRequestHandlerFactory = std::function<SessionRequestHandlerPtr()>;
78 80
79/** 81/**
80 * Manages the underlying HLE requests for a session, and whether (or not) the session should be 82 * Manages the underlying HLE requests for a session, and whether (or not) the session should be
@@ -194,8 +196,7 @@ public:
194 } 196 }
195 197
196 /// Populates this context with data from the requesting process/thread. 198 /// Populates this context with data from the requesting process/thread.
197 Result PopulateFromIncomingCommandBuffer(const Kernel::KHandleTable& handle_table, 199 Result PopulateFromIncomingCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf);
198 u32_le* src_cmdbuf);
199 200
200 /// Writes data from this context back to the requesting process/thread. 201 /// Writes data from this context back to the requesting process/thread.
201 Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread); 202 Result WriteToOutgoingCommandBuffer(Kernel::KThread& requesting_thread);
@@ -358,6 +359,10 @@ public:
358 return *thread; 359 return *thread;
359 } 360 }
360 361
362 Kernel::KHandleTable& GetClientHandleTable() {
363 return *client_handle_table;
364 }
365
361 [[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const { 366 [[nodiscard]] std::shared_ptr<SessionRequestManager> GetManager() const {
362 return manager.lock(); 367 return manager.lock();
363 } 368 }
@@ -373,12 +378,12 @@ public:
373private: 378private:
374 friend class IPC::ResponseBuilder; 379 friend class IPC::ResponseBuilder;
375 380
376 void ParseCommandBuffer(const Kernel::KHandleTable& handle_table, u32_le* src_cmdbuf, 381 void ParseCommandBuffer(Kernel::KProcess& process, u32_le* src_cmdbuf, bool incoming);
377 bool incoming);
378 382
379 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; 383 std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
380 Kernel::KServerSession* server_session{}; 384 Kernel::KServerSession* server_session{};
381 Kernel::KThread* thread; 385 Kernel::KHandleTable* client_handle_table{};
386 Kernel::KThread* thread{};
382 387
383 std::vector<Handle> incoming_move_handles; 388 std::vector<Handle> incoming_move_handles;
384 std::vector<Handle> incoming_copy_handles; 389 std::vector<Handle> incoming_copy_handles;
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/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
index d7db24f42..75bf31e32 100644
--- a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -171,6 +171,7 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han
171 buffer->height = SharedBufferHeight; 171 buffer->height = SharedBufferHeight;
172 buffer->stride = SharedBufferBlockLinearStride; 172 buffer->stride = SharedBufferBlockLinearStride;
173 buffer->format = SharedBufferBlockLinearFormat; 173 buffer->format = SharedBufferBlockLinearFormat;
174 buffer->external_format = SharedBufferBlockLinearFormat;
174 buffer->buffer_id = handle; 175 buffer->buffer_id = handle;
175 buffer->offset = slot * SharedBufferSlotSize; 176 buffer->offset = slot * SharedBufferSlotSize;
176 ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError); 177 ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
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/server_manager.cpp b/src/core/hle/service/server_manager.cpp
index e2e399534..6808247a9 100644
--- a/src/core/hle/service/server_manager.cpp
+++ b/src/core/hle/service/server_manager.cpp
@@ -93,13 +93,13 @@ Result ServerManager::RegisterSession(Kernel::KServerSession* session,
93} 93}
94 94
95Result ServerManager::RegisterNamedService(const std::string& service_name, 95Result ServerManager::RegisterNamedService(const std::string& service_name,
96 std::shared_ptr<SessionRequestHandler>&& handler, 96 SessionRequestHandlerFactory&& handler_factory,
97 u32 max_sessions) { 97 u32 max_sessions) {
98 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); 98 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
99 99
100 // Add the new server to sm:. 100 // Add the new server to sm:.
101 ASSERT(R_SUCCEEDED( 101 ASSERT(R_SUCCEEDED(
102 m_system.ServiceManager().RegisterService(service_name, max_sessions, handler))); 102 m_system.ServiceManager().RegisterService(service_name, max_sessions, handler_factory)));
103 103
104 // Get the registered port. 104 // Get the registered port.
105 Kernel::KPort* port{}; 105 Kernel::KPort* port{};
@@ -112,7 +112,7 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
112 // Begin tracking the server port. 112 // Begin tracking the server port.
113 { 113 {
114 std::scoped_lock ll{m_list_mutex}; 114 std::scoped_lock ll{m_list_mutex};
115 m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler)); 115 m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory));
116 } 116 }
117 117
118 // Signal the wakeup event. 118 // Signal the wakeup event.
@@ -121,8 +121,18 @@ Result ServerManager::RegisterNamedService(const std::string& service_name,
121 R_SUCCEED(); 121 R_SUCCEED();
122} 122}
123 123
124Result ServerManager::RegisterNamedService(const std::string& service_name,
125 std::shared_ptr<SessionRequestHandler>&& handler,
126 u32 max_sessions) {
127 // Make the factory.
128 const auto HandlerFactory = [handler]() { return handler; };
129
130 // Register the service with the new factory.
131 R_RETURN(this->RegisterNamedService(service_name, std::move(HandlerFactory), max_sessions));
132}
133
124Result ServerManager::ManageNamedPort(const std::string& service_name, 134Result ServerManager::ManageNamedPort(const std::string& service_name,
125 std::shared_ptr<SessionRequestHandler>&& handler, 135 SessionRequestHandlerFactory&& handler_factory,
126 u32 max_sessions) { 136 u32 max_sessions) {
127 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects); 137 ASSERT(m_sessions.size() + m_ports.size() < MaximumWaitObjects);
128 138
@@ -149,7 +159,7 @@ Result ServerManager::ManageNamedPort(const std::string& service_name,
149 // Begin tracking the server port. 159 // Begin tracking the server port.
150 { 160 {
151 std::scoped_lock ll{m_list_mutex}; 161 std::scoped_lock ll{m_list_mutex};
152 m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler)); 162 m_ports.emplace(std::addressof(port->GetServerPort()), std::move(handler_factory));
153 } 163 }
154 164
155 // We succeeded. 165 // We succeeded.
@@ -269,13 +279,13 @@ Result ServerManager::WaitAndProcessImpl() {
269 case HandleType::Port: { 279 case HandleType::Port: {
270 // Port signaled. 280 // Port signaled.
271 auto* port = wait_obj->DynamicCast<Kernel::KServerPort*>(); 281 auto* port = wait_obj->DynamicCast<Kernel::KServerPort*>();
272 std::shared_ptr<SessionRequestHandler> handler; 282 SessionRequestHandlerFactory handler_factory;
273 283
274 // Remove from tracking. 284 // Remove from tracking.
275 { 285 {
276 std::scoped_lock ll{m_list_mutex}; 286 std::scoped_lock ll{m_list_mutex};
277 ASSERT(m_ports.contains(port)); 287 ASSERT(m_ports.contains(port));
278 m_ports.at(port).swap(handler); 288 m_ports.at(port).swap(handler_factory);
279 m_ports.erase(port); 289 m_ports.erase(port);
280 } 290 }
281 291
@@ -283,7 +293,7 @@ Result ServerManager::WaitAndProcessImpl() {
283 sl.unlock(); 293 sl.unlock();
284 294
285 // Finish. 295 // Finish.
286 R_RETURN(this->OnPortEvent(port, std::move(handler))); 296 R_RETURN(this->OnPortEvent(port, std::move(handler_factory)));
287 } 297 }
288 case HandleType::Session: { 298 case HandleType::Session: {
289 // Session signaled. 299 // Session signaled.
@@ -333,19 +343,19 @@ Result ServerManager::WaitAndProcessImpl() {
333} 343}
334 344
335Result ServerManager::OnPortEvent(Kernel::KServerPort* port, 345Result ServerManager::OnPortEvent(Kernel::KServerPort* port,
336 std::shared_ptr<SessionRequestHandler>&& handler) { 346 SessionRequestHandlerFactory&& handler_factory) {
337 // Accept a new server session. 347 // Accept a new server session.
338 Kernel::KServerSession* session = port->AcceptSession(); 348 Kernel::KServerSession* session = port->AcceptSession();
339 ASSERT(session != nullptr); 349 ASSERT(session != nullptr);
340 350
341 // Create the session manager and install the handler. 351 // Create the session manager and install the handler.
342 auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this); 352 auto manager = std::make_shared<SessionRequestManager>(m_system.Kernel(), *this);
343 manager->SetSessionHandler(std::shared_ptr(handler)); 353 manager->SetSessionHandler(handler_factory());
344 354
345 // Track the server session. 355 // Track the server session.
346 { 356 {
347 std::scoped_lock ll{m_list_mutex}; 357 std::scoped_lock ll{m_list_mutex};
348 m_ports.emplace(port, std::move(handler)); 358 m_ports.emplace(port, std::move(handler_factory));
349 m_sessions.emplace(session, std::move(manager)); 359 m_sessions.emplace(session, std::move(manager));
350 } 360 }
351 361
diff --git a/src/core/hle/service/server_manager.h b/src/core/hle/service/server_manager.h
index 58b0a0832..c4bc07262 100644
--- a/src/core/hle/service/server_manager.h
+++ b/src/core/hle/service/server_manager.h
@@ -13,6 +13,7 @@
13#include "common/polyfill_thread.h" 13#include "common/polyfill_thread.h"
14#include "common/thread.h" 14#include "common/thread.h"
15#include "core/hle/result.h" 15#include "core/hle/result.h"
16#include "core/hle/service/hle_ipc.h"
16#include "core/hle/service/mutex.h" 17#include "core/hle/service/mutex.h"
17 18
18namespace Core { 19namespace Core {
@@ -28,10 +29,6 @@ class KSynchronizationObject;
28 29
29namespace Service { 30namespace Service {
30 31
31class HLERequestContext;
32class SessionRequestHandler;
33class SessionRequestManager;
34
35class ServerManager { 32class ServerManager {
36public: 33public:
37 explicit ServerManager(Core::System& system); 34 explicit ServerManager(Core::System& system);
@@ -40,10 +37,13 @@ public:
40 Result RegisterSession(Kernel::KServerSession* session, 37 Result RegisterSession(Kernel::KServerSession* session,
41 std::shared_ptr<SessionRequestManager> manager); 38 std::shared_ptr<SessionRequestManager> manager);
42 Result RegisterNamedService(const std::string& service_name, 39 Result RegisterNamedService(const std::string& service_name,
40 SessionRequestHandlerFactory&& handler_factory,
41 u32 max_sessions = 64);
42 Result RegisterNamedService(const std::string& service_name,
43 std::shared_ptr<SessionRequestHandler>&& handler, 43 std::shared_ptr<SessionRequestHandler>&& handler,
44 u32 max_sessions = 64); 44 u32 max_sessions = 64);
45 Result ManageNamedPort(const std::string& service_name, 45 Result ManageNamedPort(const std::string& service_name,
46 std::shared_ptr<SessionRequestHandler>&& handler, u32 max_sessions = 64); 46 SessionRequestHandlerFactory&& handler_factory, u32 max_sessions = 64);
47 Result ManageDeferral(Kernel::KEvent** out_event); 47 Result ManageDeferral(Kernel::KEvent** out_event);
48 48
49 Result LoopProcess(); 49 Result LoopProcess();
@@ -56,7 +56,7 @@ private:
56 56
57 Result LoopProcessImpl(); 57 Result LoopProcessImpl();
58 Result WaitAndProcessImpl(); 58 Result WaitAndProcessImpl();
59 Result OnPortEvent(Kernel::KServerPort* port, std::shared_ptr<SessionRequestHandler>&& handler); 59 Result OnPortEvent(Kernel::KServerPort* port, SessionRequestHandlerFactory&& handler_factory);
60 Result OnSessionEvent(Kernel::KServerSession* session, 60 Result OnSessionEvent(Kernel::KServerSession* session,
61 std::shared_ptr<SessionRequestManager>&& manager); 61 std::shared_ptr<SessionRequestManager>&& manager);
62 Result OnDeferralEvent(std::list<RequestState>&& deferrals); 62 Result OnDeferralEvent(std::list<RequestState>&& deferrals);
@@ -68,7 +68,7 @@ private:
68 std::mutex m_list_mutex; 68 std::mutex m_list_mutex;
69 69
70 // Guest state tracking 70 // Guest state tracking
71 std::map<Kernel::KServerPort*, std::shared_ptr<SessionRequestHandler>> m_ports{}; 71 std::map<Kernel::KServerPort*, SessionRequestHandlerFactory> m_ports{};
72 std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{}; 72 std::map<Kernel::KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{};
73 Kernel::KEvent* m_event{}; 73 Kernel::KEvent* m_event{};
74 Kernel::KEvent* m_deferral_event{}; 74 Kernel::KEvent* m_deferral_event{};
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); });
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 9ab718e0a..296ee6e89 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -51,7 +51,7 @@ static Result ValidateServiceName(const std::string& name) {
51} 51}
52 52
53Result ServiceManager::RegisterService(std::string name, u32 max_sessions, 53Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
54 SessionRequestHandlerPtr handler) { 54 SessionRequestHandlerFactory handler) {
55 R_TRY(ValidateServiceName(name)); 55 R_TRY(ValidateServiceName(name));
56 56
57 std::scoped_lock lk{lock}; 57 std::scoped_lock lk{lock};
@@ -121,7 +121,7 @@ void SM::Initialize(HLERequestContext& ctx) {
121 rb.Push(ResultSuccess); 121 rb.Push(ResultSuccess);
122} 122}
123 123
124void SM::GetService(HLERequestContext& ctx) { 124void SM::GetServiceCmif(HLERequestContext& ctx) {
125 Kernel::KClientSession* client_session{}; 125 Kernel::KClientSession* client_session{};
126 auto result = GetServiceImpl(&client_session, ctx); 126 auto result = GetServiceImpl(&client_session, ctx);
127 if (ctx.GetIsDeferred()) { 127 if (ctx.GetIsDeferred()) {
@@ -192,19 +192,32 @@ Result SM::GetServiceImpl(Kernel::KClientSession** out_client_session, HLEReques
192 return result; 192 return result;
193 } 193 }
194 194
195 LOG_DEBUG(Service_SM, "called service={} -> session={}", name, session->GetId());
196
197 *out_client_session = session; 195 *out_client_session = session;
198 return ResultSuccess; 196 return ResultSuccess;
199} 197}
200 198
201void SM::RegisterService(HLERequestContext& ctx) { 199void SM::RegisterServiceCmif(HLERequestContext& ctx) {
202 IPC::RequestParser rp{ctx}; 200 IPC::RequestParser rp{ctx};
203 std::string name(PopServiceName(rp)); 201 std::string name(PopServiceName(rp));
204 202
205 const auto is_light = static_cast<bool>(rp.PopRaw<u32>()); 203 const auto is_light = static_cast<bool>(rp.PopRaw<u32>());
206 const auto max_session_count = rp.PopRaw<u32>(); 204 const auto max_session_count = rp.PopRaw<u32>();
207 205
206 this->RegisterServiceImpl(ctx, name, max_session_count, is_light);
207}
208
209void SM::RegisterServiceTipc(HLERequestContext& ctx) {
210 IPC::RequestParser rp{ctx};
211 std::string name(PopServiceName(rp));
212
213 const auto max_session_count = rp.PopRaw<u32>();
214 const auto is_light = static_cast<bool>(rp.PopRaw<u32>());
215
216 this->RegisterServiceImpl(ctx, name, max_session_count, is_light);
217}
218
219void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_session_count,
220 bool is_light) {
208 LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name, 221 LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
209 max_session_count, is_light); 222 max_session_count, is_light);
210 223
@@ -240,15 +253,15 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
240 service_manager{service_manager_}, kernel{system_.Kernel()} { 253 service_manager{service_manager_}, kernel{system_.Kernel()} {
241 RegisterHandlers({ 254 RegisterHandlers({
242 {0, &SM::Initialize, "Initialize"}, 255 {0, &SM::Initialize, "Initialize"},
243 {1, &SM::GetService, "GetService"}, 256 {1, &SM::GetServiceCmif, "GetService"},
244 {2, &SM::RegisterService, "RegisterService"}, 257 {2, &SM::RegisterServiceCmif, "RegisterService"},
245 {3, &SM::UnregisterService, "UnregisterService"}, 258 {3, &SM::UnregisterService, "UnregisterService"},
246 {4, nullptr, "DetachClient"}, 259 {4, nullptr, "DetachClient"},
247 }); 260 });
248 RegisterHandlersTipc({ 261 RegisterHandlersTipc({
249 {0, &SM::Initialize, "Initialize"}, 262 {0, &SM::Initialize, "Initialize"},
250 {1, &SM::GetServiceTipc, "GetService"}, 263 {1, &SM::GetServiceTipc, "GetService"},
251 {2, &SM::RegisterService, "RegisterService"}, 264 {2, &SM::RegisterServiceTipc, "RegisterService"},
252 {3, &SM::UnregisterService, "UnregisterService"}, 265 {3, &SM::UnregisterService, "UnregisterService"},
253 {4, nullptr, "DetachClient"}, 266 {4, nullptr, "DetachClient"},
254 }); 267 });
@@ -264,7 +277,9 @@ void LoopProcess(Core::System& system) {
264 server_manager->ManageDeferral(&deferral_event); 277 server_manager->ManageDeferral(&deferral_event);
265 service_manager.SetDeferralEvent(deferral_event); 278 service_manager.SetDeferralEvent(deferral_event);
266 279
267 server_manager->ManageNamedPort("sm:", std::make_shared<SM>(system.ServiceManager(), system)); 280 auto sm_service = std::make_shared<SM>(system.ServiceManager(), system);
281 server_manager->ManageNamedPort("sm:", [sm_service] { return sm_service; });
282
268 ServerManager::RunServer(std::move(server_manager)); 283 ServerManager::RunServer(std::move(server_manager));
269} 284}
270 285
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 14bfaf8c2..ff74f588a 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -37,12 +37,15 @@ public:
37 37
38private: 38private:
39 void Initialize(HLERequestContext& ctx); 39 void Initialize(HLERequestContext& ctx);
40 void GetService(HLERequestContext& ctx); 40 void GetServiceCmif(HLERequestContext& ctx);
41 void GetServiceTipc(HLERequestContext& ctx); 41 void GetServiceTipc(HLERequestContext& ctx);
42 void RegisterService(HLERequestContext& ctx); 42 void RegisterServiceCmif(HLERequestContext& ctx);
43 void RegisterServiceTipc(HLERequestContext& ctx);
43 void UnregisterService(HLERequestContext& ctx); 44 void UnregisterService(HLERequestContext& ctx);
44 45
45 Result GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx); 46 Result GetServiceImpl(Kernel::KClientSession** out_client_session, HLERequestContext& ctx);
47 void RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_session_count,
48 bool is_light);
46 49
47 ServiceManager& service_manager; 50 ServiceManager& service_manager;
48 Kernel::KernelCore& kernel; 51 Kernel::KernelCore& kernel;
@@ -53,7 +56,8 @@ public:
53 explicit ServiceManager(Kernel::KernelCore& kernel_); 56 explicit ServiceManager(Kernel::KernelCore& kernel_);
54 ~ServiceManager(); 57 ~ServiceManager();
55 58
56 Result RegisterService(std::string name, u32 max_sessions, SessionRequestHandlerPtr handler); 59 Result RegisterService(std::string name, u32 max_sessions,
60 SessionRequestHandlerFactory handler_factory);
57 Result UnregisterService(const std::string& name); 61 Result UnregisterService(const std::string& name);
58 Result GetServicePort(Kernel::KPort** out_port, const std::string& name); 62 Result GetServicePort(Kernel::KPort** out_port, const std::string& name);
59 63
@@ -64,7 +68,7 @@ public:
64 LOG_DEBUG(Service, "Can't find service: {}", service_name); 68 LOG_DEBUG(Service, "Can't find service: {}", service_name);
65 return nullptr; 69 return nullptr;
66 } 70 }
67 return std::static_pointer_cast<T>(service->second); 71 return std::static_pointer_cast<T>(service->second());
68 } 72 }
69 73
70 void InvokeControlRequest(HLERequestContext& context); 74 void InvokeControlRequest(HLERequestContext& context);
@@ -79,7 +83,7 @@ private:
79 83
80 /// Map of registered services, retrieved using GetServicePort. 84 /// Map of registered services, retrieved using GetServicePort.
81 std::mutex lock; 85 std::mutex lock;
82 std::unordered_map<std::string, SessionRequestHandlerPtr> registered_services; 86 std::unordered_map<std::string, SessionRequestHandlerFactory> registered_services;
83 std::unordered_map<std::string, Kernel::KPort*> service_ports; 87 std::unordered_map<std::string, Kernel::KPort*> service_ports;
84 88
85 /// Kernel context 89 /// Kernel context