summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/core.cpp34
-rw-r--r--src/core/core.h4
-rw-r--r--src/core/core_cpu.cpp9
-rw-r--r--src/core/core_cpu.h8
-rw-r--r--src/core/cpu_core_manager.cpp3
-rw-r--r--src/core/crypto/key_manager.cpp3
-rw-r--r--src/core/file_sys/vfs_vector.cpp2
-rw-r--r--src/core/frontend/emu_window.cpp2
-rw-r--r--src/core/frontend/emu_window.h2
-rw-r--r--src/core/frontend/framebuffer_layout.cpp12
-rw-r--r--src/core/frontend/framebuffer_layout.h2
-rw-r--r--src/core/frontend/input.h2
-rw-r--r--src/core/hle/ipc.h4
-rw-r--r--src/core/hle/ipc_helpers.h20
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp181
-rw-r--r--src/core/hle/kernel/address_arbiter.h80
-rw-r--r--src/core/hle/kernel/client_port.cpp9
-rw-r--r--src/core/hle/kernel/client_session.cpp14
-rw-r--r--src/core/hle/kernel/client_session.h9
-rw-r--r--src/core/hle/kernel/errors.h1
-rw-r--r--src/core/hle/kernel/handle_table.cpp40
-rw-r--r--src/core/hle/kernel/handle_table.h25
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp22
-rw-r--r--src/core/hle/kernel/hle_ipc.h25
-rw-r--r--src/core/hle/kernel/kernel.cpp20
-rw-r--r--src/core/hle/kernel/kernel.h20
-rw-r--r--src/core/hle/kernel/process.cpp26
-rw-r--r--src/core/hle/kernel/process.h26
-rw-r--r--src/core/hle/kernel/process_capability.cpp4
-rw-r--r--src/core/hle/kernel/process_capability.h4
-rw-r--r--src/core/hle/kernel/scheduler.cpp20
-rw-r--r--src/core/hle/kernel/scheduler.h6
-rw-r--r--src/core/hle/kernel/server_port.cpp4
-rw-r--r--src/core/hle/kernel/server_port.h35
-rw-r--r--src/core/hle/kernel/server_session.cpp91
-rw-r--r--src/core/hle/kernel/server_session.h53
-rw-r--r--src/core/hle/kernel/shared_memory.cpp5
-rw-r--r--src/core/hle/kernel/svc.cpp95
-rw-r--r--src/core/hle/kernel/thread.cpp15
-rw-r--r--src/core/hle/kernel/thread.h13
-rw-r--r--src/core/hle/kernel/vm_manager.cpp47
-rw-r--r--src/core/hle/kernel/vm_manager.h24
-rw-r--r--src/core/hle/result.h19
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp1
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h3
-rw-r--r--src/core/hle/service/audio/audout_u.cpp17
-rw-r--r--src/core/hle/service/audio/audren_u.cpp64
-rw-r--r--src/core/hle/service/audio/audren_u.h3
-rw-r--r--src/core/hle/service/audio/errors.h15
-rw-r--r--src/core/hle/service/audio/hwopus.cpp176
-rw-r--r--src/core/hle/service/hid/hid.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp14
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp2
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h4
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp102
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h50
-rw-r--r--src/core/hle/service/service.cpp10
-rw-r--r--src/core/hle/service/sm/controller.cpp2
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp71
-rw-r--r--src/core/hle/service/vi/display/vi_display.h98
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp13
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h52
-rw-r--r--src/core/hle/service/vi/vi.cpp67
-rw-r--r--src/core/hle/service/vi/vi.h40
-rw-r--r--src/core/hle/service/vi/vi_m.cpp12
-rw-r--r--src/core/hle/service/vi/vi_m.h19
-rw-r--r--src/core/hle/service/vi/vi_s.cpp12
-rw-r--r--src/core/hle/service/vi/vi_s.h19
-rw-r--r--src/core/hle/service/vi/vi_u.cpp12
-rw-r--r--src/core/hle/service/vi/vi_u.h19
-rw-r--r--src/core/memory.cpp38
-rw-r--r--src/core/settings.cpp3
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp2
79 files changed, 1306 insertions, 693 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f61bcd40d..8ccb2d5f0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -217,6 +217,7 @@ add_library(core STATIC
217 hle/service/audio/audren_u.h 217 hle/service/audio/audren_u.h
218 hle/service/audio/codecctl.cpp 218 hle/service/audio/codecctl.cpp
219 hle/service/audio/codecctl.h 219 hle/service/audio/codecctl.h
220 hle/service/audio/errors.h
220 hle/service/audio/hwopus.cpp 221 hle/service/audio/hwopus.cpp
221 hle/service/audio/hwopus.h 222 hle/service/audio/hwopus.h
222 hle/service/bcat/bcat.cpp 223 hle/service/bcat/bcat.cpp
@@ -400,6 +401,10 @@ add_library(core STATIC
400 hle/service/time/time.h 401 hle/service/time/time.h
401 hle/service/usb/usb.cpp 402 hle/service/usb/usb.cpp
402 hle/service/usb/usb.h 403 hle/service/usb/usb.h
404 hle/service/vi/display/vi_display.cpp
405 hle/service/vi/display/vi_display.h
406 hle/service/vi/layer/vi_layer.cpp
407 hle/service/vi/layer/vi_layer.h
403 hle/service/vi/vi.cpp 408 hle/service/vi/vi.cpp
404 hle/service/vi/vi.h 409 hle/service/vi/vi.h
405 hle/service/vi/vi_m.cpp 410 hle/service/vi/vi_m.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 8aa0932c5..89b3fb418 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -36,7 +36,8 @@
36#include "frontend/applets/software_keyboard.h" 36#include "frontend/applets/software_keyboard.h"
37#include "frontend/applets/web_browser.h" 37#include "frontend/applets/web_browser.h"
38#include "video_core/debug_utils/debug_utils.h" 38#include "video_core/debug_utils/debug_utils.h"
39#include "video_core/gpu.h" 39#include "video_core/gpu_asynch.h"
40#include "video_core/gpu_synch.h"
40#include "video_core/renderer_base.h" 41#include "video_core/renderer_base.h"
41#include "video_core/video_core.h" 42#include "video_core/video_core.h"
42 43
@@ -78,6 +79,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
78 return vfs->OpenFile(path, FileSys::Mode::Read); 79 return vfs->OpenFile(path, FileSys::Mode::Read);
79} 80}
80struct System::Impl { 81struct System::Impl {
82 explicit Impl(System& system) : kernel{system} {}
81 83
82 Cpu& CurrentCpuCore() { 84 Cpu& CurrentCpuCore() {
83 return cpu_core_manager.GetCurrentCore(); 85 return cpu_core_manager.GetCurrentCore();
@@ -95,7 +97,7 @@ struct System::Impl {
95 LOG_DEBUG(HW_Memory, "initialized OK"); 97 LOG_DEBUG(HW_Memory, "initialized OK");
96 98
97 core_timing.Initialize(); 99 core_timing.Initialize();
98 kernel.Initialize(core_timing); 100 kernel.Initialize();
99 101
100 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>( 102 const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
101 std::chrono::system_clock::now().time_since_epoch()); 103 std::chrono::system_clock::now().time_since_epoch());
@@ -114,7 +116,7 @@ struct System::Impl {
114 if (web_browser == nullptr) 116 if (web_browser == nullptr)
115 web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); 117 web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
116 118
117 auto main_process = Kernel::Process::Create(kernel, "main"); 119 auto main_process = Kernel::Process::Create(system, "main");
118 kernel.MakeCurrentProcess(main_process.get()); 120 kernel.MakeCurrentProcess(main_process.get());
119 121
120 telemetry_session = std::make_unique<Core::TelemetrySession>(); 122 telemetry_session = std::make_unique<Core::TelemetrySession>();
@@ -128,10 +130,16 @@ struct System::Impl {
128 return ResultStatus::ErrorVideoCore; 130 return ResultStatus::ErrorVideoCore;
129 } 131 }
130 132
131 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); 133 is_powered_on = true;
134
135 if (Settings::values.use_asynchronous_gpu_emulation) {
136 gpu_core = std::make_unique<VideoCommon::GPUAsynch>(system, *renderer);
137 } else {
138 gpu_core = std::make_unique<VideoCommon::GPUSynch>(system, *renderer);
139 }
132 140
133 cpu_core_manager.Initialize(system); 141 cpu_core_manager.Initialize(system);
134 is_powered_on = true; 142
135 LOG_DEBUG(Core, "Initialized OK"); 143 LOG_DEBUG(Core, "Initialized OK");
136 144
137 // Reset counters and set time origin to current frame 145 // Reset counters and set time origin to current frame
@@ -182,13 +190,13 @@ struct System::Impl {
182 190
183 void Shutdown() { 191 void Shutdown() {
184 // Log last frame performance stats 192 // Log last frame performance stats
185 auto perf_results = GetAndResetPerfStats(); 193 const auto perf_results = GetAndResetPerfStats();
186 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed", 194 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
187 perf_results.emulation_speed * 100.0); 195 perf_results.emulation_speed * 100.0);
188 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", 196 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
189 perf_results.game_fps); 197 perf_results.game_fps);
190 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 198 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
191 perf_results.frametime * 1000.0); 199 perf_results.frametime * 1000.0);
192 200
193 is_powered_on = false; 201 is_powered_on = false;
194 202
@@ -265,7 +273,7 @@ struct System::Impl {
265 Core::FrameLimiter frame_limiter; 273 Core::FrameLimiter frame_limiter;
266}; 274};
267 275
268System::System() : impl{std::make_unique<Impl>()} {} 276System::System() : impl{std::make_unique<Impl>(*this)} {}
269System::~System() = default; 277System::~System() = default;
270 278
271Cpu& System::CurrentCpuCore() { 279Cpu& System::CurrentCpuCore() {
diff --git a/src/core/core.h b/src/core/core.h
index d720013f7..ba76a41d8 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -293,10 +293,6 @@ inline ARM_Interface& CurrentArmInterface() {
293 return System::GetInstance().CurrentArmInterface(); 293 return System::GetInstance().CurrentArmInterface();
294} 294}
295 295
296inline TelemetrySession& Telemetry() {
297 return System::GetInstance().TelemetrySession();
298}
299
300inline Kernel::Process* CurrentProcess() { 296inline Kernel::Process* CurrentProcess() {
301 return System::GetInstance().CurrentProcess(); 297 return System::GetInstance().CurrentProcess();
302} 298}
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index 54aa21a3a..1eefed6d0 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -11,6 +11,7 @@
11#endif 11#endif
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/arm/unicorn/arm_unicorn.h" 13#include "core/arm/unicorn/arm_unicorn.h"
14#include "core/core.h"
14#include "core/core_cpu.h" 15#include "core/core_cpu.h"
15#include "core/core_timing.h" 16#include "core/core_timing.h"
16#include "core/hle/kernel/scheduler.h" 17#include "core/hle/kernel/scheduler.h"
@@ -49,9 +50,9 @@ bool CpuBarrier::Rendezvous() {
49 return false; 50 return false;
50} 51}
51 52
52Cpu::Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, 53Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
53 CpuBarrier& cpu_barrier, std::size_t core_index) 54 std::size_t core_index)
54 : cpu_barrier{cpu_barrier}, core_timing{core_timing}, core_index{core_index} { 55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
55 if (Settings::values.use_cpu_jit) { 56 if (Settings::values.use_cpu_jit) {
56#ifdef ARCHITECTURE_x86_64 57#ifdef ARCHITECTURE_x86_64
57 arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index); 58 arm_interface = std::make_unique<ARM_Dynarmic>(core_timing, exclusive_monitor, core_index);
@@ -63,7 +64,7 @@ Cpu::Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor,
63 arm_interface = std::make_unique<ARM_Unicorn>(core_timing); 64 arm_interface = std::make_unique<ARM_Unicorn>(core_timing);
64 } 65 }
65 66
66 scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface); 67 scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
67} 68}
68 69
69Cpu::~Cpu() = default; 70Cpu::~Cpu() = default;
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h
index e2204c6b0..7589beb8c 100644
--- a/src/core/core_cpu.h
+++ b/src/core/core_cpu.h
@@ -15,6 +15,10 @@ namespace Kernel {
15class Scheduler; 15class Scheduler;
16} 16}
17 17
18namespace Core {
19class System;
20}
21
18namespace Core::Timing { 22namespace Core::Timing {
19class CoreTiming; 23class CoreTiming;
20} 24}
@@ -45,8 +49,8 @@ private:
45 49
46class Cpu { 50class Cpu {
47public: 51public:
48 Cpu(Timing::CoreTiming& core_timing, ExclusiveMonitor& exclusive_monitor, 52 Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
49 CpuBarrier& cpu_barrier, std::size_t core_index); 53 std::size_t core_index);
50 ~Cpu(); 54 ~Cpu();
51 55
52 void RunLoop(bool tight_loop = true); 56 void RunLoop(bool tight_loop = true);
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
index 2ddb3610d..93bc5619c 100644
--- a/src/core/cpu_core_manager.cpp
+++ b/src/core/cpu_core_manager.cpp
@@ -27,8 +27,7 @@ void CpuCoreManager::Initialize(System& system) {
27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size()); 27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
28 28
29 for (std::size_t index = 0; index < cores.size(); ++index) { 29 for (std::size_t index = 0; index < cores.size(); ++index) {
30 cores[index] = 30 cores[index] = std::make_unique<Cpu>(system, *exclusive_monitor, *barrier, index);
31 std::make_unique<Cpu>(system.CoreTiming(), *exclusive_monitor, *barrier, index);
32 } 31 }
33 32
34 // Create threads for CPU cores 1-3, and build thread_to_cpu map 33 // Create threads for CPU cores 1-3, and build thread_to_cpu map
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index ca12fb4ab..dfac9a4b3 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -398,7 +398,8 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_
398} 398}
399 399
400void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { 400void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
401 std::ifstream file(filename); 401 std::ifstream file;
402 OpenFStream(file, filename, std::ios_base::in);
402 if (!file.is_open()) 403 if (!file.is_open())
403 return; 404 return;
404 405
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index 515626658..75fc04302 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -47,7 +47,7 @@ std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_
47 if (offset + length > data.size()) 47 if (offset + length > data.size())
48 data.resize(offset + length); 48 data.resize(offset + length);
49 const auto write = std::min(length, data.size() - offset); 49 const auto write = std::min(length, data.size() - offset);
50 std::memcpy(data.data(), data_, write); 50 std::memcpy(data.data() + offset, data_, write);
51 return write; 51 return write;
52} 52}
53 53
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 9dd493efb..e29afd630 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -67,7 +67,7 @@ static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigne
67 framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right); 67 framebuffer_x >= layout.screen.left && framebuffer_x < layout.screen.right);
68} 68}
69 69
70std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) { 70std::tuple<unsigned, unsigned> EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const {
71 new_x = std::max(new_x, framebuffer_layout.screen.left); 71 new_x = std::max(new_x, framebuffer_layout.screen.left);
72 new_x = std::min(new_x, framebuffer_layout.screen.right - 1); 72 new_x = std::min(new_x, framebuffer_layout.screen.right - 1);
73 73
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index 7006a37b3..d0bcb4660 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -166,7 +166,7 @@ private:
166 /** 166 /**
167 * Clip the provided coordinates to be inside the touchscreen area. 167 * Clip the provided coordinates to be inside the touchscreen area.
168 */ 168 */
169 std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y); 169 std::tuple<unsigned, unsigned> ClipToTouchScreen(unsigned new_x, unsigned new_y) const;
170}; 170};
171 171
172} // namespace Core::Frontend 172} // namespace Core::Frontend
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index f8662d193..a1357179f 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -12,12 +12,12 @@ namespace Layout {
12 12
13// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio 13// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio
14template <class T> 14template <class T>
15static MathUtil::Rectangle<T> maxRectangle(MathUtil::Rectangle<T> window_area, 15static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
16 float screen_aspect_ratio) { 16 float screen_aspect_ratio) {
17 float scale = std::min(static_cast<float>(window_area.GetWidth()), 17 float scale = std::min(static_cast<float>(window_area.GetWidth()),
18 window_area.GetHeight() / screen_aspect_ratio); 18 window_area.GetHeight() / screen_aspect_ratio);
19 return MathUtil::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)), 19 return Common::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)),
20 static_cast<T>(std::round(scale * screen_aspect_ratio))}; 20 static_cast<T>(std::round(scale * screen_aspect_ratio))};
21} 21}
22 22
23FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) { 23FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
@@ -29,8 +29,8 @@ FramebufferLayout DefaultFrameLayout(unsigned width, unsigned height) {
29 29
30 const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) / 30 const float emulation_aspect_ratio{static_cast<float>(ScreenUndocked::Height) /
31 ScreenUndocked::Width}; 31 ScreenUndocked::Width};
32 MathUtil::Rectangle<unsigned> screen_window_area{0, 0, width, height}; 32 Common::Rectangle<unsigned> screen_window_area{0, 0, width, height};
33 MathUtil::Rectangle<unsigned> screen = maxRectangle(screen_window_area, emulation_aspect_ratio); 33 Common::Rectangle<unsigned> screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
34 34
35 float window_aspect_ratio = static_cast<float>(height) / width; 35 float window_aspect_ratio = static_cast<float>(height) / width;
36 36
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index e06647794..c2c63d08c 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -16,7 +16,7 @@ struct FramebufferLayout {
16 unsigned width{ScreenUndocked::Width}; 16 unsigned width{ScreenUndocked::Width};
17 unsigned height{ScreenUndocked::Height}; 17 unsigned height{ScreenUndocked::Height};
18 18
19 MathUtil::Rectangle<unsigned> screen; 19 Common::Rectangle<unsigned> screen;
20 20
21 /** 21 /**
22 * Returns the ration of pixel size of the screen, compared to the native size of the undocked 22 * Returns the ration of pixel size of the screen, compared to the native size of the undocked
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 16fdcd376..7c11d7546 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -124,7 +124,7 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>;
124 * Orientation is determined by right-hand rule. 124 * Orientation is determined by right-hand rule.
125 * Units: deg/sec 125 * Units: deg/sec
126 */ 126 */
127using MotionDevice = InputDevice<std::tuple<Math::Vec3<float>, Math::Vec3<float>>>; 127using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>;
128 128
129/** 129/**
130 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are 130 * A touch device is an input device that returns a tuple of two floats and a bool. The floats are
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index ed84197b3..455d1f346 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -4,10 +4,10 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
8#include "common/common_funcs.h"
7#include "common/common_types.h" 9#include "common/common_types.h"
8#include "common/swap.h" 10#include "common/swap.h"
9#include "core/hle/kernel/errors.h"
10#include "core/memory.h"
11 11
12namespace IPC { 12namespace IPC {
13 13
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 90f276ee8..a1e4be070 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -19,9 +19,12 @@
19#include "core/hle/kernel/hle_ipc.h" 19#include "core/hle/kernel/hle_ipc.h"
20#include "core/hle/kernel/object.h" 20#include "core/hle/kernel/object.h"
21#include "core/hle/kernel/server_session.h" 21#include "core/hle/kernel/server_session.h"
22#include "core/hle/result.h"
22 23
23namespace IPC { 24namespace IPC {
24 25
26constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
27
25class RequestHelperBase { 28class RequestHelperBase {
26protected: 29protected:
27 Kernel::HLERequestContext* context = nullptr; 30 Kernel::HLERequestContext* context = nullptr;
@@ -350,7 +353,7 @@ public:
350 template <class T> 353 template <class T>
351 std::shared_ptr<T> PopIpcInterface() { 354 std::shared_ptr<T> PopIpcInterface() {
352 ASSERT(context->Session()->IsDomain()); 355 ASSERT(context->Session()->IsDomain());
353 ASSERT(context->GetDomainMessageHeader()->input_object_count > 0); 356 ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
354 return context->GetDomainRequestHandler<T>(Pop<u32>() - 1); 357 return context->GetDomainRequestHandler<T>(Pop<u32>() - 1);
355 } 358 }
356}; 359};
@@ -362,6 +365,11 @@ inline u32 RequestParser::Pop() {
362 return cmdbuf[index++]; 365 return cmdbuf[index++];
363} 366}
364 367
368template <>
369inline s32 RequestParser::Pop() {
370 return static_cast<s32>(Pop<u32>());
371}
372
365template <typename T> 373template <typename T>
366void RequestParser::PopRaw(T& value) { 374void RequestParser::PopRaw(T& value) {
367 std::memcpy(&value, cmdbuf + index, sizeof(T)); 375 std::memcpy(&value, cmdbuf + index, sizeof(T));
@@ -393,6 +401,16 @@ inline u64 RequestParser::Pop() {
393} 401}
394 402
395template <> 403template <>
404inline s8 RequestParser::Pop() {
405 return static_cast<s8>(Pop<u8>());
406}
407
408template <>
409inline s16 RequestParser::Pop() {
410 return static_cast<s16>(Pop<u16>());
411}
412
413template <>
396inline s64 RequestParser::Pop() { 414inline s64 RequestParser::Pop() {
397 return static_cast<s64>(Pop<u64>()); 415 return static_cast<s64>(Pop<u64>());
398} 416}
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp
index 57157beb4..352190da8 100644
--- a/src/core/hle/kernel/address_arbiter.cpp
+++ b/src/core/hle/kernel/address_arbiter.cpp
@@ -9,6 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/core_cpu.h" 11#include "core/core_cpu.h"
12#include "core/hle/kernel/address_arbiter.h"
12#include "core/hle/kernel/errors.h" 13#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
14#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
@@ -18,58 +19,15 @@
18#include "core/memory.h" 19#include "core/memory.h"
19 20
20namespace Kernel { 21namespace Kernel {
21namespace AddressArbiter { 22namespace {
22
23// Performs actual address waiting logic.
24static ResultCode WaitForAddress(VAddr address, s64 timeout) {
25 SharedPtr<Thread> current_thread = GetCurrentThread();
26 current_thread->SetArbiterWaitAddress(address);
27 current_thread->SetStatus(ThreadStatus::WaitArb);
28 current_thread->InvalidateWakeupCallback();
29
30 current_thread->WakeAfterDelay(timeout);
31
32 Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
33 return RESULT_TIMEOUT;
34}
35
36// Gets the threads waiting on an address.
37static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) {
38 const auto RetrieveWaitingThreads = [](std::size_t core_index,
39 std::vector<SharedPtr<Thread>>& waiting_threads,
40 VAddr arb_addr) {
41 const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
42 const auto& thread_list = scheduler.GetThreadList();
43
44 for (const auto& thread : thread_list) {
45 if (thread->GetArbiterWaitAddress() == arb_addr)
46 waiting_threads.push_back(thread);
47 }
48 };
49
50 // Retrieve all threads that are waiting for this address.
51 std::vector<SharedPtr<Thread>> threads;
52 RetrieveWaitingThreads(0, threads, address);
53 RetrieveWaitingThreads(1, threads, address);
54 RetrieveWaitingThreads(2, threads, address);
55 RetrieveWaitingThreads(3, threads, address);
56
57 // Sort them by priority, such that the highest priority ones come first.
58 std::sort(threads.begin(), threads.end(),
59 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
60 return lhs->GetPriority() < rhs->GetPriority();
61 });
62
63 return threads;
64}
65
66// Wake up num_to_wake (or all) threads in a vector. 23// Wake up num_to_wake (or all) threads in a vector.
67static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { 24void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
68 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process 25 // Only process up to 'target' threads, unless 'target' is <= 0, in which case process
69 // them all. 26 // them all.
70 std::size_t last = waiting_threads.size(); 27 std::size_t last = waiting_threads.size();
71 if (num_to_wake > 0) 28 if (num_to_wake > 0) {
72 last = num_to_wake; 29 last = num_to_wake;
30 }
73 31
74 // Signal the waiting threads. 32 // Signal the waiting threads.
75 for (std::size_t i = 0; i < last; i++) { 33 for (std::size_t i = 0; i < last; i++) {
@@ -79,42 +37,55 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
79 waiting_threads[i]->ResumeFromWait(); 37 waiting_threads[i]->ResumeFromWait();
80 } 38 }
81} 39}
40} // Anonymous namespace
41
42AddressArbiter::AddressArbiter(Core::System& system) : system{system} {}
43AddressArbiter::~AddressArbiter() = default;
44
45ResultCode AddressArbiter::SignalToAddress(VAddr address, SignalType type, s32 value,
46 s32 num_to_wake) {
47 switch (type) {
48 case SignalType::Signal:
49 return SignalToAddressOnly(address, num_to_wake);
50 case SignalType::IncrementAndSignalIfEqual:
51 return IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
52 case SignalType::ModifyByWaitingCountAndSignalIfEqual:
53 return ModifyByWaitingCountAndSignalToAddressIfEqual(address, value, num_to_wake);
54 default:
55 return ERR_INVALID_ENUM_VALUE;
56 }
57}
82 58
83// Signals an address being waited on. 59ResultCode AddressArbiter::SignalToAddressOnly(VAddr address, s32 num_to_wake) {
84ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { 60 const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
85 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
86
87 WakeThreads(waiting_threads, num_to_wake); 61 WakeThreads(waiting_threads, num_to_wake);
88 return RESULT_SUCCESS; 62 return RESULT_SUCCESS;
89} 63}
90 64
91// Signals an address being waited on and increments its value if equal to the value argument. 65ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 value,
92ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake) { 66 s32 num_to_wake) {
93 // Ensure that we can write to the address. 67 // Ensure that we can write to the address.
94 if (!Memory::IsValidVirtualAddress(address)) { 68 if (!Memory::IsValidVirtualAddress(address)) {
95 return ERR_INVALID_ADDRESS_STATE; 69 return ERR_INVALID_ADDRESS_STATE;
96 } 70 }
97 71
98 if (static_cast<s32>(Memory::Read32(address)) == value) { 72 if (static_cast<s32>(Memory::Read32(address)) != value) {
99 Memory::Write32(address, static_cast<u32>(value + 1));
100 } else {
101 return ERR_INVALID_STATE; 73 return ERR_INVALID_STATE;
102 } 74 }
103 75
104 return SignalToAddress(address, num_to_wake); 76 Memory::Write32(address, static_cast<u32>(value + 1));
77 return SignalToAddressOnly(address, num_to_wake);
105} 78}
106 79
107// Signals an address being waited on and modifies its value based on waiting thread count if equal 80ResultCode AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
108// to the value argument. 81 s32 num_to_wake) {
109ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
110 s32 num_to_wake) {
111 // Ensure that we can write to the address. 82 // Ensure that we can write to the address.
112 if (!Memory::IsValidVirtualAddress(address)) { 83 if (!Memory::IsValidVirtualAddress(address)) {
113 return ERR_INVALID_ADDRESS_STATE; 84 return ERR_INVALID_ADDRESS_STATE;
114 } 85 }
115 86
116 // Get threads waiting on the address. 87 // Get threads waiting on the address.
117 std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); 88 const std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address);
118 89
119 // Determine the modified value depending on the waiting count. 90 // Determine the modified value depending on the waiting count.
120 s32 updated_value; 91 s32 updated_value;
@@ -126,41 +97,54 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu
126 updated_value = value; 97 updated_value = value;
127 } 98 }
128 99
129 if (static_cast<s32>(Memory::Read32(address)) == value) { 100 if (static_cast<s32>(Memory::Read32(address)) != value) {
130 Memory::Write32(address, static_cast<u32>(updated_value));
131 } else {
132 return ERR_INVALID_STATE; 101 return ERR_INVALID_STATE;
133 } 102 }
134 103
104 Memory::Write32(address, static_cast<u32>(updated_value));
135 WakeThreads(waiting_threads, num_to_wake); 105 WakeThreads(waiting_threads, num_to_wake);
136 return RESULT_SUCCESS; 106 return RESULT_SUCCESS;
137} 107}
138 108
139// Waits on an address if the value passed is less than the argument value, optionally decrementing. 109ResultCode AddressArbiter::WaitForAddress(VAddr address, ArbitrationType type, s32 value,
140ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement) { 110 s64 timeout_ns) {
111 switch (type) {
112 case ArbitrationType::WaitIfLessThan:
113 return WaitForAddressIfLessThan(address, value, timeout_ns, false);
114 case ArbitrationType::DecrementAndWaitIfLessThan:
115 return WaitForAddressIfLessThan(address, value, timeout_ns, true);
116 case ArbitrationType::WaitIfEqual:
117 return WaitForAddressIfEqual(address, value, timeout_ns);
118 default:
119 return ERR_INVALID_ENUM_VALUE;
120 }
121}
122
123ResultCode AddressArbiter::WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
124 bool should_decrement) {
141 // Ensure that we can read the address. 125 // Ensure that we can read the address.
142 if (!Memory::IsValidVirtualAddress(address)) { 126 if (!Memory::IsValidVirtualAddress(address)) {
143 return ERR_INVALID_ADDRESS_STATE; 127 return ERR_INVALID_ADDRESS_STATE;
144 } 128 }
145 129
146 s32 cur_value = static_cast<s32>(Memory::Read32(address)); 130 const s32 cur_value = static_cast<s32>(Memory::Read32(address));
147 if (cur_value < value) { 131 if (cur_value >= value) {
148 if (should_decrement) {
149 Memory::Write32(address, static_cast<u32>(cur_value - 1));
150 }
151 } else {
152 return ERR_INVALID_STATE; 132 return ERR_INVALID_STATE;
153 } 133 }
134
135 if (should_decrement) {
136 Memory::Write32(address, static_cast<u32>(cur_value - 1));
137 }
138
154 // Short-circuit without rescheduling, if timeout is zero. 139 // Short-circuit without rescheduling, if timeout is zero.
155 if (timeout == 0) { 140 if (timeout == 0) {
156 return RESULT_TIMEOUT; 141 return RESULT_TIMEOUT;
157 } 142 }
158 143
159 return WaitForAddress(address, timeout); 144 return WaitForAddressImpl(address, timeout);
160} 145}
161 146
162// Waits on an address if the value passed is equal to the argument value. 147ResultCode AddressArbiter::WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
163ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
164 // Ensure that we can read the address. 148 // Ensure that we can read the address.
165 if (!Memory::IsValidVirtualAddress(address)) { 149 if (!Memory::IsValidVirtualAddress(address)) {
166 return ERR_INVALID_ADDRESS_STATE; 150 return ERR_INVALID_ADDRESS_STATE;
@@ -174,7 +158,48 @@ ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout) {
174 return RESULT_TIMEOUT; 158 return RESULT_TIMEOUT;
175 } 159 }
176 160
177 return WaitForAddress(address, timeout); 161 return WaitForAddressImpl(address, timeout);
162}
163
164ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
165 SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
166 current_thread->SetArbiterWaitAddress(address);
167 current_thread->SetStatus(ThreadStatus::WaitArb);
168 current_thread->InvalidateWakeupCallback();
169
170 current_thread->WakeAfterDelay(timeout);
171
172 system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
173 return RESULT_TIMEOUT;
174}
175
176std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) const {
177 const auto RetrieveWaitingThreads = [this](std::size_t core_index,
178 std::vector<SharedPtr<Thread>>& waiting_threads,
179 VAddr arb_addr) {
180 const auto& scheduler = system.Scheduler(core_index);
181 const auto& thread_list = scheduler.GetThreadList();
182
183 for (const auto& thread : thread_list) {
184 if (thread->GetArbiterWaitAddress() == arb_addr) {
185 waiting_threads.push_back(thread);
186 }
187 }
188 };
189
190 // Retrieve all threads that are waiting for this address.
191 std::vector<SharedPtr<Thread>> threads;
192 RetrieveWaitingThreads(0, threads, address);
193 RetrieveWaitingThreads(1, threads, address);
194 RetrieveWaitingThreads(2, threads, address);
195 RetrieveWaitingThreads(3, threads, address);
196
197 // Sort them by priority, such that the highest priority ones come first.
198 std::sort(threads.begin(), threads.end(),
199 [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
200 return lhs->GetPriority() < rhs->GetPriority();
201 });
202
203 return threads;
178} 204}
179} // namespace AddressArbiter
180} // namespace Kernel 205} // namespace Kernel
diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h
index e3657b8e9..ed0d0e69f 100644
--- a/src/core/hle/kernel/address_arbiter.h
+++ b/src/core/hle/kernel/address_arbiter.h
@@ -4,31 +4,77 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/kernel/object.h"
8 11
9union ResultCode; 12union ResultCode;
10 13
14namespace Core {
15class System;
16}
17
11namespace Kernel { 18namespace Kernel {
12 19
13namespace AddressArbiter { 20class Thread;
14enum class ArbitrationType {
15 WaitIfLessThan = 0,
16 DecrementAndWaitIfLessThan = 1,
17 WaitIfEqual = 2,
18};
19 21
20enum class SignalType { 22class AddressArbiter {
21 Signal = 0, 23public:
22 IncrementAndSignalIfEqual = 1, 24 enum class ArbitrationType {
23 ModifyByWaitingCountAndSignalIfEqual = 2, 25 WaitIfLessThan = 0,
24}; 26 DecrementAndWaitIfLessThan = 1,
27 WaitIfEqual = 2,
28 };
29
30 enum class SignalType {
31 Signal = 0,
32 IncrementAndSignalIfEqual = 1,
33 ModifyByWaitingCountAndSignalIfEqual = 2,
34 };
35
36 explicit AddressArbiter(Core::System& system);
37 ~AddressArbiter();
38
39 AddressArbiter(const AddressArbiter&) = delete;
40 AddressArbiter& operator=(const AddressArbiter&) = delete;
41
42 AddressArbiter(AddressArbiter&&) = default;
43 AddressArbiter& operator=(AddressArbiter&&) = delete;
44
45 /// Signals an address being waited on with a particular signaling type.
46 ResultCode SignalToAddress(VAddr address, SignalType type, s32 value, s32 num_to_wake);
25 47
26ResultCode SignalToAddress(VAddr address, s32 num_to_wake); 48 /// Waits on an address with a particular arbitration type.
27ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake); 49 ResultCode WaitForAddress(VAddr address, ArbitrationType type, s32 value, s64 timeout_ns);
28ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
29 50
30ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout, bool should_decrement); 51private:
31ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout); 52 /// Signals an address being waited on.
32} // namespace AddressArbiter 53 ResultCode SignalToAddressOnly(VAddr address, s32 num_to_wake);
54
55 /// Signals an address being waited on and increments its value if equal to the value argument.
56 ResultCode IncrementAndSignalToAddressIfEqual(VAddr address, s32 value, s32 num_to_wake);
57
58 /// Signals an address being waited on and modifies its value based on waiting thread count if
59 /// equal to the value argument.
60 ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 value,
61 s32 num_to_wake);
62
63 /// Waits on an address if the value passed is less than the argument value,
64 /// optionally decrementing.
65 ResultCode WaitForAddressIfLessThan(VAddr address, s32 value, s64 timeout,
66 bool should_decrement);
67
68 /// Waits on an address if the value passed is equal to the argument value.
69 ResultCode WaitForAddressIfEqual(VAddr address, s32 value, s64 timeout);
70
71 // Waits on the given address with a timeout in nanoseconds
72 ResultCode WaitForAddressImpl(VAddr address, s64 timeout);
73
74 // Gets the threads waiting on an address.
75 std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) const;
76
77 Core::System& system;
78};
33 79
34} // namespace Kernel 80} // namespace Kernel
diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp
index d4c91d529..aa432658e 100644
--- a/src/core/hle/kernel/client_port.cpp
+++ b/src/core/hle/kernel/client_port.cpp
@@ -33,10 +33,11 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
33 // Create a new session pair, let the created sessions inherit the parent port's HLE handler. 33 // Create a new session pair, let the created sessions inherit the parent port's HLE handler.
34 auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this); 34 auto sessions = ServerSession::CreateSessionPair(kernel, server_port->GetName(), this);
35 35
36 if (server_port->hle_handler) 36 if (server_port->HasHLEHandler()) {
37 server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); 37 server_port->GetHLEHandler()->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions));
38 else 38 } else {
39 server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions)); 39 server_port->AppendPendingSession(std::get<SharedPtr<ServerSession>>(sessions));
40 }
40 41
41 // Wake the threads waiting on the ServerPort 42 // Wake the threads waiting on the ServerPort
42 server_port->WakeupAllWaitingThreads(); 43 server_port->WakeupAllWaitingThreads();
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 704e82824..c17baa50a 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -17,21 +17,11 @@ ClientSession::~ClientSession() {
17 // This destructor will be called automatically when the last ClientSession handle is closed by 17 // This destructor will be called automatically when the last ClientSession handle is closed by
18 // the emulated application. 18 // the emulated application.
19 19
20 // Local references to ServerSession and SessionRequestHandler are necessary to guarantee they 20 // A local reference to the ServerSession is necessary to guarantee it
21 // will be kept alive until after ClientDisconnected() returns. 21 // will be kept alive until after ClientDisconnected() returns.
22 SharedPtr<ServerSession> server = parent->server; 22 SharedPtr<ServerSession> server = parent->server;
23 if (server) { 23 if (server) {
24 std::shared_ptr<SessionRequestHandler> hle_handler = server->hle_handler; 24 server->ClientDisconnected();
25 if (hle_handler)
26 hle_handler->ClientDisconnected(server);
27
28 // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
29 // their WaitSynchronization result to 0xC920181A.
30
31 // Clean up the list of client threads with pending requests, they are unneeded now that the
32 // client endpoint is closed.
33 server->pending_requesting_threads.clear();
34 server->currently_handling = nullptr;
35 } 25 }
36 26
37 parent->client = nullptr; 27 parent->client = nullptr;
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 4c18de69c..b1f39aad7 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -36,14 +36,15 @@ public:
36 36
37 ResultCode SendSyncRequest(SharedPtr<Thread> thread); 37 ResultCode SendSyncRequest(SharedPtr<Thread> thread);
38 38
39 std::string name; ///< Name of client port (optional) 39private:
40 explicit ClientSession(KernelCore& kernel);
41 ~ClientSession() override;
40 42
41 /// The parent session, which links to the server endpoint. 43 /// The parent session, which links to the server endpoint.
42 std::shared_ptr<Session> parent; 44 std::shared_ptr<Session> parent;
43 45
44private: 46 /// Name of the client session (optional)
45 explicit ClientSession(KernelCore& kernel); 47 std::string name;
46 ~ClientSession() override;
47}; 48};
48 49
49} // namespace Kernel 50} // namespace Kernel
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index d17eb0cb6..8097b3863 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -14,6 +14,7 @@ constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
14constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; 14constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
15constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; 15constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
16constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; 16constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
17constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};
17constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; 18constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
18constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; 19constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
19constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108}; 20constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index c8acde5b1..bdfaa977f 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -14,32 +14,47 @@
14namespace Kernel { 14namespace Kernel {
15namespace { 15namespace {
16constexpr u16 GetSlot(Handle handle) { 16constexpr u16 GetSlot(Handle handle) {
17 return handle >> 15; 17 return static_cast<u16>(handle >> 15);
18} 18}
19 19
20constexpr u16 GetGeneration(Handle handle) { 20constexpr u16 GetGeneration(Handle handle) {
21 return handle & 0x7FFF; 21 return static_cast<u16>(handle & 0x7FFF);
22} 22}
23} // Anonymous namespace 23} // Anonymous namespace
24 24
25HandleTable::HandleTable() { 25HandleTable::HandleTable() {
26 next_generation = 1;
27 Clear(); 26 Clear();
28} 27}
29 28
30HandleTable::~HandleTable() = default; 29HandleTable::~HandleTable() = default;
31 30
31ResultCode HandleTable::SetSize(s32 handle_table_size) {
32 if (static_cast<u32>(handle_table_size) > MAX_COUNT) {
33 return ERR_OUT_OF_MEMORY;
34 }
35
36 // Values less than or equal to zero indicate to use the maximum allowable
37 // size for the handle table in the actual kernel, so we ignore the given
38 // value in that case, since we assume this by default unless this function
39 // is called.
40 if (handle_table_size > 0) {
41 table_size = static_cast<u16>(handle_table_size);
42 }
43
44 return RESULT_SUCCESS;
45}
46
32ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { 47ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
33 DEBUG_ASSERT(obj != nullptr); 48 DEBUG_ASSERT(obj != nullptr);
34 49
35 u16 slot = next_free_slot; 50 const u16 slot = next_free_slot;
36 if (slot >= generations.size()) { 51 if (slot >= table_size) {
37 LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); 52 LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
38 return ERR_HANDLE_TABLE_FULL; 53 return ERR_HANDLE_TABLE_FULL;
39 } 54 }
40 next_free_slot = generations[slot]; 55 next_free_slot = generations[slot];
41 56
42 u16 generation = next_generation++; 57 const u16 generation = next_generation++;
43 58
44 // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. 59 // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
45 // Horizon OS uses zero to represent an invalid handle, so skip to 1. 60 // Horizon OS uses zero to represent an invalid handle, so skip to 1.
@@ -64,10 +79,11 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
64} 79}
65 80
66ResultCode HandleTable::Close(Handle handle) { 81ResultCode HandleTable::Close(Handle handle) {
67 if (!IsValid(handle)) 82 if (!IsValid(handle)) {
68 return ERR_INVALID_HANDLE; 83 return ERR_INVALID_HANDLE;
84 }
69 85
70 u16 slot = GetSlot(handle); 86 const u16 slot = GetSlot(handle);
71 87
72 objects[slot] = nullptr; 88 objects[slot] = nullptr;
73 89
@@ -77,10 +93,10 @@ ResultCode HandleTable::Close(Handle handle) {
77} 93}
78 94
79bool HandleTable::IsValid(Handle handle) const { 95bool HandleTable::IsValid(Handle handle) const {
80 std::size_t slot = GetSlot(handle); 96 const std::size_t slot = GetSlot(handle);
81 u16 generation = GetGeneration(handle); 97 const u16 generation = GetGeneration(handle);
82 98
83 return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; 99 return slot < table_size && objects[slot] != nullptr && generations[slot] == generation;
84} 100}
85 101
86SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { 102SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
@@ -97,7 +113,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
97} 113}
98 114
99void HandleTable::Clear() { 115void HandleTable::Clear() {
100 for (u16 i = 0; i < MAX_COUNT; ++i) { 116 for (u16 i = 0; i < table_size; ++i) {
101 generations[i] = i + 1; 117 generations[i] = i + 1;
102 objects[i] = nullptr; 118 objects[i] = nullptr;
103 } 119 }
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index 89a3bc740..44901391b 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -50,6 +50,20 @@ public:
50 ~HandleTable(); 50 ~HandleTable();
51 51
52 /** 52 /**
53 * Sets the number of handles that may be in use at one time
54 * for this handle table.
55 *
56 * @param handle_table_size The desired size to limit the handle table to.
57 *
58 * @returns an error code indicating if initialization was successful.
59 * If initialization was not successful, then ERR_OUT_OF_MEMORY
60 * will be returned.
61 *
62 * @pre handle_table_size must be within the range [0, 1024]
63 */
64 ResultCode SetSize(s32 handle_table_size);
65
66 /**
53 * Allocates a handle for the given object. 67 * Allocates a handle for the given object.
54 * @return The created Handle or one of the following errors: 68 * @return The created Handle or one of the following errors:
55 * - `ERR_HANDLE_TABLE_FULL`: the maximum number of handles has been exceeded. 69 * - `ERR_HANDLE_TABLE_FULL`: the maximum number of handles has been exceeded.
@@ -104,13 +118,20 @@ private:
104 std::array<u16, MAX_COUNT> generations; 118 std::array<u16, MAX_COUNT> generations;
105 119
106 /** 120 /**
121 * The limited size of the handle table. This can be specified by process
122 * capabilities in order to restrict the overall number of handles that
123 * can be created in a process instance
124 */
125 u16 table_size = static_cast<u16>(MAX_COUNT);
126
127 /**
107 * Global counter of the number of created handles. Stored in `generations` when a handle is 128 * Global counter of the number of created handles. Stored in `generations` when a handle is
108 * created, and wraps around to 1 when it hits 0x8000. 129 * created, and wraps around to 1 when it hits 0x8000.
109 */ 130 */
110 u16 next_generation; 131 u16 next_generation = 1;
111 132
112 /// Head of the free slots linked list. 133 /// Head of the free slots linked list.
113 u16 next_free_slot; 134 u16 next_free_slot = 0;
114}; 135};
115 136
116} // namespace Kernel 137} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 5dd855db8..fe710eb6e 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -86,7 +86,7 @@ HLERequestContext::~HLERequestContext() = default;
86void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, 86void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
87 bool incoming) { 87 bool incoming) {
88 IPC::RequestParser rp(src_cmdbuf); 88 IPC::RequestParser rp(src_cmdbuf);
89 command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>()); 89 command_header = rp.PopRaw<IPC::CommandHeader>();
90 90
91 if (command_header->type == IPC::CommandType::Close) { 91 if (command_header->type == IPC::CommandType::Close) {
92 // Close does not populate the rest of the IPC header 92 // Close does not populate the rest of the IPC header
@@ -95,8 +95,7 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
95 95
96 // If handle descriptor is present, add size of it 96 // If handle descriptor is present, add size of it
97 if (command_header->enable_handle_descriptor) { 97 if (command_header->enable_handle_descriptor) {
98 handle_descriptor_header = 98 handle_descriptor_header = rp.PopRaw<IPC::HandleDescriptorHeader>();
99 std::make_shared<IPC::HandleDescriptorHeader>(rp.PopRaw<IPC::HandleDescriptorHeader>());
100 if (handle_descriptor_header->send_current_pid) { 99 if (handle_descriptor_header->send_current_pid) {
101 rp.Skip(2, false); 100 rp.Skip(2, false);
102 } 101 }
@@ -140,16 +139,15 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
140 // If this is an incoming message, only CommandType "Request" has a domain header 139 // If this is an incoming message, only CommandType "Request" has a domain header
141 // All outgoing domain messages have the domain header, if only incoming has it 140 // All outgoing domain messages have the domain header, if only incoming has it
142 if (incoming || domain_message_header) { 141 if (incoming || domain_message_header) {
143 domain_message_header = 142 domain_message_header = rp.PopRaw<IPC::DomainMessageHeader>();
144 std::make_shared<IPC::DomainMessageHeader>(rp.PopRaw<IPC::DomainMessageHeader>());
145 } else { 143 } else {
146 if (Session()->IsDomain()) 144 if (Session()->IsDomain()) {
147 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!"); 145 LOG_WARNING(IPC, "Domain request has no DomainMessageHeader!");
146 }
148 } 147 }
149 } 148 }
150 149
151 data_payload_header = 150 data_payload_header = rp.PopRaw<IPC::DataPayloadHeader>();
152 std::make_shared<IPC::DataPayloadHeader>(rp.PopRaw<IPC::DataPayloadHeader>());
153 151
154 data_payload_offset = rp.GetCurrentOffset(); 152 data_payload_offset = rp.GetCurrentOffset();
155 153
@@ -264,11 +262,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
264 // Write the domain objects to the command buffer, these go after the raw untranslated data. 262 // Write the domain objects to the command buffer, these go after the raw untranslated data.
265 // TODO(Subv): This completely ignores C buffers. 263 // TODO(Subv): This completely ignores C buffers.
266 std::size_t domain_offset = size - domain_message_header->num_objects; 264 std::size_t domain_offset = size - domain_message_header->num_objects;
267 auto& request_handlers = server_session->domain_request_handlers;
268 265
269 for (auto& object : domain_objects) { 266 for (const auto& object : domain_objects) {
270 request_handlers.emplace_back(object); 267 server_session->AppendDomainRequestHandler(object);
271 dst_cmdbuf[domain_offset++] = static_cast<u32_le>(request_handlers.size()); 268 dst_cmdbuf[domain_offset++] =
269 static_cast<u32_le>(server_session->NumDomainRequestHandlers());
272 } 270 }
273 } 271 }
274 272
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index cb1c5aff3..2bdd9f02c 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -6,6 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <memory> 8#include <memory>
9#include <optional>
9#include <string> 10#include <string>
10#include <type_traits> 11#include <type_traits>
11#include <vector> 12#include <vector>
@@ -15,6 +16,8 @@
15#include "core/hle/ipc.h" 16#include "core/hle/ipc.h"
16#include "core/hle/kernel/object.h" 17#include "core/hle/kernel/object.h"
17 18
19union ResultCode;
20
18namespace Service { 21namespace Service {
19class ServiceFrameworkBase; 22class ServiceFrameworkBase;
20} 23}
@@ -166,12 +169,12 @@ public:
166 return buffer_c_desciptors; 169 return buffer_c_desciptors;
167 } 170 }
168 171
169 const IPC::DomainMessageHeader* GetDomainMessageHeader() const { 172 const IPC::DomainMessageHeader& GetDomainMessageHeader() const {
170 return domain_message_header.get(); 173 return domain_message_header.value();
171 } 174 }
172 175
173 bool HasDomainMessageHeader() const { 176 bool HasDomainMessageHeader() const {
174 return domain_message_header != nullptr; 177 return domain_message_header.has_value();
175 } 178 }
176 179
177 /// Helper function to read a buffer using the appropriate buffer descriptor 180 /// Helper function to read a buffer using the appropriate buffer descriptor
@@ -208,14 +211,12 @@ public:
208 211
209 template <typename T> 212 template <typename T>
210 SharedPtr<T> GetCopyObject(std::size_t index) { 213 SharedPtr<T> GetCopyObject(std::size_t index) {
211 ASSERT(index < copy_objects.size()); 214 return DynamicObjectCast<T>(copy_objects.at(index));
212 return DynamicObjectCast<T>(copy_objects[index]);
213 } 215 }
214 216
215 template <typename T> 217 template <typename T>
216 SharedPtr<T> GetMoveObject(std::size_t index) { 218 SharedPtr<T> GetMoveObject(std::size_t index) {
217 ASSERT(index < move_objects.size()); 219 return DynamicObjectCast<T>(move_objects.at(index));
218 return DynamicObjectCast<T>(move_objects[index]);
219 } 220 }
220 221
221 void AddMoveObject(SharedPtr<Object> object) { 222 void AddMoveObject(SharedPtr<Object> object) {
@@ -232,7 +233,7 @@ public:
232 233
233 template <typename T> 234 template <typename T>
234 std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const { 235 std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const {
235 return std::static_pointer_cast<T>(domain_request_handlers[index]); 236 return std::static_pointer_cast<T>(domain_request_handlers.at(index));
236 } 237 }
237 238
238 void SetDomainRequestHandlers( 239 void SetDomainRequestHandlers(
@@ -272,10 +273,10 @@ private:
272 boost::container::small_vector<SharedPtr<Object>, 8> copy_objects; 273 boost::container::small_vector<SharedPtr<Object>, 8> copy_objects;
273 boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects; 274 boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
274 275
275 std::shared_ptr<IPC::CommandHeader> command_header; 276 std::optional<IPC::CommandHeader> command_header;
276 std::shared_ptr<IPC::HandleDescriptorHeader> handle_descriptor_header; 277 std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
277 std::shared_ptr<IPC::DataPayloadHeader> data_payload_header; 278 std::optional<IPC::DataPayloadHeader> data_payload_header;
278 std::shared_ptr<IPC::DomainMessageHeader> domain_message_header; 279 std::optional<IPC::DomainMessageHeader> domain_message_header;
279 std::vector<IPC::BufferDescriptorX> buffer_x_desciptors; 280 std::vector<IPC::BufferDescriptorX> buffer_x_desciptors;
280 std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors; 281 std::vector<IPC::BufferDescriptorABW> buffer_a_desciptors;
281 std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors; 282 std::vector<IPC::BufferDescriptorABW> buffer_b_desciptors;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index dd749eed4..4d224d01d 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -12,6 +12,7 @@
12 12
13#include "core/core.h" 13#include "core/core.h"
14#include "core/core_timing.h" 14#include "core/core_timing.h"
15#include "core/hle/kernel/address_arbiter.h"
15#include "core/hle/kernel/client_port.h" 16#include "core/hle/kernel/client_port.h"
16#include "core/hle/kernel/handle_table.h" 17#include "core/hle/kernel/handle_table.h"
17#include "core/hle/kernel/kernel.h" 18#include "core/hle/kernel/kernel.h"
@@ -86,11 +87,13 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
86} 87}
87 88
88struct KernelCore::Impl { 89struct KernelCore::Impl {
89 void Initialize(KernelCore& kernel, Core::Timing::CoreTiming& core_timing) { 90 explicit Impl(Core::System& system) : system{system} {}
91
92 void Initialize(KernelCore& kernel) {
90 Shutdown(); 93 Shutdown();
91 94
92 InitializeSystemResourceLimit(kernel); 95 InitializeSystemResourceLimit(kernel);
93 InitializeThreads(core_timing); 96 InitializeThreads();
94 } 97 }
95 98
96 void Shutdown() { 99 void Shutdown() {
@@ -122,9 +125,9 @@ struct KernelCore::Impl {
122 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); 125 ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
123 } 126 }
124 127
125 void InitializeThreads(Core::Timing::CoreTiming& core_timing) { 128 void InitializeThreads() {
126 thread_wakeup_event_type = 129 thread_wakeup_event_type =
127 core_timing.RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); 130 system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
128 } 131 }
129 132
130 std::atomic<u32> next_object_id{0}; 133 std::atomic<u32> next_object_id{0};
@@ -145,15 +148,18 @@ struct KernelCore::Impl {
145 /// Map of named ports managed by the kernel, which can be retrieved using 148 /// Map of named ports managed by the kernel, which can be retrieved using
146 /// the ConnectToPort SVC. 149 /// the ConnectToPort SVC.
147 NamedPortTable named_ports; 150 NamedPortTable named_ports;
151
152 // System context
153 Core::System& system;
148}; 154};
149 155
150KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {} 156KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
151KernelCore::~KernelCore() { 157KernelCore::~KernelCore() {
152 Shutdown(); 158 Shutdown();
153} 159}
154 160
155void KernelCore::Initialize(Core::Timing::CoreTiming& core_timing) { 161void KernelCore::Initialize() {
156 impl->Initialize(*this, core_timing); 162 impl->Initialize(*this);
157} 163}
158 164
159void KernelCore::Shutdown() { 165void KernelCore::Shutdown() {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 154bced42..ff17ff865 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,6 +11,10 @@
11template <typename T> 11template <typename T>
12class ResultVal; 12class ResultVal;
13 13
14namespace Core {
15class System;
16}
17
14namespace Core::Timing { 18namespace Core::Timing {
15class CoreTiming; 19class CoreTiming;
16struct EventType; 20struct EventType;
@@ -18,6 +22,7 @@ struct EventType;
18 22
19namespace Kernel { 23namespace Kernel {
20 24
25class AddressArbiter;
21class ClientPort; 26class ClientPort;
22class HandleTable; 27class HandleTable;
23class Process; 28class Process;
@@ -30,7 +35,14 @@ private:
30 using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>; 35 using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
31 36
32public: 37public:
33 KernelCore(); 38 /// Constructs an instance of the kernel using the given System
39 /// instance as a context for any necessary system-related state,
40 /// such as threads, CPU core state, etc.
41 ///
42 /// @post After execution of the constructor, the provided System
43 /// object *must* outlive the kernel instance itself.
44 ///
45 explicit KernelCore(Core::System& system);
34 ~KernelCore(); 46 ~KernelCore();
35 47
36 KernelCore(const KernelCore&) = delete; 48 KernelCore(const KernelCore&) = delete;
@@ -40,11 +52,7 @@ public:
40 KernelCore& operator=(KernelCore&&) = delete; 52 KernelCore& operator=(KernelCore&&) = delete;
41 53
42 /// Resets the kernel to a clean slate for use. 54 /// Resets the kernel to a clean slate for use.
43 /// 55 void Initialize();
44 /// @param core_timing CoreTiming instance used to create any necessary
45 /// kernel-specific callback events.
46 ///
47 void Initialize(Core::Timing::CoreTiming& core_timing);
48 56
49 /// Clears all resources in use by the kernel instance. 57 /// Clears all resources in use by the kernel instance.
50 void Shutdown(); 58 void Shutdown();
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index c5aa19afa..49fced7b1 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -53,9 +53,10 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_poi
53CodeSet::CodeSet() = default; 53CodeSet::CodeSet() = default;
54CodeSet::~CodeSet() = default; 54CodeSet::~CodeSet() = default;
55 55
56SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { 56SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
57 SharedPtr<Process> process(new Process(kernel)); 57 auto& kernel = system.Kernel();
58 58
59 SharedPtr<Process> process(new Process(system));
59 process->name = std::move(name); 60 process->name = std::move(name);
60 process->resource_limit = kernel.GetSystemResourceLimit(); 61 process->resource_limit = kernel.GetSystemResourceLimit();
61 process->status = ProcessStatus::Created; 62 process->status = ProcessStatus::Created;
@@ -99,7 +100,13 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
99 vm_manager.Reset(metadata.GetAddressSpaceType()); 100 vm_manager.Reset(metadata.GetAddressSpaceType());
100 101
101 const auto& caps = metadata.GetKernelCapabilities(); 102 const auto& caps = metadata.GetKernelCapabilities();
102 return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); 103 const auto capability_init_result =
104 capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
105 if (capability_init_result.IsError()) {
106 return capability_init_result;
107 }
108
109 return handle_table.SetSize(capabilities.GetHandleTableSize());
103} 110}
104 111
105void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { 112void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
@@ -126,7 +133,7 @@ void Process::PrepareForTermination() {
126 if (thread->GetOwnerProcess() != this) 133 if (thread->GetOwnerProcess() != this)
127 continue; 134 continue;
128 135
129 if (thread == GetCurrentThread()) 136 if (thread == system.CurrentScheduler().GetCurrentThread())
130 continue; 137 continue;
131 138
132 // TODO(Subv): When are the other running/ready threads terminated? 139 // TODO(Subv): When are the other running/ready threads terminated?
@@ -138,7 +145,6 @@ void Process::PrepareForTermination() {
138 } 145 }
139 }; 146 };
140 147
141 const auto& system = Core::System::GetInstance();
142 stop_threads(system.Scheduler(0).GetThreadList()); 148 stop_threads(system.Scheduler(0).GetThreadList());
143 stop_threads(system.Scheduler(1).GetThreadList()); 149 stop_threads(system.Scheduler(1).GetThreadList());
144 stop_threads(system.Scheduler(2).GetThreadList()); 150 stop_threads(system.Scheduler(2).GetThreadList());
@@ -221,14 +227,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
221 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); 227 MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
222 228
223 // Clear instruction cache in CPU JIT 229 // Clear instruction cache in CPU JIT
224 Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); 230 system.InvalidateCpuInstructionCaches();
225 Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
226 Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
227 Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
228} 231}
229 232
230Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {} 233Process::Process(Core::System& system)
231Kernel::Process::~Process() {} 234 : WaitObject{system.Kernel()}, address_arbiter{system}, system{system} {}
235Process::~Process() = default;
232 236
233void Process::Acquire(Thread* thread) { 237void Process::Acquire(Thread* thread) {
234 ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); 238 ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index dcc57ae9f..47ffd4ad3 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -12,12 +12,17 @@
12#include <vector> 12#include <vector>
13#include <boost/container/static_vector.hpp> 13#include <boost/container/static_vector.hpp>
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "core/hle/kernel/address_arbiter.h"
15#include "core/hle/kernel/handle_table.h" 16#include "core/hle/kernel/handle_table.h"
16#include "core/hle/kernel/process_capability.h" 17#include "core/hle/kernel/process_capability.h"
17#include "core/hle/kernel/vm_manager.h" 18#include "core/hle/kernel/vm_manager.h"
18#include "core/hle/kernel/wait_object.h" 19#include "core/hle/kernel/wait_object.h"
19#include "core/hle/result.h" 20#include "core/hle/result.h"
20 21
22namespace Core {
23class System;
24}
25
21namespace FileSys { 26namespace FileSys {
22class ProgramMetadata; 27class ProgramMetadata;
23} 28}
@@ -116,7 +121,7 @@ public:
116 121
117 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; 122 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
118 123
119 static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name); 124 static SharedPtr<Process> Create(Core::System& system, std::string&& name);
120 125
121 std::string GetTypeName() const override { 126 std::string GetTypeName() const override {
122 return "Process"; 127 return "Process";
@@ -150,6 +155,16 @@ public:
150 return handle_table; 155 return handle_table;
151 } 156 }
152 157
158 /// Gets a reference to the process' address arbiter.
159 AddressArbiter& GetAddressArbiter() {
160 return address_arbiter;
161 }
162
163 /// Gets a const reference to the process' address arbiter.
164 const AddressArbiter& GetAddressArbiter() const {
165 return address_arbiter;
166 }
167
153 /// Gets the current status of the process 168 /// Gets the current status of the process
154 ProcessStatus GetStatus() const { 169 ProcessStatus GetStatus() const {
155 return status; 170 return status;
@@ -251,7 +266,7 @@ public:
251 void FreeTLSSlot(VAddr tls_address); 266 void FreeTLSSlot(VAddr tls_address);
252 267
253private: 268private:
254 explicit Process(KernelCore& kernel); 269 explicit Process(Core::System& system);
255 ~Process() override; 270 ~Process() override;
256 271
257 /// Checks if the specified thread should wait until this process is available. 272 /// Checks if the specified thread should wait until this process is available.
@@ -309,9 +324,16 @@ private:
309 /// Per-process handle table for storing created object handles in. 324 /// Per-process handle table for storing created object handles in.
310 HandleTable handle_table; 325 HandleTable handle_table;
311 326
327 /// Per-process address arbiter.
328 AddressArbiter address_arbiter;
329
312 /// Random values for svcGetInfo RandomEntropy 330 /// Random values for svcGetInfo RandomEntropy
313 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; 331 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
314 332
333 /// System context
334 Core::System& system;
335
336 /// Name of this process
315 std::string name; 337 std::string name;
316}; 338};
317 339
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
index 3a2164b25..583e35b79 100644
--- a/src/core/hle/kernel/process_capability.cpp
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -96,7 +96,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
96 interrupt_capabilities.set(); 96 interrupt_capabilities.set();
97 97
98 // Allow using the maximum possible amount of handles 98 // Allow using the maximum possible amount of handles
99 handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT); 99 handle_table_size = static_cast<s32>(HandleTable::MAX_COUNT);
100 100
101 // Allow all debugging capabilities. 101 // Allow all debugging capabilities.
102 is_debuggable = true; 102 is_debuggable = true;
@@ -337,7 +337,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
337 return ERR_RESERVED_VALUE; 337 return ERR_RESERVED_VALUE;
338 } 338 }
339 339
340 handle_table_size = (flags >> 16) & 0x3FF; 340 handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
341 return RESULT_SUCCESS; 341 return RESULT_SUCCESS;
342} 342}
343 343
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
index fbc8812a3..5cdd80747 100644
--- a/src/core/hle/kernel/process_capability.h
+++ b/src/core/hle/kernel/process_capability.h
@@ -156,7 +156,7 @@ public:
156 } 156 }
157 157
158 /// Gets the number of total allowable handles for the process' handle table. 158 /// Gets the number of total allowable handles for the process' handle table.
159 u32 GetHandleTableSize() const { 159 s32 GetHandleTableSize() const {
160 return handle_table_size; 160 return handle_table_size;
161 } 161 }
162 162
@@ -252,7 +252,7 @@ private:
252 u64 core_mask = 0; 252 u64 core_mask = 0;
253 u64 priority_mask = 0; 253 u64 priority_mask = 0;
254 254
255 u32 handle_table_size = 0; 255 s32 handle_table_size = 0;
256 u32 kernel_version = 0; 256 u32 kernel_version = 0;
257 257
258 ProgramType program_type = ProgramType::SysModule; 258 ProgramType program_type = ProgramType::SysModule;
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 44f30d070..e524509df 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -19,7 +19,8 @@ namespace Kernel {
19 19
20std::mutex Scheduler::scheduler_mutex; 20std::mutex Scheduler::scheduler_mutex;
21 21
22Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {} 22Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core)
23 : cpu_core{cpu_core}, system{system} {}
23 24
24Scheduler::~Scheduler() { 25Scheduler::~Scheduler() {
25 for (auto& thread : thread_list) { 26 for (auto& thread : thread_list) {
@@ -61,7 +62,7 @@ Thread* Scheduler::PopNextReadyThread() {
61 62
62void Scheduler::SwitchContext(Thread* new_thread) { 63void Scheduler::SwitchContext(Thread* new_thread) {
63 Thread* const previous_thread = GetCurrentThread(); 64 Thread* const previous_thread = GetCurrentThread();
64 Process* const previous_process = Core::CurrentProcess(); 65 Process* const previous_process = system.Kernel().CurrentProcess();
65 66
66 UpdateLastContextSwitchTime(previous_thread, previous_process); 67 UpdateLastContextSwitchTime(previous_thread, previous_process);
67 68
@@ -94,8 +95,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
94 95
95 auto* const thread_owner_process = current_thread->GetOwnerProcess(); 96 auto* const thread_owner_process = current_thread->GetOwnerProcess();
96 if (previous_process != thread_owner_process) { 97 if (previous_process != thread_owner_process) {
97 Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process); 98 system.Kernel().MakeCurrentProcess(thread_owner_process);
98 SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); 99 SetCurrentPageTable(&thread_owner_process->VMManager().page_table);
99 } 100 }
100 101
101 cpu_core.LoadContext(new_thread->GetContext()); 102 cpu_core.LoadContext(new_thread->GetContext());
@@ -111,7 +112,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
111 112
112void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { 113void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
113 const u64 prev_switch_ticks = last_context_switch_time; 114 const u64 prev_switch_ticks = last_context_switch_time;
114 const u64 most_recent_switch_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); 115 const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks();
115 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; 116 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
116 117
117 if (thread != nullptr) { 118 if (thread != nullptr) {
@@ -198,8 +199,7 @@ void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
198 ASSERT(thread->GetPriority() < THREADPRIO_COUNT); 199 ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
199 200
200 // Yield this thread -- sleep for zero time and force reschedule to different thread 201 // Yield this thread -- sleep for zero time and force reschedule to different thread
201 WaitCurrentThread_Sleep(); 202 GetCurrentThread()->Sleep(0);
202 GetCurrentThread()->WakeAfterDelay(0);
203} 203}
204 204
205void Scheduler::YieldWithLoadBalancing(Thread* thread) { 205void Scheduler::YieldWithLoadBalancing(Thread* thread) {
@@ -214,8 +214,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
214 ASSERT(priority < THREADPRIO_COUNT); 214 ASSERT(priority < THREADPRIO_COUNT);
215 215
216 // Sleep for zero time to be able to force reschedule to different thread 216 // Sleep for zero time to be able to force reschedule to different thread
217 WaitCurrentThread_Sleep(); 217 GetCurrentThread()->Sleep(0);
218 GetCurrentThread()->WakeAfterDelay(0);
219 218
220 Thread* suggested_thread = nullptr; 219 Thread* suggested_thread = nullptr;
221 220
@@ -223,8 +222,7 @@ void Scheduler::YieldWithLoadBalancing(Thread* thread) {
223 // Take the first non-nullptr one 222 // Take the first non-nullptr one
224 for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) { 223 for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
225 const auto res = 224 const auto res =
226 Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread( 225 system.CpuCore(cur_core).Scheduler().GetNextSuggestedThread(core, priority);
227 core, priority);
228 226
229 // If scheduler provides a suggested thread 227 // If scheduler provides a suggested thread
230 if (res != nullptr) { 228 if (res != nullptr) {
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 97ced4dfc..1c5bf57d9 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -13,7 +13,8 @@
13 13
14namespace Core { 14namespace Core {
15class ARM_Interface; 15class ARM_Interface;
16} 16class System;
17} // namespace Core
17 18
18namespace Kernel { 19namespace Kernel {
19 20
@@ -21,7 +22,7 @@ class Process;
21 22
22class Scheduler final { 23class Scheduler final {
23public: 24public:
24 explicit Scheduler(Core::ARM_Interface& cpu_core); 25 explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core);
25 ~Scheduler(); 26 ~Scheduler();
26 27
27 /// Returns whether there are any threads that are ready to run. 28 /// Returns whether there are any threads that are ready to run.
@@ -162,6 +163,7 @@ private:
162 Core::ARM_Interface& cpu_core; 163 Core::ARM_Interface& cpu_core;
163 u64 last_context_switch_time = 0; 164 u64 last_context_switch_time = 0;
164 165
166 Core::System& system;
165 static std::mutex scheduler_mutex; 167 static std::mutex scheduler_mutex;
166}; 168};
167 169
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp
index d6ceeb2da..0e1515c89 100644
--- a/src/core/hle/kernel/server_port.cpp
+++ b/src/core/hle/kernel/server_port.cpp
@@ -26,6 +26,10 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
26 return MakeResult(std::move(session)); 26 return MakeResult(std::move(session));
27} 27}
28 28
29void ServerPort::AppendPendingSession(SharedPtr<ServerSession> pending_session) {
30 pending_sessions.push_back(std::move(pending_session));
31}
32
29bool ServerPort::ShouldWait(Thread* thread) const { 33bool ServerPort::ShouldWait(Thread* thread) const {
30 // If there are no pending sessions, we wait until a new one is added. 34 // If there are no pending sessions, we wait until a new one is added.
31 return pending_sessions.empty(); 35 return pending_sessions.empty();
diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h
index e52f8245f..9bc667cf2 100644
--- a/src/core/hle/kernel/server_port.h
+++ b/src/core/hle/kernel/server_port.h
@@ -22,6 +22,8 @@ class SessionRequestHandler;
22 22
23class ServerPort final : public WaitObject { 23class ServerPort final : public WaitObject {
24public: 24public:
25 using HLEHandler = std::shared_ptr<SessionRequestHandler>;
26
25 /** 27 /**
26 * Creates a pair of ServerPort and an associated ClientPort. 28 * Creates a pair of ServerPort and an associated ClientPort.
27 * 29 *
@@ -51,22 +53,27 @@ public:
51 */ 53 */
52 ResultVal<SharedPtr<ServerSession>> Accept(); 54 ResultVal<SharedPtr<ServerSession>> Accept();
53 55
56 /// Whether or not this server port has an HLE handler available.
57 bool HasHLEHandler() const {
58 return hle_handler != nullptr;
59 }
60
61 /// Gets the HLE handler for this port.
62 HLEHandler GetHLEHandler() const {
63 return hle_handler;
64 }
65
54 /** 66 /**
55 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port 67 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
56 * will inherit a reference to this handler. 68 * will inherit a reference to this handler.
57 */ 69 */
58 void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { 70 void SetHleHandler(HLEHandler hle_handler_) {
59 hle_handler = std::move(hle_handler_); 71 hle_handler = std::move(hle_handler_);
60 } 72 }
61 73
62 std::string name; ///< Name of port (optional) 74 /// Appends a ServerSession to the collection of ServerSessions
63 75 /// waiting to be accepted by this port.
64 /// ServerSessions waiting to be accepted by the port 76 void AppendPendingSession(SharedPtr<ServerSession> pending_session);
65 std::vector<SharedPtr<ServerSession>> pending_sessions;
66
67 /// This session's HLE request handler template (optional)
68 /// ServerSessions created from this port inherit a reference to this handler.
69 std::shared_ptr<SessionRequestHandler> hle_handler;
70 77
71 bool ShouldWait(Thread* thread) const override; 78 bool ShouldWait(Thread* thread) const override;
72 void Acquire(Thread* thread) override; 79 void Acquire(Thread* thread) override;
@@ -74,6 +81,16 @@ public:
74private: 81private:
75 explicit ServerPort(KernelCore& kernel); 82 explicit ServerPort(KernelCore& kernel);
76 ~ServerPort() override; 83 ~ServerPort() override;
84
85 /// ServerSessions waiting to be accepted by the port
86 std::vector<SharedPtr<ServerSession>> pending_sessions;
87
88 /// This session's HLE request handler template (optional)
89 /// ServerSessions created from this port inherit a reference to this handler.
90 HLEHandler hle_handler;
91
92 /// Name of the port (optional)
93 std::string name;
77}; 94};
78 95
79} // namespace Kernel 96} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 027434f92..4d8a337a7 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -63,42 +63,71 @@ void ServerSession::Acquire(Thread* thread) {
63 pending_requesting_threads.pop_back(); 63 pending_requesting_threads.pop_back();
64} 64}
65 65
66void ServerSession::ClientDisconnected() {
67 // We keep a shared pointer to the hle handler to keep it alive throughout
68 // the call to ClientDisconnected, as ClientDisconnected invalidates the
69 // hle_handler member itself during the course of the function executing.
70 std::shared_ptr<SessionRequestHandler> handler = hle_handler;
71 if (handler) {
72 // Note that after this returns, this server session's hle_handler is
73 // invalidated (set to null).
74 handler->ClientDisconnected(this);
75 }
76
77 // TODO(Subv): Force a wake up of all the ServerSession's waiting threads and set
78 // their WaitSynchronization result to 0xC920181A.
79
80 // Clean up the list of client threads with pending requests, they are unneeded now that the
81 // client endpoint is closed.
82 pending_requesting_threads.clear();
83 currently_handling = nullptr;
84}
85
86void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
87 domain_request_handlers.push_back(std::move(handler));
88}
89
90std::size_t ServerSession::NumDomainRequestHandlers() const {
91 return domain_request_handlers.size();
92}
93
66ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { 94ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
67 auto* const domain_message_header = context.GetDomainMessageHeader(); 95 if (!context.HasDomainMessageHeader()) {
68 if (domain_message_header) { 96 return RESULT_SUCCESS;
69 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs 97 }
70 context.SetDomainRequestHandlers(domain_request_handlers); 98
71 99 // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
72 // If there is a DomainMessageHeader, then this is CommandType "Request" 100 context.SetDomainRequestHandlers(domain_request_handlers);
73 const u32 object_id{context.GetDomainMessageHeader()->object_id}; 101
74 switch (domain_message_header->command) { 102 // If there is a DomainMessageHeader, then this is CommandType "Request"
75 case IPC::DomainMessageHeader::CommandType::SendMessage: 103 const auto& domain_message_header = context.GetDomainMessageHeader();
76 if (object_id > domain_request_handlers.size()) { 104 const u32 object_id{domain_message_header.object_id};
77 LOG_CRITICAL(IPC, 105 switch (domain_message_header.command) {
78 "object_id {} is too big! This probably means a recent service call " 106 case IPC::DomainMessageHeader::CommandType::SendMessage:
79 "to {} needed to return a new interface!", 107 if (object_id > domain_request_handlers.size()) {
80 object_id, name); 108 LOG_CRITICAL(IPC,
81 UNREACHABLE(); 109 "object_id {} is too big! This probably means a recent service call "
82 return RESULT_SUCCESS; // Ignore error if asserts are off 110 "to {} needed to return a new interface!",
83 } 111 object_id, name);
84 return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); 112 UNREACHABLE();
85 113 return RESULT_SUCCESS; // Ignore error if asserts are off
86 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
87 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
88
89 domain_request_handlers[object_id - 1] = nullptr;
90
91 IPC::ResponseBuilder rb{context, 2};
92 rb.Push(RESULT_SUCCESS);
93 return RESULT_SUCCESS;
94 }
95 } 114 }
115 return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
96 116
97 LOG_CRITICAL(IPC, "Unknown domain command={}", 117 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
98 static_cast<int>(domain_message_header->command.Value())); 118 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
99 ASSERT(false); 119
120 domain_request_handlers[object_id - 1] = nullptr;
121
122 IPC::ResponseBuilder rb{context, 2};
123 rb.Push(RESULT_SUCCESS);
124 return RESULT_SUCCESS;
125 }
100 } 126 }
101 127
128 LOG_CRITICAL(IPC, "Unknown domain command={}",
129 static_cast<int>(domain_message_header.command.Value()));
130 ASSERT(false);
102 return RESULT_SUCCESS; 131 return RESULT_SUCCESS;
103} 132}
104 133
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index e0e9d64c8..aea4ccfeb 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -46,6 +46,14 @@ public:
46 return HANDLE_TYPE; 46 return HANDLE_TYPE;
47 } 47 }
48 48
49 Session* GetParent() {
50 return parent.get();
51 }
52
53 const Session* GetParent() const {
54 return parent.get();
55 }
56
49 using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>; 57 using SessionPair = std::tuple<SharedPtr<ServerSession>, SharedPtr<ClientSession>>;
50 58
51 /** 59 /**
@@ -78,23 +86,16 @@ public:
78 86
79 void Acquire(Thread* thread) override; 87 void Acquire(Thread* thread) override;
80 88
81 std::string name; ///< The name of this session (optional) 89 /// Called when a client disconnection occurs.
82 std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. 90 void ClientDisconnected();
83 std::shared_ptr<SessionRequestHandler>
84 hle_handler; ///< This session's HLE request handler (applicable when not a domain)
85 91
86 /// This is the list of domain request handlers (after conversion to a domain) 92 /// Adds a new domain request handler to the collection of request handlers within
87 std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers; 93 /// this ServerSession instance.
88 94 void AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler);
89 /// List of threads that are pending a response after a sync request. This list is processed in
90 /// a LIFO manner, thus, the last request will be dispatched first.
91 /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
92 std::vector<SharedPtr<Thread>> pending_requesting_threads;
93 95
94 /// Thread whose request is currently being handled. A request is considered "handled" when a 96 /// Retrieves the total number of domain request handlers that have been
95 /// response is sent via svcReplyAndReceive. 97 /// appended to this ServerSession instance.
96 /// TODO(Subv): Find a better name for this. 98 std::size_t NumDomainRequestHandlers() const;
97 SharedPtr<Thread> currently_handling;
98 99
99 /// Returns true if the session has been converted to a domain, otherwise False 100 /// Returns true if the session has been converted to a domain, otherwise False
100 bool IsDomain() const { 101 bool IsDomain() const {
@@ -129,8 +130,30 @@ private:
129 /// object handle. 130 /// object handle.
130 ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); 131 ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
131 132
133 /// The parent session, which links to the client endpoint.
134 std::shared_ptr<Session> parent;
135
136 /// This session's HLE request handler (applicable when not a domain)
137 std::shared_ptr<SessionRequestHandler> hle_handler;
138
139 /// This is the list of domain request handlers (after conversion to a domain)
140 std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
141
142 /// List of threads that are pending a response after a sync request. This list is processed in
143 /// a LIFO manner, thus, the last request will be dispatched first.
144 /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test.
145 std::vector<SharedPtr<Thread>> pending_requesting_threads;
146
147 /// Thread whose request is currently being handled. A request is considered "handled" when a
148 /// response is sent via svcReplyAndReceive.
149 /// TODO(Subv): Find a better name for this.
150 SharedPtr<Thread> currently_handling;
151
132 /// When set to True, converts the session to a domain at the end of the command 152 /// When set to True, converts the session to a domain at the end of the command
133 bool convert_to_domain{}; 153 bool convert_to_domain{};
154
155 /// The name of this session (optional)
156 std::string name;
134}; 157};
135 158
136} // namespace Kernel 159} // namespace Kernel
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index 22d0c1dd5..62861da36 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -6,7 +6,6 @@
6 6
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/hle/kernel/errors.h" 9#include "core/hle/kernel/errors.h"
11#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/shared_memory.h" 11#include "core/hle/kernel/shared_memory.h"
@@ -34,8 +33,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
34 shared_memory->backing_block_offset = 0; 33 shared_memory->backing_block_offset = 0;
35 34
36 // Refresh the address mappings for the current process. 35 // Refresh the address mappings for the current process.
37 if (Core::CurrentProcess() != nullptr) { 36 if (kernel.CurrentProcess() != nullptr) {
38 Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings( 37 kernel.CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
39 shared_memory->backing_block.get()); 38 shared_memory->backing_block.get());
40 } 39 }
41 } else { 40 } else {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index c5d399bab..047fa0c19 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -20,6 +20,7 @@
20#include "core/hle/kernel/address_arbiter.h" 20#include "core/hle/kernel/address_arbiter.h"
21#include "core/hle/kernel/client_port.h" 21#include "core/hle/kernel/client_port.h"
22#include "core/hle/kernel/client_session.h" 22#include "core/hle/kernel/client_session.h"
23#include "core/hle/kernel/errors.h"
23#include "core/hle/kernel/handle_table.h" 24#include "core/hle/kernel/handle_table.h"
24#include "core/hle/kernel/kernel.h" 25#include "core/hle/kernel/kernel.h"
25#include "core/hle/kernel/mutex.h" 26#include "core/hle/kernel/mutex.h"
@@ -47,23 +48,6 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
47 return address + size > address; 48 return address + size > address;
48} 49}
49 50
50// Checks if a given address range lies within a larger address range.
51constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
52 VAddr address_range_end) {
53 const VAddr end_address = address + size - 1;
54 return address_range_begin <= address && end_address <= address_range_end - 1;
55}
56
57bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
58 return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
59 vm.GetAddressSpaceEndAddress());
60}
61
62bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
63 return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
64 vm.GetNewMapRegionEndAddress());
65}
66
67// 8 GiB 51// 8 GiB
68constexpr u64 MAIN_MEMORY_SIZE = 0x200000000; 52constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
69 53
@@ -105,14 +89,14 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
105 return ERR_INVALID_ADDRESS_STATE; 89 return ERR_INVALID_ADDRESS_STATE;
106 } 90 }
107 91
108 if (!IsInsideAddressSpace(vm_manager, src_addr, size)) { 92 if (!vm_manager.IsWithinAddressSpace(src_addr, size)) {
109 LOG_ERROR(Kernel_SVC, 93 LOG_ERROR(Kernel_SVC,
110 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", 94 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
111 src_addr, size); 95 src_addr, size);
112 return ERR_INVALID_ADDRESS_STATE; 96 return ERR_INVALID_ADDRESS_STATE;
113 } 97 }
114 98
115 if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) { 99 if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) {
116 LOG_ERROR(Kernel_SVC, 100 LOG_ERROR(Kernel_SVC,
117 "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}", 101 "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
118 dst_addr, size); 102 dst_addr, size);
@@ -238,7 +222,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
238 auto* const current_process = Core::CurrentProcess(); 222 auto* const current_process = Core::CurrentProcess();
239 auto& vm_manager = current_process->VMManager(); 223 auto& vm_manager = current_process->VMManager();
240 224
241 if (!IsInsideAddressSpace(vm_manager, addr, size)) { 225 if (!vm_manager.IsWithinAddressSpace(addr, size)) {
242 LOG_ERROR(Kernel_SVC, 226 LOG_ERROR(Kernel_SVC,
243 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, 227 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
244 size); 228 size);
@@ -299,7 +283,7 @@ static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attr
299 } 283 }
300 284
301 auto& vm_manager = Core::CurrentProcess()->VMManager(); 285 auto& vm_manager = Core::CurrentProcess()->VMManager();
302 if (!IsInsideAddressSpace(vm_manager, address, size)) { 286 if (!vm_manager.IsWithinAddressSpace(address, size)) {
303 LOG_ERROR(Kernel_SVC, 287 LOG_ERROR(Kernel_SVC,
304 "Given address (0x{:016X}) is outside the bounds of the address space.", address); 288 "Given address (0x{:016X}) is outside the bounds of the address space.", address);
305 return ERR_INVALID_ADDRESS_STATE; 289 return ERR_INVALID_ADDRESS_STATE;
@@ -1300,10 +1284,14 @@ static ResultCode StartThread(Handle thread_handle) {
1300 1284
1301/// Called when a thread exits 1285/// Called when a thread exits
1302static void ExitThread() { 1286static void ExitThread() {
1303 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", Core::CurrentArmInterface().GetPC()); 1287 auto& system = Core::System::GetInstance();
1304 1288
1305 ExitCurrentThread(); 1289 LOG_TRACE(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
1306 Core::System::GetInstance().PrepareReschedule(); 1290
1291 auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
1292 current_thread->Stop();
1293 system.CurrentScheduler().RemoveThread(current_thread);
1294 system.PrepareReschedule();
1307} 1295}
1308 1296
1309/// Sleep the current thread 1297/// Sleep the current thread
@@ -1316,32 +1304,32 @@ static void SleepThread(s64 nanoseconds) {
1316 YieldAndWaitForLoadBalancing = -2, 1304 YieldAndWaitForLoadBalancing = -2,
1317 }; 1305 };
1318 1306
1307 auto& system = Core::System::GetInstance();
1308 auto& scheduler = system.CurrentScheduler();
1309 auto* const current_thread = scheduler.GetCurrentThread();
1310
1319 if (nanoseconds <= 0) { 1311 if (nanoseconds <= 0) {
1320 auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
1321 switch (static_cast<SleepType>(nanoseconds)) { 1312 switch (static_cast<SleepType>(nanoseconds)) {
1322 case SleepType::YieldWithoutLoadBalancing: 1313 case SleepType::YieldWithoutLoadBalancing:
1323 scheduler.YieldWithoutLoadBalancing(GetCurrentThread()); 1314 scheduler.YieldWithoutLoadBalancing(current_thread);
1324 break; 1315 break;
1325 case SleepType::YieldWithLoadBalancing: 1316 case SleepType::YieldWithLoadBalancing:
1326 scheduler.YieldWithLoadBalancing(GetCurrentThread()); 1317 scheduler.YieldWithLoadBalancing(current_thread);
1327 break; 1318 break;
1328 case SleepType::YieldAndWaitForLoadBalancing: 1319 case SleepType::YieldAndWaitForLoadBalancing:
1329 scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread()); 1320 scheduler.YieldAndWaitForLoadBalancing(current_thread);
1330 break; 1321 break;
1331 default: 1322 default:
1332 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); 1323 UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
1333 } 1324 }
1334 } else { 1325 } else {
1335 // Sleep current thread and check for next thread to schedule 1326 current_thread->Sleep(nanoseconds);
1336 WaitCurrentThread_Sleep();
1337
1338 // Create an event to wake the thread up after the specified nanosecond delay has passed
1339 GetCurrentThread()->WakeAfterDelay(nanoseconds);
1340 } 1327 }
1341 1328
1342 // Reschedule all CPU cores 1329 // Reschedule all CPU cores
1343 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) 1330 for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
1344 Core::System::GetInstance().CpuCore(i).PrepareReschedule(); 1331 system.CpuCore(i).PrepareReschedule();
1332 }
1345} 1333}
1346 1334
1347/// Wait process wide key atomic 1335/// Wait process wide key atomic
@@ -1495,20 +1483,10 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
1495 return ERR_INVALID_ADDRESS; 1483 return ERR_INVALID_ADDRESS;
1496 } 1484 }
1497 1485
1498 switch (static_cast<AddressArbiter::ArbitrationType>(type)) { 1486 const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
1499 case AddressArbiter::ArbitrationType::WaitIfLessThan: 1487 auto& address_arbiter =
1500 return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, false); 1488 Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter();
1501 case AddressArbiter::ArbitrationType::DecrementAndWaitIfLessThan: 1489 return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
1502 return AddressArbiter::WaitForAddressIfLessThan(address, value, timeout, true);
1503 case AddressArbiter::ArbitrationType::WaitIfEqual:
1504 return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
1505 default:
1506 LOG_ERROR(Kernel_SVC,
1507 "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan "
1508 "or WaitIfEqual but got {}",
1509 type);
1510 return ERR_INVALID_ENUM_VALUE;
1511 }
1512} 1490}
1513 1491
1514// Signals to an address (via Address Arbiter) 1492// Signals to an address (via Address Arbiter)
@@ -1526,21 +1504,10 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
1526 return ERR_INVALID_ADDRESS; 1504 return ERR_INVALID_ADDRESS;
1527 } 1505 }
1528 1506
1529 switch (static_cast<AddressArbiter::SignalType>(type)) { 1507 const auto signal_type = static_cast<AddressArbiter::SignalType>(type);
1530 case AddressArbiter::SignalType::Signal: 1508 auto& address_arbiter =
1531 return AddressArbiter::SignalToAddress(address, num_to_wake); 1509 Core::System::GetInstance().Kernel().CurrentProcess()->GetAddressArbiter();
1532 case AddressArbiter::SignalType::IncrementAndSignalIfEqual: 1510 return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake);
1533 return AddressArbiter::IncrementAndSignalToAddressIfEqual(address, value, num_to_wake);
1534 case AddressArbiter::SignalType::ModifyByWaitingCountAndSignalIfEqual:
1535 return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
1536 num_to_wake);
1537 default:
1538 LOG_ERROR(Kernel_SVC,
1539 "Invalid signal type, expected Signal, IncrementAndSignalIfEqual "
1540 "or ModifyByWaitingCountAndSignalIfEqual but got {}",
1541 type);
1542 return ERR_INVALID_ENUM_VALUE;
1543 }
1544} 1511}
1545 1512
1546/// This returns the total CPU ticks elapsed since the CPU was powered-on 1513/// This returns the total CPU ticks elapsed since the CPU was powered-on
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 6661e2130..2e712c9cb 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -68,11 +68,6 @@ void Thread::Stop() {
68 owner_process->FreeTLSSlot(tls_address); 68 owner_process->FreeTLSSlot(tls_address);
69} 69}
70 70
71void WaitCurrentThread_Sleep() {
72 Thread* thread = GetCurrentThread();
73 thread->SetStatus(ThreadStatus::WaitSleep);
74}
75
76void ExitCurrentThread() { 71void ExitCurrentThread() {
77 Thread* thread = GetCurrentThread(); 72 Thread* thread = GetCurrentThread();
78 thread->Stop(); 73 thread->Stop();
@@ -184,8 +179,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
184 return ERR_INVALID_PROCESSOR_ID; 179 return ERR_INVALID_PROCESSOR_ID;
185 } 180 }
186 181
187 // TODO(yuriks): Other checks, returning 0xD9001BEA
188
189 if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) { 182 if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
190 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); 183 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
191 // TODO (bunnei): Find the correct error code to use here 184 // TODO (bunnei): Find the correct error code to use here
@@ -393,6 +386,14 @@ void Thread::SetActivity(ThreadActivity value) {
393 } 386 }
394} 387}
395 388
389void Thread::Sleep(s64 nanoseconds) {
390 // Sleep current thread and check for next thread to schedule
391 SetStatus(ThreadStatus::WaitSleep);
392
393 // Create an event to wake the thread up after the specified nanosecond delay has passed
394 WakeAfterDelay(nanoseconds);
395}
396
396//////////////////////////////////////////////////////////////////////////////////////////////////// 397////////////////////////////////////////////////////////////////////////////////////////////////////
397 398
398/** 399/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index c48b21aba..ccdefeecc 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -383,6 +383,9 @@ public:
383 383
384 void SetActivity(ThreadActivity value); 384 void SetActivity(ThreadActivity value);
385 385
386 /// Sleeps this thread for the given amount of nanoseconds.
387 void Sleep(s64 nanoseconds);
388
386private: 389private:
387 explicit Thread(KernelCore& kernel); 390 explicit Thread(KernelCore& kernel);
388 ~Thread() override; 391 ~Thread() override;
@@ -460,14 +463,4 @@ private:
460 */ 463 */
461Thread* GetCurrentThread(); 464Thread* GetCurrentThread();
462 465
463/**
464 * Waits the current thread on a sleep
465 */
466void WaitCurrentThread_Sleep();
467
468/**
469 * Stops the current thread and removes it from the thread_list
470 */
471void ExitCurrentThread();
472
473} // namespace Kernel 466} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 10ad94aa6..05c59af34 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -17,8 +17,8 @@
17#include "core/memory_setup.h" 17#include "core/memory_setup.h"
18 18
19namespace Kernel { 19namespace Kernel {
20 20namespace {
21static const char* GetMemoryStateName(MemoryState state) { 21const char* GetMemoryStateName(MemoryState state) {
22 static constexpr const char* names[] = { 22 static constexpr const char* names[] = {
23 "Unmapped", "Io", 23 "Unmapped", "Io",
24 "Normal", "CodeStatic", 24 "Normal", "CodeStatic",
@@ -35,6 +35,14 @@ static const char* GetMemoryStateName(MemoryState state) {
35 return names[ToSvcMemoryState(state)]; 35 return names[ToSvcMemoryState(state)];
36} 36}
37 37
38// Checks if a given address range lies within a larger address range.
39constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
40 VAddr address_range_end) {
41 const VAddr end_address = address + size - 1;
42 return address_range_begin <= address && end_address <= address_range_end - 1;
43}
44} // Anonymous namespace
45
38bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { 46bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
39 ASSERT(base + size == next.base); 47 ASSERT(base + size == next.base);
40 if (permissions != next.permissions || state != next.state || attribute != next.attribute || 48 if (permissions != next.permissions || state != next.state || attribute != next.attribute ||
@@ -249,8 +257,7 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
249} 257}
250 258
251ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 259ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
252 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || 260 if (!IsWithinHeapRegion(target, size)) {
253 target + size < target) {
254 return ERR_INVALID_ADDRESS; 261 return ERR_INVALID_ADDRESS;
255 } 262 }
256 263
@@ -285,8 +292,7 @@ ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission p
285} 292}
286 293
287ResultCode VMManager::HeapFree(VAddr target, u64 size) { 294ResultCode VMManager::HeapFree(VAddr target, u64 size) {
288 if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() || 295 if (!IsWithinHeapRegion(target, size)) {
289 target + size < target) {
290 return ERR_INVALID_ADDRESS; 296 return ERR_INVALID_ADDRESS;
291 } 297 }
292 298
@@ -706,6 +712,11 @@ u64 VMManager::GetAddressSpaceWidth() const {
706 return address_space_width; 712 return address_space_width;
707} 713}
708 714
715bool VMManager::IsWithinAddressSpace(VAddr address, u64 size) const {
716 return IsInsideAddressRange(address, size, GetAddressSpaceBaseAddress(),
717 GetAddressSpaceEndAddress());
718}
719
709VAddr VMManager::GetASLRRegionBaseAddress() const { 720VAddr VMManager::GetASLRRegionBaseAddress() const {
710 return aslr_region_base; 721 return aslr_region_base;
711} 722}
@@ -750,6 +761,11 @@ u64 VMManager::GetCodeRegionSize() const {
750 return code_region_end - code_region_base; 761 return code_region_end - code_region_base;
751} 762}
752 763
764bool VMManager::IsWithinCodeRegion(VAddr address, u64 size) const {
765 return IsInsideAddressRange(address, size, GetCodeRegionBaseAddress(),
766 GetCodeRegionEndAddress());
767}
768
753VAddr VMManager::GetHeapRegionBaseAddress() const { 769VAddr VMManager::GetHeapRegionBaseAddress() const {
754 return heap_region_base; 770 return heap_region_base;
755} 771}
@@ -762,6 +778,11 @@ u64 VMManager::GetHeapRegionSize() const {
762 return heap_region_end - heap_region_base; 778 return heap_region_end - heap_region_base;
763} 779}
764 780
781bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
782 return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
783 GetHeapRegionEndAddress());
784}
785
765VAddr VMManager::GetMapRegionBaseAddress() const { 786VAddr VMManager::GetMapRegionBaseAddress() const {
766 return map_region_base; 787 return map_region_base;
767} 788}
@@ -774,6 +795,10 @@ u64 VMManager::GetMapRegionSize() const {
774 return map_region_end - map_region_base; 795 return map_region_end - map_region_base;
775} 796}
776 797
798bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const {
799 return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress());
800}
801
777VAddr VMManager::GetNewMapRegionBaseAddress() const { 802VAddr VMManager::GetNewMapRegionBaseAddress() const {
778 return new_map_region_base; 803 return new_map_region_base;
779} 804}
@@ -786,6 +811,11 @@ u64 VMManager::GetNewMapRegionSize() const {
786 return new_map_region_end - new_map_region_base; 811 return new_map_region_end - new_map_region_base;
787} 812}
788 813
814bool VMManager::IsWithinNewMapRegion(VAddr address, u64 size) const {
815 return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(),
816 GetNewMapRegionEndAddress());
817}
818
789VAddr VMManager::GetTLSIORegionBaseAddress() const { 819VAddr VMManager::GetTLSIORegionBaseAddress() const {
790 return tls_io_region_base; 820 return tls_io_region_base;
791} 821}
@@ -798,4 +828,9 @@ u64 VMManager::GetTLSIORegionSize() const {
798 return tls_io_region_end - tls_io_region_base; 828 return tls_io_region_end - tls_io_region_base;
799} 829}
800 830
831bool VMManager::IsWithinTLSIORegion(VAddr address, u64 size) const {
832 return IsInsideAddressRange(address, size, GetTLSIORegionBaseAddress(),
833 GetTLSIORegionEndAddress());
834}
835
801} // namespace Kernel 836} // namespace Kernel
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 6091533bc..88e0b3c02 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -432,18 +432,21 @@ public:
432 /// Gets the address space width in bits. 432 /// Gets the address space width in bits.
433 u64 GetAddressSpaceWidth() const; 433 u64 GetAddressSpaceWidth() const;
434 434
435 /// Determines whether or not the given address range lies within the address space.
436 bool IsWithinAddressSpace(VAddr address, u64 size) const;
437
435 /// Gets the base address of the ASLR region. 438 /// Gets the base address of the ASLR region.
436 VAddr GetASLRRegionBaseAddress() const; 439 VAddr GetASLRRegionBaseAddress() const;
437 440
438 /// Gets the end address of the ASLR region. 441 /// Gets the end address of the ASLR region.
439 VAddr GetASLRRegionEndAddress() const; 442 VAddr GetASLRRegionEndAddress() const;
440 443
441 /// Determines whether or not the specified address range is within the ASLR region.
442 bool IsWithinASLRRegion(VAddr address, u64 size) const;
443
444 /// Gets the size of the ASLR region 444 /// Gets the size of the ASLR region
445 u64 GetASLRRegionSize() const; 445 u64 GetASLRRegionSize() const;
446 446
447 /// Determines whether or not the specified address range is within the ASLR region.
448 bool IsWithinASLRRegion(VAddr address, u64 size) const;
449
447 /// Gets the base address of the code region. 450 /// Gets the base address of the code region.
448 VAddr GetCodeRegionBaseAddress() const; 451 VAddr GetCodeRegionBaseAddress() const;
449 452
@@ -453,6 +456,9 @@ public:
453 /// Gets the total size of the code region in bytes. 456 /// Gets the total size of the code region in bytes.
454 u64 GetCodeRegionSize() const; 457 u64 GetCodeRegionSize() const;
455 458
459 /// Determines whether or not the specified range is within the code region.
460 bool IsWithinCodeRegion(VAddr address, u64 size) const;
461
456 /// Gets the base address of the heap region. 462 /// Gets the base address of the heap region.
457 VAddr GetHeapRegionBaseAddress() const; 463 VAddr GetHeapRegionBaseAddress() const;
458 464
@@ -462,6 +468,9 @@ public:
462 /// Gets the total size of the heap region in bytes. 468 /// Gets the total size of the heap region in bytes.
463 u64 GetHeapRegionSize() const; 469 u64 GetHeapRegionSize() const;
464 470
471 /// Determines whether or not the specified range is within the heap region.
472 bool IsWithinHeapRegion(VAddr address, u64 size) const;
473
465 /// Gets the base address of the map region. 474 /// Gets the base address of the map region.
466 VAddr GetMapRegionBaseAddress() const; 475 VAddr GetMapRegionBaseAddress() const;
467 476
@@ -471,6 +480,9 @@ public:
471 /// Gets the total size of the map region in bytes. 480 /// Gets the total size of the map region in bytes.
472 u64 GetMapRegionSize() const; 481 u64 GetMapRegionSize() const;
473 482
483 /// Determines whether or not the specified range is within the map region.
484 bool IsWithinMapRegion(VAddr address, u64 size) const;
485
474 /// Gets the base address of the new map region. 486 /// Gets the base address of the new map region.
475 VAddr GetNewMapRegionBaseAddress() const; 487 VAddr GetNewMapRegionBaseAddress() const;
476 488
@@ -480,6 +492,9 @@ public:
480 /// Gets the total size of the new map region in bytes. 492 /// Gets the total size of the new map region in bytes.
481 u64 GetNewMapRegionSize() const; 493 u64 GetNewMapRegionSize() const;
482 494
495 /// Determines whether or not the given address range is within the new map region
496 bool IsWithinNewMapRegion(VAddr address, u64 size) const;
497
483 /// Gets the base address of the TLS IO region. 498 /// Gets the base address of the TLS IO region.
484 VAddr GetTLSIORegionBaseAddress() const; 499 VAddr GetTLSIORegionBaseAddress() const;
485 500
@@ -489,6 +504,9 @@ public:
489 /// Gets the total size of the TLS IO region in bytes. 504 /// Gets the total size of the TLS IO region in bytes.
490 u64 GetTLSIORegionSize() const; 505 u64 GetTLSIORegionSize() const;
491 506
507 /// Determines if the given address range is within the TLS IO region.
508 bool IsWithinTLSIORegion(VAddr address, u64 size) const;
509
492 /// Each VMManager has its own page table, which is set as the main one when the owning process 510 /// Each VMManager has its own page table, which is set as the main one when the owning process
493 /// is scheduled. 511 /// is scheduled.
494 Memory::PageTable page_table; 512 Memory::PageTable page_table;
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index bfb77cc31..ab84f5ddc 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -8,20 +8,11 @@
8#include <utility> 8#include <utility>
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_funcs.h"
12#include "common/common_types.h" 11#include "common/common_types.h"
13 12
14// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes 13// All the constants in this file come from http://switchbrew.org/index.php?title=Error_codes
15 14
16/** 15/**
17 * Detailed description of the error. Code 0 always means success.
18 */
19enum class ErrorDescription : u32 {
20 Success = 0,
21 RemoteProcessDead = 301,
22};
23
24/**
25 * Identifies the module which caused the error. Error codes can be propagated through a call 16 * Identifies the module which caused the error. Error codes can be propagated through a call
26 * chain, meaning that this doesn't always correspond to the module where the API call made is 17 * chain, meaning that this doesn't always correspond to the module where the API call made is
27 * contained. 18 * contained.
@@ -121,7 +112,7 @@ enum class ErrorModule : u32 {
121 ShopN = 811, 112 ShopN = 811,
122}; 113};
123 114
124/// Encapsulates a CTR-OS error code, allowing it to be separated into its constituent fields. 115/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
125union ResultCode { 116union ResultCode {
126 u32 raw; 117 u32 raw;
127 118
@@ -134,17 +125,9 @@ union ResultCode {
134 125
135 constexpr explicit ResultCode(u32 raw) : raw(raw) {} 126 constexpr explicit ResultCode(u32 raw) : raw(raw) {}
136 127
137 constexpr ResultCode(ErrorModule module, ErrorDescription description)
138 : ResultCode(module, static_cast<u32>(description)) {}
139
140 constexpr ResultCode(ErrorModule module_, u32 description_) 128 constexpr ResultCode(ErrorModule module_, u32 description_)
141 : raw(module.FormatValue(module_) | description.FormatValue(description_)) {} 129 : raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
142 130
143 constexpr ResultCode& operator=(const ResultCode& o) {
144 raw = o.raw;
145 return *this;
146 }
147
148 constexpr bool IsSuccess() const { 131 constexpr bool IsSuccess() const {
149 return raw == 0; 132 return raw == 0;
150 } 133 }
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
index f255f74b5..8c5bd6059 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -7,6 +7,7 @@
7#include "common/string_util.h" 7#include "common/string_util.h"
8#include "core/core.h" 8#include "core/core.h"
9#include "core/frontend/applets/software_keyboard.h" 9#include "core/frontend/applets/software_keyboard.h"
10#include "core/hle/result.h"
10#include "core/hle/service/am/am.h" 11#include "core/hle/service/am/am.h"
11#include "core/hle/service/am/applets/software_keyboard.h" 12#include "core/hle/service/am/applets/software_keyboard.h"
12 13
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
index efd5753a1..b93a30d28 100644
--- a/src/core/hle/service/am/applets/software_keyboard.h
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -9,10 +9,13 @@
9#include <vector> 9#include <vector>
10 10
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/common_types.h"
12#include "common/swap.h" 13#include "common/swap.h"
13#include "core/hle/service/am/am.h" 14#include "core/hle/service/am/am.h"
14#include "core/hle/service/am/applets/applets.h" 15#include "core/hle/service/am/applets/applets.h"
15 16
17union ResultCode;
18
16namespace Service::AM::Applets { 19namespace Service::AM::Applets {
17 20
18enum class KeysetDisable : u32 { 21enum class KeysetDisable : u32 {
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index 6831c0735..21f5e64c7 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -18,17 +18,11 @@
18#include "core/hle/kernel/readable_event.h" 18#include "core/hle/kernel/readable_event.h"
19#include "core/hle/kernel/writable_event.h" 19#include "core/hle/kernel/writable_event.h"
20#include "core/hle/service/audio/audout_u.h" 20#include "core/hle/service/audio/audout_u.h"
21#include "core/hle/service/audio/errors.h"
21#include "core/memory.h" 22#include "core/memory.h"
22 23
23namespace Service::Audio { 24namespace Service::Audio {
24 25
25namespace ErrCodes {
26enum {
27 ErrorUnknown = 2,
28 BufferCountExceeded = 8,
29};
30}
31
32constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; 26constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
33constexpr int DefaultSampleRate{48000}; 27constexpr int DefaultSampleRate{48000};
34 28
@@ -100,7 +94,7 @@ private:
100 94
101 if (stream->IsPlaying()) { 95 if (stream->IsPlaying()) {
102 IPC::ResponseBuilder rb{ctx, 2}; 96 IPC::ResponseBuilder rb{ctx, 2};
103 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown)); 97 rb.Push(ERR_OPERATION_FAILED);
104 return; 98 return;
105 } 99 }
106 100
@@ -113,7 +107,9 @@ private:
113 void StopAudioOut(Kernel::HLERequestContext& ctx) { 107 void StopAudioOut(Kernel::HLERequestContext& ctx) {
114 LOG_DEBUG(Service_Audio, "called"); 108 LOG_DEBUG(Service_Audio, "called");
115 109
116 audio_core.StopStream(stream); 110 if (stream->IsPlaying()) {
111 audio_core.StopStream(stream);
112 }
117 113
118 IPC::ResponseBuilder rb{ctx, 2}; 114 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(RESULT_SUCCESS); 115 rb.Push(RESULT_SUCCESS);
@@ -143,7 +139,8 @@ private:
143 139
144 if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { 140 if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
145 IPC::ResponseBuilder rb{ctx, 2}; 141 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); 142 rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
143 return;
147 } 144 }
148 145
149 IPC::ResponseBuilder rb{ctx, 2}; 146 IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 7e0cc64a8..c9de10a24 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -17,6 +17,7 @@
17#include "core/hle/kernel/readable_event.h" 17#include "core/hle/kernel/readable_event.h"
18#include "core/hle/kernel/writable_event.h" 18#include "core/hle/kernel/writable_event.h"
19#include "core/hle/service/audio/audren_u.h" 19#include "core/hle/service/audio/audren_u.h"
20#include "core/hle/service/audio/errors.h"
20 21
21namespace Service::Audio { 22namespace Service::Audio {
22 23
@@ -37,7 +38,7 @@ public:
37 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"}, 38 {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
38 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"}, 39 {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
39 {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"}, 40 {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
40 {11, nullptr, "ExecuteAudioRendererRendering"}, 41 {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"},
41 }; 42 };
42 // clang-format on 43 // clang-format on
43 RegisterHandlers(functions); 44 RegisterHandlers(functions);
@@ -138,6 +139,17 @@ private:
138 rb.Push(rendering_time_limit_percent); 139 rb.Push(rendering_time_limit_percent);
139 } 140 }
140 141
142 void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) {
143 LOG_DEBUG(Service_Audio, "called");
144
145 // This service command currently only reports an unsupported operation
146 // error code, or aborts. Given that, we just always return an error
147 // code in this case.
148
149 IPC::ResponseBuilder rb{ctx, 2};
150 rb.Push(ERR_NOT_SUPPORTED);
151 }
152
141 Kernel::EventPair system_event; 153 Kernel::EventPair system_event;
142 std::unique_ptr<AudioCore::AudioRenderer> renderer; 154 std::unique_ptr<AudioCore::AudioRenderer> renderer;
143 u32 rendering_time_limit_percent = 100; 155 u32 rendering_time_limit_percent = 100;
@@ -235,7 +247,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
235 {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"}, 247 {0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
236 {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"}, 248 {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetAudioRendererWorkBufferSize"},
237 {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"}, 249 {2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
238 {3, nullptr, "OpenAudioRendererAuto"}, 250 {3, &AudRenU::OpenAudioRendererAuto, "OpenAudioRendererAuto"},
239 {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"}, 251 {4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
240 }; 252 };
241 // clang-format on 253 // clang-format on
@@ -248,12 +260,7 @@ AudRenU::~AudRenU() = default;
248void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { 260void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
249 LOG_DEBUG(Service_Audio, "called"); 261 LOG_DEBUG(Service_Audio, "called");
250 262
251 IPC::RequestParser rp{ctx}; 263 OpenAudioRendererImpl(ctx);
252 auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
253 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
254
255 rb.Push(RESULT_SUCCESS);
256 rb.PushIpcInterface<Audio::IAudioRenderer>(std::move(params));
257} 264}
258 265
259void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { 266void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
@@ -262,20 +269,20 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
262 LOG_DEBUG(Service_Audio, "called"); 269 LOG_DEBUG(Service_Audio, "called");
263 270
264 u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40); 271 u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40);
265 buffer_sz += params.unknown_c * 1024; 272 buffer_sz += params.submix_count * 1024;
266 buffer_sz += 0x940 * (params.unknown_c + 1); 273 buffer_sz += 0x940 * (params.submix_count + 1);
267 buffer_sz += 0x3F0 * params.voice_count; 274 buffer_sz += 0x3F0 * params.voice_count;
268 buffer_sz += Common::AlignUp(8 * (params.unknown_c + 1), 0x10); 275 buffer_sz += Common::AlignUp(8 * (params.submix_count + 1), 0x10);
269 buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10); 276 buffer_sz += Common::AlignUp(8 * params.voice_count, 0x10);
270 buffer_sz += 277 buffer_sz += Common::AlignUp(
271 Common::AlignUp((0x3C0 * (params.sink_count + params.unknown_c) + 4 * params.sample_count) * 278 (0x3C0 * (params.sink_count + params.submix_count) + 4 * params.sample_count) *
272 (params.mix_buffer_count + 6), 279 (params.mix_buffer_count + 6),
273 0x40); 280 0x40);
274 281
275 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { 282 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
276 u32 count = params.unknown_c + 1; 283 const u32 count = params.submix_count + 1;
277 u64 node_count = Common::AlignUp(count, 0x40); 284 u64 node_count = Common::AlignUp(count, 0x40);
278 u64 node_state_buffer_sz = 285 const u64 node_state_buffer_sz =
279 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8); 286 4 * (node_count * node_count) + 0xC * node_count + 2 * (node_count / 8);
280 u64 edge_matrix_buffer_sz = 0; 287 u64 edge_matrix_buffer_sz = 0;
281 node_count = Common::AlignUp(count * count, 0x40); 288 node_count = Common::AlignUp(count * count, 0x40);
@@ -289,19 +296,19 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
289 296
290 buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50; 297 buffer_sz += 0x20 * (params.effect_count + 4 * params.voice_count) + 0x50;
291 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { 298 if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
292 buffer_sz += 0xE0 * params.unknown_2c; 299 buffer_sz += 0xE0 * params.num_splitter_send_channels;
293 buffer_sz += 0x20 * params.splitter_count; 300 buffer_sz += 0x20 * params.splitter_count;
294 buffer_sz += Common::AlignUp(4 * params.unknown_2c, 0x10); 301 buffer_sz += Common::AlignUp(4 * params.num_splitter_send_channels, 0x10);
295 } 302 }
296 buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count; 303 buffer_sz = Common::AlignUp(buffer_sz, 0x40) + 0x170 * params.sink_count;
297 u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count + 304 u64 output_sz = buffer_sz + 0x280 * params.sink_count + 0x4B0 * params.effect_count +
298 ((params.voice_count * 256) | 0x40); 305 ((params.voice_count * 256) | 0x40);
299 306
300 if (params.unknown_1c >= 1) { 307 if (params.performance_frame_count >= 1) {
301 output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count + 308 output_sz = Common::AlignUp(((16 * params.sink_count + 16 * params.effect_count +
302 16 * params.voice_count + 16) + 309 16 * params.voice_count + 16) +
303 0x658) * 310 0x658) *
304 (params.unknown_1c + 1) + 311 (params.performance_frame_count + 1) +
305 0xc0, 312 0xc0,
306 0x40) + 313 0x40) +
307 output_sz; 314 output_sz;
@@ -325,6 +332,12 @@ void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
325 rb.PushIpcInterface<Audio::IAudioDevice>(); 332 rb.PushIpcInterface<Audio::IAudioDevice>();
326} 333}
327 334
335void AudRenU::OpenAudioRendererAuto(Kernel::HLERequestContext& ctx) {
336 LOG_DEBUG(Service_Audio, "called");
337
338 OpenAudioRendererImpl(ctx);
339}
340
328void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) { 341void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
329 LOG_WARNING(Service_Audio, "(STUBBED) called"); 342 LOG_WARNING(Service_Audio, "(STUBBED) called");
330 343
@@ -335,6 +348,15 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c
335 // based on the current revision 348 // based on the current revision
336} 349}
337 350
351void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
352 IPC::RequestParser rp{ctx};
353 const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
354 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
355
356 rb.Push(RESULT_SUCCESS);
357 rb.PushIpcInterface<IAudioRenderer>(params);
358}
359
338bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const { 360bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
339 u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap 361 u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
340 switch (feature) { 362 switch (feature) {
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 3d63388fb..e55d25973 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -21,8 +21,11 @@ private:
21 void OpenAudioRenderer(Kernel::HLERequestContext& ctx); 21 void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
22 void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx); 22 void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
23 void GetAudioDeviceService(Kernel::HLERequestContext& ctx); 23 void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
24 void OpenAudioRendererAuto(Kernel::HLERequestContext& ctx);
24 void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx); 25 void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
25 26
27 void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
28
26 enum class AudioFeatures : u32 { 29 enum class AudioFeatures : u32 {
27 Splitter, 30 Splitter,
28 }; 31 };
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h
new file mode 100644
index 000000000..6f8c09bcf
--- /dev/null
+++ b/src/core/hle/service/audio/errors.h
@@ -0,0 +1,15 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8
9namespace Service::Audio {
10
11constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
12constexpr ResultCode ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
13constexpr ResultCode ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
14
15} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 11eba4a12..377e12cfa 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -9,43 +9,32 @@
9 9
10#include <opus.h> 10#include <opus.h>
11 11
12#include "common/common_funcs.h" 12#include "common/assert.h"
13#include "common/logging/log.h" 13#include "common/logging/log.h"
14#include "core/hle/ipc_helpers.h" 14#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/hle_ipc.h" 15#include "core/hle/kernel/hle_ipc.h"
16#include "core/hle/service/audio/hwopus.h" 16#include "core/hle/service/audio/hwopus.h"
17 17
18namespace Service::Audio { 18namespace Service::Audio {
19 19namespace {
20struct OpusDeleter { 20struct OpusDeleter {
21 void operator()(void* ptr) const { 21 void operator()(void* ptr) const {
22 operator delete(ptr); 22 operator delete(ptr);
23 } 23 }
24}; 24};
25 25
26class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 26using OpusDecoderPtr = std::unique_ptr<OpusDecoder, OpusDeleter>;
27public:
28 IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate,
29 u32 channel_count)
30 : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)),
31 sample_rate(sample_rate), channel_count(channel_count) {
32 // clang-format off
33 static const FunctionInfo functions[] = {
34 {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
35 {1, nullptr, "SetContext"},
36 {2, nullptr, "DecodeInterleavedForMultiStreamOld"},
37 {3, nullptr, "SetContextForMultiStream"},
38 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
39 {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
40 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
41 {7, nullptr, "DecodeInterleavedForMultiStream"},
42 };
43 // clang-format on
44 27
45 RegisterHandlers(functions); 28struct OpusPacketHeader {
46 } 29 // Packet size in bytes.
30 u32_be size;
31 // Indicates the final range of the codec's entropy coder.
32 u32_be final_range;
33};
34static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
47 35
48private: 36class OpusDecoderStateBase {
37public:
49 /// Describes extra behavior that may be asked of the decoding context. 38 /// Describes extra behavior that may be asked of the decoding context.
50 enum class ExtraBehavior { 39 enum class ExtraBehavior {
51 /// No extra behavior. 40 /// No extra behavior.
@@ -55,30 +44,36 @@ private:
55 ResetContext, 44 ResetContext,
56 }; 45 };
57 46
58 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { 47 enum class PerfTime {
59 LOG_DEBUG(Audio, "called"); 48 Disabled,
60 49 Enabled,
61 DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None); 50 };
62 }
63
64 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
65 LOG_DEBUG(Audio, "called");
66 51
67 u64 performance = 0; 52 virtual ~OpusDecoderStateBase() = default;
68 DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None);
69 }
70 53
71 void DecodeInterleaved(Kernel::HLERequestContext& ctx) { 54 // Decodes interleaved Opus packets. Optionally allows reporting time taken to
72 LOG_DEBUG(Audio, "called"); 55 // perform the decoding, as well as any relevant extra behavior.
73 56 virtual void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
74 IPC::RequestParser rp{ctx}; 57 ExtraBehavior extra_behavior) = 0;
75 const auto extra_behavior = 58};
76 rp.Pop<bool>() ? ExtraBehavior::ResetContext : ExtraBehavior::None;
77 59
78 u64 performance = 0; 60// Represents the decoder state for a non-multistream decoder.
79 DecodeInterleavedHelper(ctx, &performance, extra_behavior); 61class OpusDecoderState final : public OpusDecoderStateBase {
62public:
63 explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
64 : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
65
66 void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
67 ExtraBehavior extra_behavior) override {
68 if (perf_time == PerfTime::Disabled) {
69 DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
70 } else {
71 u64 performance = 0;
72 DecodeInterleavedHelper(ctx, &performance, extra_behavior);
73 }
80 } 74 }
81 75
76private:
82 void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance, 77 void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance,
83 ExtraBehavior extra_behavior) { 78 ExtraBehavior extra_behavior) {
84 u32 consumed = 0; 79 u32 consumed = 0;
@@ -89,8 +84,7 @@ private:
89 ResetDecoderContext(); 84 ResetDecoderContext();
90 } 85 }
91 86
92 if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples, 87 if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) {
93 performance)) {
94 LOG_ERROR(Audio, "Failed to decode opus data"); 88 LOG_ERROR(Audio, "Failed to decode opus data");
95 IPC::ResponseBuilder rb{ctx, 2}; 89 IPC::ResponseBuilder rb{ctx, 2};
96 // TODO(ogniK): Use correct error code 90 // TODO(ogniK): Use correct error code
@@ -109,27 +103,27 @@ private:
109 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); 103 ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
110 } 104 }
111 105
112 bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, 106 bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input,
113 std::vector<opus_int16>& output, u64* out_performance_time) { 107 std::vector<opus_int16>& output, u64* out_performance_time) const {
114 const auto start_time = std::chrono::high_resolution_clock::now(); 108 const auto start_time = std::chrono::high_resolution_clock::now();
115 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); 109 const std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
116 if (sizeof(OpusHeader) > input.size()) { 110 if (sizeof(OpusPacketHeader) > input.size()) {
117 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", 111 LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
118 sizeof(OpusHeader), input.size()); 112 sizeof(OpusPacketHeader), input.size());
119 return false; 113 return false;
120 } 114 }
121 115
122 OpusHeader hdr{}; 116 OpusPacketHeader hdr{};
123 std::memcpy(&hdr, input.data(), sizeof(OpusHeader)); 117 std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader));
124 if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) { 118 if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) {
125 LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}", 119 LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}",
126 sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size()); 120 sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size());
127 return false; 121 return false;
128 } 122 }
129 123
130 const auto frame = input.data() + sizeof(OpusHeader); 124 const auto frame = input.data() + sizeof(OpusPacketHeader);
131 const auto decoded_sample_count = opus_packet_get_nb_samples( 125 const auto decoded_sample_count = opus_packet_get_nb_samples(
132 frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)), 126 frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)),
133 static_cast<opus_int32>(sample_rate)); 127 static_cast<opus_int32>(sample_rate));
134 if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { 128 if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
135 LOG_ERROR( 129 LOG_ERROR(
@@ -141,18 +135,18 @@ private:
141 135
142 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); 136 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
143 const auto out_sample_count = 137 const auto out_sample_count =
144 opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0); 138 opus_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
145 if (out_sample_count < 0) { 139 if (out_sample_count < 0) {
146 LOG_ERROR(Audio, 140 LOG_ERROR(Audio,
147 "Incorrect sample count received from opus_decode, " 141 "Incorrect sample count received from opus_decode, "
148 "output_sample_count={}, frame_size={}, data_sz_from_hdr={}", 142 "output_sample_count={}, frame_size={}, data_sz_from_hdr={}",
149 out_sample_count, frame_size, static_cast<u32>(hdr.sz)); 143 out_sample_count, frame_size, static_cast<u32>(hdr.size));
150 return false; 144 return false;
151 } 145 }
152 146
153 const auto end_time = std::chrono::high_resolution_clock::now() - start_time; 147 const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
154 sample_count = out_sample_count; 148 sample_count = out_sample_count;
155 consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); 149 consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size);
156 if (out_performance_time != nullptr) { 150 if (out_performance_time != nullptr) {
157 *out_performance_time = 151 *out_performance_time =
158 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); 152 std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count();
@@ -167,21 +161,66 @@ private:
167 opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE); 161 opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
168 } 162 }
169 163
170 struct OpusHeader { 164 OpusDecoderPtr decoder;
171 u32_be sz; // Needs to be BE for some odd reason
172 INSERT_PADDING_WORDS(1);
173 };
174 static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size");
175
176 std::unique_ptr<OpusDecoder, OpusDeleter> decoder;
177 u32 sample_rate; 165 u32 sample_rate;
178 u32 channel_count; 166 u32 channel_count;
179}; 167};
180 168
181static std::size_t WorkerBufferSize(u32 channel_count) { 169class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
170public:
171 explicit IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoderStateBase> decoder_state)
172 : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
173 // clang-format off
174 static const FunctionInfo functions[] = {
175 {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"},
176 {1, nullptr, "SetContext"},
177 {2, nullptr, "DecodeInterleavedForMultiStreamOld"},
178 {3, nullptr, "SetContextForMultiStream"},
179 {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"},
180 {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"},
181 {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"},
182 {7, nullptr, "DecodeInterleavedForMultiStream"},
183 };
184 // clang-format on
185
186 RegisterHandlers(functions);
187 }
188
189private:
190 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
191 LOG_DEBUG(Audio, "called");
192
193 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Disabled,
194 OpusDecoderStateBase::ExtraBehavior::None);
195 }
196
197 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
198 LOG_DEBUG(Audio, "called");
199
200 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled,
201 OpusDecoderStateBase::ExtraBehavior::None);
202 }
203
204 void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
205 LOG_DEBUG(Audio, "called");
206
207 IPC::RequestParser rp{ctx};
208 const auto extra_behavior = rp.Pop<bool>()
209 ? OpusDecoderStateBase::ExtraBehavior::ResetContext
210 : OpusDecoderStateBase::ExtraBehavior::None;
211
212 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled,
213 extra_behavior);
214 }
215
216 std::unique_ptr<OpusDecoderStateBase> decoder_state;
217};
218
219std::size_t WorkerBufferSize(u32 channel_count) {
182 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 220 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
183 return opus_decoder_get_size(static_cast<int>(channel_count)); 221 return opus_decoder_get_size(static_cast<int>(channel_count));
184} 222}
223} // Anonymous namespace
185 224
186void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { 225void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
187 IPC::RequestParser rp{ctx}; 226 IPC::RequestParser rp{ctx};
@@ -220,8 +259,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
220 const std::size_t worker_sz = WorkerBufferSize(channel_count); 259 const std::size_t worker_sz = WorkerBufferSize(channel_count);
221 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); 260 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
222 261
223 std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ 262 OpusDecoderPtr decoder{static_cast<OpusDecoder*>(operator new(worker_sz))};
224 static_cast<OpusDecoder*>(operator new(worker_sz))};
225 if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { 263 if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
226 LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err); 264 LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err);
227 IPC::ResponseBuilder rb{ctx, 2}; 265 IPC::ResponseBuilder rb{ctx, 2};
@@ -232,8 +270,8 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
232 270
233 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 271 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
234 rb.Push(RESULT_SUCCESS); 272 rb.Push(RESULT_SUCCESS);
235 rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate, 273 rb.PushIpcInterface<IHardwareOpusDecoderManager>(
236 channel_count); 274 std::make_unique<OpusDecoderState>(std::move(decoder), sample_rate, channel_count));
237} 275}
238 276
239HwOpus::HwOpus() : ServiceFramework("hwopus") { 277HwOpus::HwOpus() : ServiceFramework("hwopus") {
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 6d897c842..7cc58db4c 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -15,7 +15,7 @@ namespace Kernel {
15class SharedMemory; 15class SharedMemory;
16} 16}
17 17
18namespace SM { 18namespace Service::SM {
19class ServiceManager; 19class ServiceManager;
20} 20}
21 21
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 21ccfe1f8..20c7c39aa 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -23,7 +23,7 @@ u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector
23 23
24void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 24void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
25 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, 25 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
26 const MathUtil::Rectangle<int>& crop_rect) { 26 const Common::Rectangle<int>& crop_rect) {
27 VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 27 VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
28 LOG_TRACE(Service, 28 LOG_TRACE(Service,
29 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 29 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
@@ -36,7 +36,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
36 36
37 auto& instance = Core::System::GetInstance(); 37 auto& instance = Core::System::GetInstance();
38 instance.GetPerfStats().EndGameFrame(); 38 instance.GetPerfStats().EndGameFrame();
39 instance.Renderer().SwapBuffers(framebuffer); 39 instance.GPU().SwapBuffers(framebuffer);
40} 40}
41 41
42} // namespace Service::Nvidia::Devices 42} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index a45086e45..ace71169f 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -25,7 +25,7 @@ public:
25 /// Performs a screen flip, drawing the buffer pointed to by the handle. 25 /// Performs a screen flip, drawing the buffer pointed to by the handle.
26 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 26 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
27 NVFlinger::BufferQueue::BufferTransformFlags transform, 27 NVFlinger::BufferQueue::BufferTransformFlags transform,
28 const MathUtil::Rectangle<int>& crop_rect); 28 const Common::Rectangle<int>& crop_rect);
29 29
30private: 30private:
31 std::shared_ptr<nvmap> nvmap_dev; 31 std::shared_ptr<nvmap> nvmap_dev;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 466db7ccd..b031ebc66 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -10,6 +10,7 @@
10#include "core/core.h" 10#include "core/core.h"
11#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" 11#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
12#include "core/hle/service/nvdrv/devices/nvmap.h" 12#include "core/hle/service/nvdrv/devices/nvmap.h"
13#include "core/memory.h"
13#include "video_core/memory_manager.h" 14#include "video_core/memory_manager.h"
14#include "video_core/rasterizer_interface.h" 15#include "video_core/rasterizer_interface.h"
15#include "video_core/renderer_base.h" 16#include "video_core/renderer_base.h"
@@ -178,7 +179,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
178 auto& gpu = system_instance.GPU(); 179 auto& gpu = system_instance.GPU();
179 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset); 180 auto cpu_addr = gpu.MemoryManager().GpuToCpuAddress(params.offset);
180 ASSERT(cpu_addr); 181 ASSERT(cpu_addr);
181 system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(*cpu_addr, itr->second.size); 182 gpu.FlushAndInvalidateRegion(ToCacheAddr(Memory::GetPointer(*cpu_addr)), itr->second.size);
182 183
183 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size); 184 params.offset = gpu.MemoryManager().UnmapBuffer(params.offset, itr->second.size);
184 185
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 0a650f36c..8ce7bc7a5 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -136,16 +136,6 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
136 return 0; 136 return 0;
137} 137}
138 138
139static void PushGPUEntries(Tegra::CommandList&& entries) {
140 if (entries.empty()) {
141 return;
142 }
143
144 auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()};
145 dma_pusher.Push(std::move(entries));
146 dma_pusher.DispatchCalls();
147}
148
149u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { 139u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
150 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 140 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
151 UNIMPLEMENTED(); 141 UNIMPLEMENTED();
@@ -163,7 +153,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
163 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], 153 std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
164 params.num_entries * sizeof(Tegra::CommandListHeader)); 154 params.num_entries * sizeof(Tegra::CommandListHeader));
165 155
166 PushGPUEntries(std::move(entries)); 156 Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
167 157
168 params.fence_out.id = 0; 158 params.fence_out.id = 0;
169 params.fence_out.value = 0; 159 params.fence_out.value = 0;
@@ -184,7 +174,7 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
184 Memory::ReadBlock(params.address, entries.data(), 174 Memory::ReadBlock(params.address, entries.data(),
185 params.num_entries * sizeof(Tegra::CommandListHeader)); 175 params.num_entries * sizeof(Tegra::CommandListHeader));
186 176
187 PushGPUEntries(std::move(entries)); 177 Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries));
188 178
189 params.fence_out.id = 0; 179 params.fence_out.id = 0;
190 params.fence_out.value = 0; 180 params.fence_out.value = 0;
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index fc07d9bb8..4d150fc71 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -63,7 +63,7 @@ const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
63} 63}
64 64
65void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform, 65void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
66 const MathUtil::Rectangle<int>& crop_rect) { 66 const Common::Rectangle<int>& crop_rect) {
67 auto itr = std::find_if(queue.begin(), queue.end(), 67 auto itr = std::find_if(queue.begin(), queue.end(),
68 [&](const Buffer& buffer) { return buffer.slot == slot; }); 68 [&](const Buffer& buffer) { return buffer.slot == slot; });
69 ASSERT(itr != queue.end()); 69 ASSERT(itr != queue.end());
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index ab90d591e..e1ccb6171 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -67,14 +67,14 @@ public:
67 Status status = Status::Free; 67 Status status = Status::Free;
68 IGBPBuffer igbp_buffer; 68 IGBPBuffer igbp_buffer;
69 BufferTransformFlags transform; 69 BufferTransformFlags transform;
70 MathUtil::Rectangle<int> crop_rect; 70 Common::Rectangle<int> crop_rect;
71 }; 71 };
72 72
73 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer); 73 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
74 std::optional<u32> DequeueBuffer(u32 width, u32 height); 74 std::optional<u32> DequeueBuffer(u32 width, u32 height);
75 const IGBPBuffer& RequestBuffer(u32 slot) const; 75 const IGBPBuffer& RequestBuffer(u32 slot) const;
76 void QueueBuffer(u32 slot, BufferTransformFlags transform, 76 void QueueBuffer(u32 slot, BufferTransformFlags transform,
77 const MathUtil::Rectangle<int>& crop_rect); 77 const Common::Rectangle<int>& crop_rect);
78 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer(); 78 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
79 void ReleaseBuffer(u32 slot); 79 void ReleaseBuffer(u32 slot);
80 u32 Query(QueryType type); 80 u32 Query(QueryType type);
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 3babc3f7c..fc496b654 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -14,11 +14,12 @@
14#include "core/core_timing_util.h" 14#include "core/core_timing_util.h"
15#include "core/hle/kernel/kernel.h" 15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/readable_event.h" 16#include "core/hle/kernel/readable_event.h"
17#include "core/hle/kernel/writable_event.h"
18#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 17#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
19#include "core/hle/service/nvdrv/nvdrv.h" 18#include "core/hle/service/nvdrv/nvdrv.h"
20#include "core/hle/service/nvflinger/buffer_queue.h" 19#include "core/hle/service/nvflinger/buffer_queue.h"
21#include "core/hle/service/nvflinger/nvflinger.h" 20#include "core/hle/service/nvflinger/nvflinger.h"
21#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h"
22#include "core/perf_stats.h" 23#include "core/perf_stats.h"
23#include "video_core/renderer_base.h" 24#include "video_core/renderer_base.h"
24 25
@@ -28,6 +29,12 @@ constexpr std::size_t SCREEN_REFRESH_RATE = 60;
28constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); 29constexpr u64 frame_ticks = static_cast<u64>(Core::Timing::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
29 30
30NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { 31NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} {
32 displays.emplace_back(0, "Default");
33 displays.emplace_back(1, "External");
34 displays.emplace_back(2, "Edid");
35 displays.emplace_back(3, "Internal");
36 displays.emplace_back(4, "Null");
37
31 // Schedule the screen composition events 38 // Schedule the screen composition events
32 composition_event = 39 composition_event =
33 core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { 40 core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
@@ -52,13 +59,14 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
52 // TODO(Subv): Currently we only support the Default display. 59 // TODO(Subv): Currently we only support the Default display.
53 ASSERT(name == "Default"); 60 ASSERT(name == "Default");
54 61
55 const auto itr = std::find_if(displays.begin(), displays.end(), 62 const auto itr =
56 [&](const Display& display) { return display.name == name; }); 63 std::find_if(displays.begin(), displays.end(),
64 [&](const VI::Display& display) { return display.GetName() == name; });
57 if (itr == displays.end()) { 65 if (itr == displays.end()) {
58 return {}; 66 return {};
59 } 67 }
60 68
61 return itr->id; 69 return itr->GetID();
62} 70}
63 71
64std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { 72std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
@@ -68,13 +76,10 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
68 return {}; 76 return {};
69 } 77 }
70 78
71 ASSERT_MSG(display->layers.empty(), "Only one layer is supported per display at the moment");
72
73 const u64 layer_id = next_layer_id++; 79 const u64 layer_id = next_layer_id++;
74 const u32 buffer_queue_id = next_buffer_queue_id++; 80 const u32 buffer_queue_id = next_buffer_queue_id++;
75 auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id); 81 buffer_queues.emplace_back(buffer_queue_id, layer_id);
76 display->layers.emplace_back(layer_id, buffer_queue); 82 display->CreateLayer(layer_id, buffer_queues.back());
77 buffer_queues.emplace_back(std::move(buffer_queue));
78 return layer_id; 83 return layer_id;
79} 84}
80 85
@@ -85,7 +90,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
85 return {}; 90 return {};
86 } 91 }
87 92
88 return layer->buffer_queue->GetId(); 93 return layer->GetBufferQueue().GetId();
89} 94}
90 95
91Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const { 96Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_id) const {
@@ -95,20 +100,29 @@ Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::FindVsyncEvent(u64 display_i
95 return nullptr; 100 return nullptr;
96 } 101 }
97 102
98 return display->vsync_event.readable; 103 return display->GetVSyncEvent();
104}
105
106BufferQueue& NVFlinger::FindBufferQueue(u32 id) {
107 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
108 [id](const auto& queue) { return queue.GetId() == id; });
109
110 ASSERT(itr != buffer_queues.end());
111 return *itr;
99} 112}
100 113
101std::shared_ptr<BufferQueue> NVFlinger::FindBufferQueue(u32 id) const { 114const BufferQueue& NVFlinger::FindBufferQueue(u32 id) const {
102 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(), 115 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
103 [&](const auto& queue) { return queue->GetId() == id; }); 116 [id](const auto& queue) { return queue.GetId() == id; });
104 117
105 ASSERT(itr != buffer_queues.end()); 118 ASSERT(itr != buffer_queues.end());
106 return *itr; 119 return *itr;
107} 120}
108 121
109Display* NVFlinger::FindDisplay(u64 display_id) { 122VI::Display* NVFlinger::FindDisplay(u64 display_id) {
110 const auto itr = std::find_if(displays.begin(), displays.end(), 123 const auto itr =
111 [&](const Display& display) { return display.id == display_id; }); 124 std::find_if(displays.begin(), displays.end(),
125 [&](const VI::Display& display) { return display.GetID() == display_id; });
112 126
113 if (itr == displays.end()) { 127 if (itr == displays.end()) {
114 return nullptr; 128 return nullptr;
@@ -117,9 +131,10 @@ Display* NVFlinger::FindDisplay(u64 display_id) {
117 return &*itr; 131 return &*itr;
118} 132}
119 133
120const Display* NVFlinger::FindDisplay(u64 display_id) const { 134const VI::Display* NVFlinger::FindDisplay(u64 display_id) const {
121 const auto itr = std::find_if(displays.begin(), displays.end(), 135 const auto itr =
122 [&](const Display& display) { return display.id == display_id; }); 136 std::find_if(displays.begin(), displays.end(),
137 [&](const VI::Display& display) { return display.GetID() == display_id; });
123 138
124 if (itr == displays.end()) { 139 if (itr == displays.end()) {
125 return nullptr; 140 return nullptr;
@@ -128,57 +143,41 @@ const Display* NVFlinger::FindDisplay(u64 display_id) const {
128 return &*itr; 143 return &*itr;
129} 144}
130 145
131Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) { 146VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) {
132 auto* const display = FindDisplay(display_id); 147 auto* const display = FindDisplay(display_id);
133 148
134 if (display == nullptr) { 149 if (display == nullptr) {
135 return nullptr; 150 return nullptr;
136 } 151 }
137 152
138 const auto itr = std::find_if(display->layers.begin(), display->layers.end(), 153 return display->FindLayer(layer_id);
139 [&](const Layer& layer) { return layer.id == layer_id; });
140
141 if (itr == display->layers.end()) {
142 return nullptr;
143 }
144
145 return &*itr;
146} 154}
147 155
148const Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const { 156const VI::Layer* NVFlinger::FindLayer(u64 display_id, u64 layer_id) const {
149 const auto* const display = FindDisplay(display_id); 157 const auto* const display = FindDisplay(display_id);
150 158
151 if (display == nullptr) { 159 if (display == nullptr) {
152 return nullptr; 160 return nullptr;
153 } 161 }
154 162
155 const auto itr = std::find_if(display->layers.begin(), display->layers.end(), 163 return display->FindLayer(layer_id);
156 [&](const Layer& layer) { return layer.id == layer_id; });
157
158 if (itr == display->layers.end()) {
159 return nullptr;
160 }
161
162 return &*itr;
163} 164}
164 165
165void NVFlinger::Compose() { 166void NVFlinger::Compose() {
166 for (auto& display : displays) { 167 for (auto& display : displays) {
167 // Trigger vsync for this display at the end of drawing 168 // Trigger vsync for this display at the end of drawing
168 SCOPE_EXIT({ display.vsync_event.writable->Signal(); }); 169 SCOPE_EXIT({ display.SignalVSyncEvent(); });
169 170
170 // Don't do anything for displays without layers. 171 // Don't do anything for displays without layers.
171 if (display.layers.empty()) 172 if (!display.HasLayers())
172 continue; 173 continue;
173 174
174 // TODO(Subv): Support more than 1 layer. 175 // TODO(Subv): Support more than 1 layer.
175 ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported"); 176 VI::Layer& layer = display.GetLayer(0);
176 177 auto& buffer_queue = layer.GetBufferQueue();
177 Layer& layer = display.layers[0];
178 auto& buffer_queue = layer.buffer_queue;
179 178
180 // Search for a queued buffer and acquire it 179 // Search for a queued buffer and acquire it
181 auto buffer = buffer_queue->AcquireBuffer(); 180 auto buffer = buffer_queue.AcquireBuffer();
182 181
183 MicroProfileFlip(); 182 MicroProfileFlip();
184 183
@@ -187,7 +186,7 @@ void NVFlinger::Compose() {
187 186
188 // There was no queued buffer to draw, render previous frame 187 // There was no queued buffer to draw, render previous frame
189 system_instance.GetPerfStats().EndGameFrame(); 188 system_instance.GetPerfStats().EndGameFrame();
190 system_instance.Renderer().SwapBuffers({}); 189 system_instance.GPU().SwapBuffers({});
191 continue; 190 continue;
192 } 191 }
193 192
@@ -203,19 +202,8 @@ void NVFlinger::Compose() {
203 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, 202 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
204 buffer->get().transform, buffer->get().crop_rect); 203 buffer->get().transform, buffer->get().crop_rect);
205 204
206 buffer_queue->ReleaseBuffer(buffer->get().slot); 205 buffer_queue.ReleaseBuffer(buffer->get().slot);
207 } 206 }
208} 207}
209 208
210Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
211Layer::~Layer() = default;
212
213Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
214 auto& kernel = Core::System::GetInstance().Kernel();
215 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
216 fmt::format("Display VSync Event {}", id));
217}
218
219Display::~Display() = default;
220
221} // namespace Service::NVFlinger 209} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 437aa592d..c0a83fffb 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -4,7 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <memory> 7#include <memory>
9#include <optional> 8#include <optional>
10#include <string> 9#include <string>
@@ -26,31 +25,17 @@ class WritableEvent;
26 25
27namespace Service::Nvidia { 26namespace Service::Nvidia {
28class Module; 27class Module;
29} 28} // namespace Service::Nvidia
29
30namespace Service::VI {
31class Display;
32class Layer;
33} // namespace Service::VI
30 34
31namespace Service::NVFlinger { 35namespace Service::NVFlinger {
32 36
33class BufferQueue; 37class BufferQueue;
34 38
35struct Layer {
36 Layer(u64 id, std::shared_ptr<BufferQueue> queue);
37 ~Layer();
38
39 u64 id;
40 std::shared_ptr<BufferQueue> buffer_queue;
41};
42
43struct Display {
44 Display(u64 id, std::string name);
45 ~Display();
46
47 u64 id;
48 std::string name;
49
50 std::vector<Layer> layers;
51 Kernel::EventPair vsync_event;
52};
53
54class NVFlinger final { 39class NVFlinger final {
55public: 40public:
56 explicit NVFlinger(Core::Timing::CoreTiming& core_timing); 41 explicit NVFlinger(Core::Timing::CoreTiming& core_timing);
@@ -80,7 +65,10 @@ public:
80 Kernel::SharedPtr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; 65 Kernel::SharedPtr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const;
81 66
82 /// Obtains a buffer queue identified by the ID. 67 /// Obtains a buffer queue identified by the ID.
83 std::shared_ptr<BufferQueue> FindBufferQueue(u32 id) const; 68 BufferQueue& FindBufferQueue(u32 id);
69
70 /// Obtains a buffer queue identified by the ID.
71 const BufferQueue& FindBufferQueue(u32 id) const;
84 72
85 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 73 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
86 /// finished. 74 /// finished.
@@ -88,27 +76,21 @@ public:
88 76
89private: 77private:
90 /// Finds the display identified by the specified ID. 78 /// Finds the display identified by the specified ID.
91 Display* FindDisplay(u64 display_id); 79 VI::Display* FindDisplay(u64 display_id);
92 80
93 /// Finds the display identified by the specified ID. 81 /// Finds the display identified by the specified ID.
94 const Display* FindDisplay(u64 display_id) const; 82 const VI::Display* FindDisplay(u64 display_id) const;
95 83
96 /// Finds the layer identified by the specified ID in the desired display. 84 /// Finds the layer identified by the specified ID in the desired display.
97 Layer* FindLayer(u64 display_id, u64 layer_id); 85 VI::Layer* FindLayer(u64 display_id, u64 layer_id);
98 86
99 /// Finds the layer identified by the specified ID in the desired display. 87 /// Finds the layer identified by the specified ID in the desired display.
100 const Layer* FindLayer(u64 display_id, u64 layer_id) const; 88 const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const;
101 89
102 std::shared_ptr<Nvidia::Module> nvdrv; 90 std::shared_ptr<Nvidia::Module> nvdrv;
103 91
104 std::array<Display, 5> displays{{ 92 std::vector<VI::Display> displays;
105 {0, "Default"}, 93 std::vector<BufferQueue> buffer_queues;
106 {1, "External"},
107 {2, "Edid"},
108 {3, "Internal"},
109 {4, "Null"},
110 }};
111 std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
112 94
113 /// Id to use for the next layer that is created, this counter is shared among all displays. 95 /// Id to use for the next layer that is created, this counter is shared among all displays.
114 u64 next_layer_id = 1; 96 u64 next_layer_id = 1;
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 117f87a45..00806b0ed 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -11,7 +11,6 @@
11#include "core/hle/ipc.h" 11#include "core/hle/ipc.h"
12#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/client_port.h" 13#include "core/hle/kernel/client_port.h"
14#include "core/hle/kernel/handle_table.h"
15#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/process.h" 15#include "core/hle/kernel/process.h"
17#include "core/hle/kernel/server_port.h" 16#include "core/hle/kernel/server_port.h"
@@ -76,7 +75,8 @@ namespace Service {
76 * Creates a function string for logging, complete with the name (or header code, depending 75 * Creates a function string for logging, complete with the name (or header code, depending
77 * on what's passed in) the port name, and all the cmd_buff arguments. 76 * on what's passed in) the port name, and all the cmd_buff arguments.
78 */ 77 */
79[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name, 78[[maybe_unused]] static std::string MakeFunctionString(std::string_view name,
79 std::string_view port_name,
80 const u32* cmd_buff) { 80 const u32* cmd_buff) {
81 // Number of params == bits 0-5 + bits 6-11 81 // Number of params == bits 0-5 + bits 6-11
82 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); 82 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
@@ -158,9 +158,7 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
158 return ReportUnimplementedFunction(ctx, info); 158 return ReportUnimplementedFunction(ctx, info);
159 } 159 }
160 160
161 LOG_TRACE( 161 LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
162 Service, "{}",
163 MakeFunctionString(info->name, GetServiceName().c_str(), ctx.CommandBuffer()).c_str());
164 handler_invoker(this, info->handler_callback, ctx); 162 handler_invoker(this, info->handler_callback, ctx);
165} 163}
166 164
@@ -169,7 +167,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
169 case IPC::CommandType::Close: { 167 case IPC::CommandType::Close: {
170 IPC::ResponseBuilder rb{context, 2}; 168 IPC::ResponseBuilder rb{context, 2};
171 rb.Push(RESULT_SUCCESS); 169 rb.Push(RESULT_SUCCESS);
172 return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead); 170 return IPC::ERR_REMOTE_PROCESS_DEAD;
173 } 171 }
174 case IPC::CommandType::ControlWithContext: 172 case IPC::CommandType::ControlWithContext:
175 case IPC::CommandType::Control: { 173 case IPC::CommandType::Control: {
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index 74da4d5e6..e9ee73710 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -30,7 +30,7 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
30 30
31 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; 31 IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
32 rb.Push(RESULT_SUCCESS); 32 rb.Push(RESULT_SUCCESS);
33 Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client}; 33 Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->GetParent()->client};
34 rb.PushMoveObjects(session); 34 rb.PushMoveObjects(session);
35 35
36 LOG_DEBUG(Service, "session={}", session->GetObjectId()); 36 LOG_DEBUG(Service, "session={}", session->GetObjectId());
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index bef25433e..b9d6381b4 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -67,7 +67,7 @@ public:
67 if (port == nullptr) { 67 if (port == nullptr) {
68 return nullptr; 68 return nullptr;
69 } 69 }
70 return std::static_pointer_cast<T>(port->hle_handler); 70 return std::static_pointer_cast<T>(port->GetHLEHandler());
71 } 71 }
72 72
73 void InvokeControlRequest(Kernel::HLERequestContext& context); 73 void InvokeControlRequest(Kernel::HLERequestContext& context);
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
new file mode 100644
index 000000000..01d80311b
--- /dev/null
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -0,0 +1,71 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <utility>
7
8#include <fmt/format.h>
9
10#include "common/assert.h"
11#include "core/core.h"
12#include "core/hle/kernel/readable_event.h"
13#include "core/hle/service/vi/display/vi_display.h"
14#include "core/hle/service/vi/layer/vi_layer.h"
15
16namespace Service::VI {
17
18Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} {
19 auto& kernel = Core::System::GetInstance().Kernel();
20 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
21 fmt::format("Display VSync Event {}", id));
22}
23
24Display::~Display() = default;
25
26Layer& Display::GetLayer(std::size_t index) {
27 return layers.at(index);
28}
29
30const Layer& Display::GetLayer(std::size_t index) const {
31 return layers.at(index);
32}
33
34Kernel::SharedPtr<Kernel::ReadableEvent> Display::GetVSyncEvent() const {
35 return vsync_event.readable;
36}
37
38void Display::SignalVSyncEvent() {
39 vsync_event.writable->Signal();
40}
41
42void Display::CreateLayer(u64 id, NVFlinger::BufferQueue& buffer_queue) {
43 // TODO(Subv): Support more than 1 layer.
44 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
45
46 layers.emplace_back(id, buffer_queue);
47}
48
49Layer* Display::FindLayer(u64 id) {
50 const auto itr = std::find_if(layers.begin(), layers.end(),
51 [id](const VI::Layer& layer) { return layer.GetID() == id; });
52
53 if (itr == layers.end()) {
54 return nullptr;
55 }
56
57 return &*itr;
58}
59
60const Layer* Display::FindLayer(u64 id) const {
61 const auto itr = std::find_if(layers.begin(), layers.end(),
62 [id](const VI::Layer& layer) { return layer.GetID() == id; });
63
64 if (itr == layers.end()) {
65 return nullptr;
66 }
67
68 return &*itr;
69}
70
71} // namespace Service::VI
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
new file mode 100644
index 000000000..2acd46ff8
--- /dev/null
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -0,0 +1,98 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <vector>
9
10#include "common/common_types.h"
11#include "core/hle/kernel/writable_event.h"
12
13namespace Service::NVFlinger {
14class BufferQueue;
15}
16
17namespace Service::VI {
18
19class Layer;
20
21/// Represents a single display type
22class Display {
23public:
24 /// Constructs a display with a given unique ID and name.
25 ///
26 /// @param id The unique ID for this display.
27 /// @param name The name for this display.
28 ///
29 Display(u64 id, std::string name);
30 ~Display();
31
32 Display(const Display&) = delete;
33 Display& operator=(const Display&) = delete;
34
35 Display(Display&&) = default;
36 Display& operator=(Display&&) = default;
37
38 /// Gets the unique ID assigned to this display.
39 u64 GetID() const {
40 return id;
41 }
42
43 /// Gets the name of this display
44 const std::string& GetName() const {
45 return name;
46 }
47
48 /// Whether or not this display has any layers added to it.
49 bool HasLayers() const {
50 return !layers.empty();
51 }
52
53 /// Gets a layer for this display based off an index.
54 Layer& GetLayer(std::size_t index);
55
56 /// Gets a layer for this display based off an index.
57 const Layer& GetLayer(std::size_t index) const;
58
59 /// Gets the readable vsync event.
60 Kernel::SharedPtr<Kernel::ReadableEvent> GetVSyncEvent() const;
61
62 /// Signals the internal vsync event.
63 void SignalVSyncEvent();
64
65 /// Creates and adds a layer to this display with the given ID.
66 ///
67 /// @param id The ID to assign to the created layer.
68 /// @param buffer_queue The buffer queue for the layer instance to use.
69 ///
70 void CreateLayer(u64 id, NVFlinger::BufferQueue& buffer_queue);
71
72 /// Attempts to find a layer with the given ID.
73 ///
74 /// @param id The layer ID.
75 ///
76 /// @returns If found, the Layer instance with the given ID.
77 /// If not found, then nullptr is returned.
78 ///
79 Layer* FindLayer(u64 id);
80
81 /// Attempts to find a layer with the given ID.
82 ///
83 /// @param id The layer ID.
84 ///
85 /// @returns If found, the Layer instance with the given ID.
86 /// If not found, then nullptr is returned.
87 ///
88 const Layer* FindLayer(u64 id) const;
89
90private:
91 u64 id;
92 std::string name;
93
94 std::vector<Layer> layers;
95 Kernel::EventPair vsync_event;
96};
97
98} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
new file mode 100644
index 000000000..954225c26
--- /dev/null
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -0,0 +1,13 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/vi/layer/vi_layer.h"
6
7namespace Service::VI {
8
9Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : id{id}, buffer_queue{queue} {}
10
11Layer::~Layer() = default;
12
13} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
new file mode 100644
index 000000000..c6bfd01f6
--- /dev/null
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -0,0 +1,52 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Service::NVFlinger {
10class BufferQueue;
11}
12
13namespace Service::VI {
14
15/// Represents a single display layer.
16class Layer {
17public:
18 /// Constructs a layer with a given ID and buffer queue.
19 ///
20 /// @param id The ID to assign to this layer.
21 /// @param queue The buffer queue for this layer to use.
22 ///
23 Layer(u64 id, NVFlinger::BufferQueue& queue);
24 ~Layer();
25
26 Layer(const Layer&) = delete;
27 Layer& operator=(const Layer&) = delete;
28
29 Layer(Layer&&) = default;
30 Layer& operator=(Layer&&) = delete;
31
32 /// Gets the ID for this layer.
33 u64 GetID() const {
34 return id;
35 }
36
37 /// Gets a reference to the buffer queue this layer is using.
38 NVFlinger::BufferQueue& GetBufferQueue() {
39 return buffer_queue;
40 }
41
42 /// Gets a const reference to the buffer queue this layer is using.
43 const NVFlinger::BufferQueue& GetBufferQueue() const {
44 return buffer_queue;
45 }
46
47private:
48 u64 id;
49 NVFlinger::BufferQueue& buffer_queue;
50};
51
52} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index a317a2885..566cd6006 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -24,6 +24,7 @@
24#include "core/hle/service/nvdrv/nvdrv.h" 24#include "core/hle/service/nvdrv/nvdrv.h"
25#include "core/hle/service/nvflinger/buffer_queue.h" 25#include "core/hle/service/nvflinger/buffer_queue.h"
26#include "core/hle/service/nvflinger/nvflinger.h" 26#include "core/hle/service/nvflinger/nvflinger.h"
27#include "core/hle/service/service.h"
27#include "core/hle/service/vi/vi.h" 28#include "core/hle/service/vi/vi.h"
28#include "core/hle/service/vi/vi_m.h" 29#include "core/hle/service/vi/vi_m.h"
29#include "core/hle/service/vi/vi_s.h" 30#include "core/hle/service/vi/vi_s.h"
@@ -33,6 +34,7 @@
33namespace Service::VI { 34namespace Service::VI {
34 35
35constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1}; 36constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
37constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
36constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6}; 38constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
37constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7}; 39constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
38 40
@@ -420,7 +422,7 @@ public:
420 u32_le fence_is_valid; 422 u32_le fence_is_valid;
421 std::array<Fence, 2> fences; 423 std::array<Fence, 2> fences;
422 424
423 MathUtil::Rectangle<int> GetCropRect() const { 425 Common::Rectangle<int> GetCropRect() const {
424 return {crop_left, crop_top, crop_right, crop_bottom}; 426 return {crop_left, crop_top, crop_right, crop_bottom};
425 } 427 }
426 }; 428 };
@@ -525,7 +527,7 @@ private:
525 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 527 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
526 static_cast<u32>(transaction), flags); 528 static_cast<u32>(transaction), flags);
527 529
528 auto buffer_queue = nv_flinger->FindBufferQueue(id); 530 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
529 531
530 if (transaction == TransactionId::Connect) { 532 if (transaction == TransactionId::Connect) {
531 IGBPConnectRequestParcel request{ctx.ReadBuffer()}; 533 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
@@ -538,7 +540,7 @@ private:
538 } else if (transaction == TransactionId::SetPreallocatedBuffer) { 540 } else if (transaction == TransactionId::SetPreallocatedBuffer) {
539 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()}; 541 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
540 542
541 buffer_queue->SetPreallocatedBuffer(request.data.slot, request.buffer); 543 buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer);
542 544
543 IGBPSetPreallocatedBufferResponseParcel response{}; 545 IGBPSetPreallocatedBufferResponseParcel response{};
544 ctx.WriteBuffer(response.Serialize()); 546 ctx.WriteBuffer(response.Serialize());
@@ -546,7 +548,7 @@ private:
546 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()}; 548 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
547 const u32 width{request.data.width}; 549 const u32 width{request.data.width};
548 const u32 height{request.data.height}; 550 const u32 height{request.data.height};
549 std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 551 std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height);
550 552
551 if (slot) { 553 if (slot) {
552 // Buffer is available 554 // Buffer is available
@@ -559,8 +561,8 @@ private:
559 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, 561 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
560 Kernel::ThreadWakeupReason reason) { 562 Kernel::ThreadWakeupReason reason) {
561 // Repeat TransactParcel DequeueBuffer when a buffer is available 563 // Repeat TransactParcel DequeueBuffer when a buffer is available
562 auto buffer_queue = nv_flinger->FindBufferQueue(id); 564 auto& buffer_queue = nv_flinger->FindBufferQueue(id);
563 std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height); 565 std::optional<u32> slot = buffer_queue.DequeueBuffer(width, height);
564 ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer."); 566 ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
565 567
566 IGBPDequeueBufferResponseParcel response{*slot}; 568 IGBPDequeueBufferResponseParcel response{*slot};
@@ -568,28 +570,28 @@ private:
568 IPC::ResponseBuilder rb{ctx, 2}; 570 IPC::ResponseBuilder rb{ctx, 2};
569 rb.Push(RESULT_SUCCESS); 571 rb.Push(RESULT_SUCCESS);
570 }, 572 },
571 buffer_queue->GetWritableBufferWaitEvent()); 573 buffer_queue.GetWritableBufferWaitEvent());
572 } 574 }
573 } else if (transaction == TransactionId::RequestBuffer) { 575 } else if (transaction == TransactionId::RequestBuffer) {
574 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()}; 576 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
575 577
576 auto& buffer = buffer_queue->RequestBuffer(request.slot); 578 auto& buffer = buffer_queue.RequestBuffer(request.slot);
577 579
578 IGBPRequestBufferResponseParcel response{buffer}; 580 IGBPRequestBufferResponseParcel response{buffer};
579 ctx.WriteBuffer(response.Serialize()); 581 ctx.WriteBuffer(response.Serialize());
580 } else if (transaction == TransactionId::QueueBuffer) { 582 } else if (transaction == TransactionId::QueueBuffer) {
581 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()}; 583 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
582 584
583 buffer_queue->QueueBuffer(request.data.slot, request.data.transform, 585 buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
584 request.data.GetCropRect()); 586 request.data.GetCropRect());
585 587
586 IGBPQueueBufferResponseParcel response{1280, 720}; 588 IGBPQueueBufferResponseParcel response{1280, 720};
587 ctx.WriteBuffer(response.Serialize()); 589 ctx.WriteBuffer(response.Serialize());
588 } else if (transaction == TransactionId::Query) { 590 } else if (transaction == TransactionId::Query) {
589 IGBPQueryRequestParcel request{ctx.ReadBuffer()}; 591 IGBPQueryRequestParcel request{ctx.ReadBuffer()};
590 592
591 u32 value = 593 const u32 value =
592 buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type)); 594 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
593 595
594 IGBPQueryResponseParcel response{value}; 596 IGBPQueryResponseParcel response{value};
595 ctx.WriteBuffer(response.Serialize()); 597 ctx.WriteBuffer(response.Serialize());
@@ -629,12 +631,12 @@ private:
629 631
630 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 632 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
631 633
632 const auto buffer_queue = nv_flinger->FindBufferQueue(id); 634 const auto& buffer_queue = nv_flinger->FindBufferQueue(id);
633 635
634 // TODO(Subv): Find out what this actually is. 636 // TODO(Subv): Find out what this actually is.
635 IPC::ResponseBuilder rb{ctx, 2, 1}; 637 IPC::ResponseBuilder rb{ctx, 2, 1};
636 rb.Push(RESULT_SUCCESS); 638 rb.Push(RESULT_SUCCESS);
637 rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent()); 639 rb.PushCopyObjects(buffer_queue.GetBufferWaitEvent());
638 } 640 }
639 641
640 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 642 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
@@ -752,6 +754,7 @@ public:
752 {1102, nullptr, "GetDisplayResolution"}, 754 {1102, nullptr, "GetDisplayResolution"},
753 {2010, &IManagerDisplayService::CreateManagedLayer, "CreateManagedLayer"}, 755 {2010, &IManagerDisplayService::CreateManagedLayer, "CreateManagedLayer"},
754 {2011, nullptr, "DestroyManagedLayer"}, 756 {2011, nullptr, "DestroyManagedLayer"},
757 {2012, nullptr, "CreateStrayLayer"},
755 {2050, nullptr, "CreateIndirectLayer"}, 758 {2050, nullptr, "CreateIndirectLayer"},
756 {2051, nullptr, "DestroyIndirectLayer"}, 759 {2051, nullptr, "DestroyIndirectLayer"},
757 {2052, nullptr, "CreateIndirectProducerEndPoint"}, 760 {2052, nullptr, "CreateIndirectProducerEndPoint"},
@@ -1202,26 +1205,40 @@ IApplicationDisplayService::IApplicationDisplayService(
1202 RegisterHandlers(functions); 1205 RegisterHandlers(functions);
1203} 1206}
1204 1207
1205Module::Interface::Interface(std::shared_ptr<Module> module, const char* name, 1208static bool IsValidServiceAccess(Permission permission, Policy policy) {
1206 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 1209 if (permission == Permission::User) {
1207 : ServiceFramework(name), module(std::move(module)), nv_flinger(std::move(nv_flinger)) {} 1210 return policy == Policy::User;
1211 }
1212
1213 if (permission == Permission::System || permission == Permission::Manager) {
1214 return policy == Policy::User || policy == Policy::Compositor;
1215 }
1208 1216
1209Module::Interface::~Interface() = default; 1217 return false;
1218}
1210 1219
1211void Module::Interface::GetDisplayService(Kernel::HLERequestContext& ctx) { 1220void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
1212 LOG_WARNING(Service_VI, "(STUBBED) called"); 1221 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger,
1222 Permission permission) {
1223 IPC::RequestParser rp{ctx};
1224 const auto policy = rp.PopEnum<Policy>();
1225
1226 if (!IsValidServiceAccess(permission, policy)) {
1227 IPC::ResponseBuilder rb{ctx, 2};
1228 rb.Push(ERR_PERMISSION_DENIED);
1229 return;
1230 }
1213 1231
1214 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1232 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1215 rb.Push(RESULT_SUCCESS); 1233 rb.Push(RESULT_SUCCESS);
1216 rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger); 1234 rb.PushIpcInterface<IApplicationDisplayService>(std::move(nv_flinger));
1217} 1235}
1218 1236
1219void InstallInterfaces(SM::ServiceManager& service_manager, 1237void InstallInterfaces(SM::ServiceManager& service_manager,
1220 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) { 1238 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
1221 auto module = std::make_shared<Module>(); 1239 std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
1222 std::make_shared<VI_M>(module, nv_flinger)->InstallAsService(service_manager); 1240 std::make_shared<VI_S>(nv_flinger)->InstallAsService(service_manager);
1223 std::make_shared<VI_S>(module, nv_flinger)->InstallAsService(service_manager); 1241 std::make_shared<VI_U>(nv_flinger)->InstallAsService(service_manager);
1224 std::make_shared<VI_U>(module, nv_flinger)->InstallAsService(service_manager);
1225} 1242}
1226 1243
1227} // namespace Service::VI 1244} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index e3963502a..6b66f8b81 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -4,12 +4,21 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/service.h" 7#include <memory>
8#include "common/common_types.h"
9
10namespace Kernel {
11class HLERequestContext;
12}
8 13
9namespace Service::NVFlinger { 14namespace Service::NVFlinger {
10class NVFlinger; 15class NVFlinger;
11} 16}
12 17
18namespace Service::SM {
19class ServiceManager;
20}
21
13namespace Service::VI { 22namespace Service::VI {
14 23
15enum class DisplayResolution : u32 { 24enum class DisplayResolution : u32 {
@@ -19,22 +28,25 @@ enum class DisplayResolution : u32 {
19 UndockedHeight = 720, 28 UndockedHeight = 720,
20}; 29};
21 30
22class Module final { 31/// Permission level for a particular VI service instance
23public: 32enum class Permission {
24 class Interface : public ServiceFramework<Interface> { 33 User,
25 public: 34 System,
26 explicit Interface(std::shared_ptr<Module> module, const char* name, 35 Manager,
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 36};
28 ~Interface() override;
29
30 void GetDisplayService(Kernel::HLERequestContext& ctx);
31 37
32 protected: 38/// A policy type that may be requested via GetDisplayService and
33 std::shared_ptr<Module> module; 39/// GetDisplayServiceWithProxyNameExchange
34 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; 40enum class Policy {
35 }; 41 User,
42 Compositor,
36}; 43};
37 44
45namespace detail {
46void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx,
47 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger, Permission permission);
48} // namespace detail
49
38/// Registers all VI services with the specified service manager. 50/// Registers all VI services with the specified service manager.
39void InstallInterfaces(SM::ServiceManager& service_manager, 51void InstallInterfaces(SM::ServiceManager& service_manager,
40 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 52 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 207c06b16..06070087f 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -2,12 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/service/vi/vi.h"
5#include "core/hle/service/vi/vi_m.h" 7#include "core/hle/service/vi/vi_m.h"
6 8
7namespace Service::VI { 9namespace Service::VI {
8 10
9VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
10 : Module::Interface(std::move(module), "vi:m", std::move(nv_flinger)) { 12 : ServiceFramework{"vi:m"}, nv_flinger{std::move(nv_flinger)} {
11 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
12 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 14 {2, &VI_M::GetDisplayService, "GetDisplayService"},
13 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_M::VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
17 19
18VI_M::~VI_M() = default; 20VI_M::~VI_M() = default;
19 21
22void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called");
24
25 detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::Manager);
26}
27
20} // namespace Service::VI 28} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index 487d58d50..290e06689 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -4,14 +4,27 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/vi/vi.h" 7#include "core/hle/service/service.h"
8
9namespace Kernel {
10class HLERequestContext;
11}
12
13namespace Service::NVFlinger {
14class NVFlinger;
15}
8 16
9namespace Service::VI { 17namespace Service::VI {
10 18
11class VI_M final : public Module::Interface { 19class VI_M final : public ServiceFramework<VI_M> {
12public: 20public:
13 explicit VI_M(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_M() override; 22 ~VI_M() override;
23
24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
15}; 28};
16 29
17} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 920e6a1f6..57c596cc4 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -2,12 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/service/vi/vi.h"
5#include "core/hle/service/vi/vi_s.h" 7#include "core/hle/service/vi/vi_s.h"
6 8
7namespace Service::VI { 9namespace Service::VI {
8 10
9VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_S::VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
10 : Module::Interface(std::move(module), "vi:s", std::move(nv_flinger)) { 12 : ServiceFramework{"vi:s"}, nv_flinger{std::move(nv_flinger)} {
11 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
12 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 14 {1, &VI_S::GetDisplayService, "GetDisplayService"},
13 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -17,4 +19,10 @@ VI_S::VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
17 19
18VI_S::~VI_S() = default; 20VI_S::~VI_S() = default;
19 21
22void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called");
24
25 detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::System);
26}
27
20} // namespace Service::VI 28} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index bbc31148f..47804dc0b 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -4,14 +4,27 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/vi/vi.h" 7#include "core/hle/service/service.h"
8
9namespace Kernel {
10class HLERequestContext;
11}
12
13namespace Service::NVFlinger {
14class NVFlinger;
15}
8 16
9namespace Service::VI { 17namespace Service::VI {
10 18
11class VI_S final : public Module::Interface { 19class VI_S final : public ServiceFramework<VI_S> {
12public: 20public:
13 explicit VI_S(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_S(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_S() override; 22 ~VI_S() override;
23
24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
15}; 28};
16 29
17} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index d81e410d6..9d5ceb608 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -2,12 +2,14 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/logging/log.h"
6#include "core/hle/service/vi/vi.h"
5#include "core/hle/service/vi/vi_u.h" 7#include "core/hle/service/vi/vi_u.h"
6 8
7namespace Service::VI { 9namespace Service::VI {
8 10
9VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) 11VI_U::VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
10 : Module::Interface(std::move(module), "vi:u", std::move(nv_flinger)) { 12 : ServiceFramework{"vi:u"}, nv_flinger{std::move(nv_flinger)} {
11 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
12 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 14 {0, &VI_U::GetDisplayService, "GetDisplayService"},
13 }; 15 };
@@ -16,4 +18,10 @@ VI_U::VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger>
16 18
17VI_U::~VI_U() = default; 19VI_U::~VI_U() = default;
18 20
21void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
22 LOG_DEBUG(Service_VI, "called");
23
24 detail::GetDisplayServiceImpl(ctx, nv_flinger, Permission::User);
25}
26
19} // namespace Service::VI 27} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index b92f28c92..19bdb73b0 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -4,14 +4,27 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/vi/vi.h" 7#include "core/hle/service/service.h"
8
9namespace Kernel {
10class HLERequestContext;
11}
12
13namespace Service::NVFlinger {
14class NVFlinger;
15}
8 16
9namespace Service::VI { 17namespace Service::VI {
10 18
11class VI_U final : public Module::Interface { 19class VI_U final : public ServiceFramework<VI_U> {
12public: 20public:
13 explicit VI_U(std::shared_ptr<Module> module, std::shared_ptr<NVFlinger::NVFlinger> nv_flinger); 21 explicit VI_U(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
14 ~VI_U() override; 22 ~VI_U() override;
23
24private:
25 void GetDisplayService(Kernel::HLERequestContext& ctx);
26
27 std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
15}; 28};
16 29
17} // namespace Service::VI 30} // namespace Service::VI
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index e9166dbd9..4fde53033 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -67,19 +67,27 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa
67 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, 67 LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE,
68 (base + size) * PAGE_SIZE); 68 (base + size) * PAGE_SIZE);
69 69
70 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE, 70 // During boot, current_page_table might not be set yet, in which case we need not flush
71 FlushMode::FlushAndInvalidate); 71 if (current_page_table) {
72 RasterizerFlushVirtualRegion(base << PAGE_BITS, size * PAGE_SIZE,
73 FlushMode::FlushAndInvalidate);
74 }
72 75
73 VAddr end = base + size; 76 VAddr end = base + size;
74 while (base != end) { 77 ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",
75 ASSERT_MSG(base < page_table.pointers.size(), "out of range mapping at {:016X}", base); 78 base + page_table.pointers.size());
79
80 std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type);
76 81
77 page_table.attributes[base] = type; 82 if (memory == nullptr) {
78 page_table.pointers[base] = memory; 83 std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, memory);
84 } else {
85 while (base != end) {
86 page_table.pointers[base] = memory;
79 87
80 base += 1; 88 base += 1;
81 if (memory != nullptr)
82 memory += PAGE_SIZE; 89 memory += PAGE_SIZE;
90 }
83 } 91 }
84} 92}
85 93
@@ -166,9 +174,6 @@ T Read(const VAddr vaddr) {
166 return value; 174 return value;
167 } 175 }
168 176
169 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
170 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
171
172 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 177 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
173 switch (type) { 178 switch (type) {
174 case PageType::Unmapped: 179 case PageType::Unmapped:
@@ -199,9 +204,6 @@ void Write(const VAddr vaddr, const T data) {
199 return; 204 return;
200 } 205 }
201 206
202 // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state
203 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
204
205 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; 207 PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
206 switch (type) { 208 switch (type) {
207 case PageType::Unmapped: 209 case PageType::Unmapped:
@@ -357,16 +359,16 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
357 const VAddr overlap_end = std::min(end, region_end); 359 const VAddr overlap_end = std::min(end, region_end);
358 const VAddr overlap_size = overlap_end - overlap_start; 360 const VAddr overlap_size = overlap_end - overlap_start;
359 361
360 auto& rasterizer = system_instance.Renderer().Rasterizer(); 362 auto& gpu = system_instance.GPU();
361 switch (mode) { 363 switch (mode) {
362 case FlushMode::Flush: 364 case FlushMode::Flush:
363 rasterizer.FlushRegion(overlap_start, overlap_size); 365 gpu.FlushRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
364 break; 366 break;
365 case FlushMode::Invalidate: 367 case FlushMode::Invalidate:
366 rasterizer.InvalidateRegion(overlap_start, overlap_size); 368 gpu.InvalidateRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
367 break; 369 break;
368 case FlushMode::FlushAndInvalidate: 370 case FlushMode::FlushAndInvalidate:
369 rasterizer.FlushAndInvalidateRegion(overlap_start, overlap_size); 371 gpu.FlushAndInvalidateRegion(ToCacheAddr(GetPointer(overlap_start)), overlap_size);
370 break; 372 break;
371 } 373 }
372 }; 374 };
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 2e232e1e7..6dd3139cc 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -91,7 +91,10 @@ void LogSettings() {
91 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); 91 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
92 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); 92 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
93 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit); 93 LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
94 LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
94 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation); 95 LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
96 LogSetting("Renderer_UseAsynchronousGpuEmulation",
97 Settings::values.use_asynchronous_gpu_emulation);
95 LogSetting("Audio_OutputEngine", Settings::values.sink_id); 98 LogSetting("Audio_OutputEngine", Settings::values.sink_id);
96 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 99 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
97 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); 100 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
diff --git a/src/core/settings.h b/src/core/settings.h
index 7e76e0466..cdfb2f742 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -393,6 +393,7 @@ struct Values {
393 u16 frame_limit; 393 u16 frame_limit;
394 bool use_disk_shader_cache; 394 bool use_disk_shader_cache;
395 bool use_accurate_gpu_emulation; 395 bool use_accurate_gpu_emulation;
396 bool use_asynchronous_gpu_emulation;
396 397
397 float bg_red; 398 float bg_red;
398 float bg_green; 399 float bg_green;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 58dfcc4df..e1db06811 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -162,6 +162,8 @@ TelemetrySession::TelemetrySession() {
162 Settings::values.use_disk_shader_cache); 162 Settings::values.use_disk_shader_cache);
163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation", 163 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAccurateGpuEmulation",
164 Settings::values.use_accurate_gpu_emulation); 164 Settings::values.use_accurate_gpu_emulation);
165 AddField(Telemetry::FieldType::UserConfig, "Renderer_UseAsynchronousGpuEmulation",
166 Settings::values.use_asynchronous_gpu_emulation);
165 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode", 167 AddField(Telemetry::FieldType::UserConfig, "System_UseDockedMode",
166 Settings::values.use_docked_mode); 168 Settings::values.use_docked_mode);
167} 169}