summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_core/renderer/command/effect/biquad_filter.cpp38
-rw-r--r--src/audio_core/renderer/voice/voice_state.h8
-rw-r--r--src/audio_core/sink/cubeb_sink.cpp20
-rw-r--r--src/common/input.h2
-rw-r--r--src/common/settings.h1
-rw-r--r--src/core/CMakeLists.txt42
-rw-r--r--src/core/arm/arm_interface.h1
-rw-r--r--src/core/hardware_properties.h20
-rw-r--r--src/core/hid/emulated_controller.cpp75
-rw-r--r--src/core/hid/emulated_controller.h6
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_capabilities.cpp358
-rw-r--r--src/core/hle/kernel/k_capabilities.h295
-rw-r--r--src/core/hle/kernel/k_device_address_space.cpp150
-rw-r--r--src/core/hle/kernel/k_device_address_space.h60
-rw-r--r--src/core/hle/kernel/k_process.cpp2
-rw-r--r--src/core/hle/kernel/kernel.cpp8
-rw-r--r--src/core/hle/kernel/kernel.h10
-rw-r--r--src/core/hle/kernel/physical_core.h1
-rw-r--r--src/core/hle/kernel/svc.cpp6406
-rw-r--r--src/core/hle/kernel/svc.h528
-rw-r--r--src/core/hle/kernel/svc/svc_activity.cpp65
-rw-r--r--src/core/hle/kernel/svc/svc_address_arbiter.cpp122
-rw-r--r--src/core/hle/kernel/svc/svc_address_translation.cpp50
-rw-r--r--src/core/hle/kernel/svc/svc_cache.cpp98
-rw-r--r--src/core/hle/kernel/svc/svc_code_memory.cpp168
-rw-r--r--src/core/hle/kernel/svc/svc_condition_variable.cpp77
-rw-r--r--src/core/hle/kernel/svc/svc_debug.cpp194
-rw-r--r--src/core/hle/kernel/svc/svc_debug_string.cpp29
-rw-r--r--src/core/hle/kernel/svc/svc_device_address_space.cpp253
-rw-r--r--src/core/hle/kernel/svc/svc_event.cpp124
-rw-r--r--src/core/hle/kernel/svc/svc_exception.cpp137
-rw-r--r--src/core/hle/kernel/svc/svc_info.cpp297
-rw-r--r--src/core/hle/kernel/svc/svc_insecure_memory.cpp35
-rw-r--r--src/core/hle/kernel/svc/svc_interrupt_event.cpp25
-rw-r--r--src/core/hle/kernel/svc/svc_io_pool.cpp71
-rw-r--r--src/core/hle/kernel/svc/svc_ipc.cpp174
-rw-r--r--src/core/hle/kernel/svc/svc_kernel_debug.cpp35
-rw-r--r--src/core/hle/kernel/svc/svc_light_ipc.cpp73
-rw-r--r--src/core/hle/kernel/svc/svc_lock.cpp66
-rw-r--r--src/core/hle/kernel/svc/svc_memory.cpp217
-rw-r--r--src/core/hle/kernel/svc/svc_physical_memory.cpp185
-rw-r--r--src/core/hle/kernel/svc/svc_port.cpp122
-rw-r--r--src/core/hle/kernel/svc/svc_power_management.cpp21
-rw-r--r--src/core/hle/kernel/svc/svc_process.cpp194
-rw-r--r--src/core/hle/kernel/svc/svc_process_memory.cpp324
-rw-r--r--src/core/hle/kernel/svc/svc_processor.cpp25
-rw-r--r--src/core/hle/kernel/svc/svc_query_memory.cpp65
-rw-r--r--src/core/hle/kernel/svc/svc_register.cpp27
-rw-r--r--src/core/hle/kernel/svc/svc_resource_limit.cpp149
-rw-r--r--src/core/hle/kernel/svc/svc_secure_monitor_call.cpp53
-rw-r--r--src/core/hle/kernel/svc/svc_session.cpp128
-rw-r--r--src/core/hle/kernel/svc/svc_shared_memory.cpp133
-rw-r--r--src/core/hle/kernel/svc/svc_synchronization.cpp163
-rw-r--r--src/core/hle/kernel/svc/svc_thread.cpp437
-rw-r--r--src/core/hle/kernel/svc/svc_thread_profiler.cpp60
-rw-r--r--src/core/hle/kernel/svc/svc_tick.cpp35
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp117
-rw-r--r--src/core/hle/kernel/svc_generator.py716
-rw-r--r--src/core/hle/kernel/svc_results.h1
-rw-r--r--src/core/hle/kernel/svc_types.h30
-rw-r--r--src/core/hle/kernel/svc_version.h58
-rw-r--r--src/core/hle/kernel/svc_wrap.h733
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp15
-rw-r--r--src/core/hle/service/hid/controllers/npad.h2
-rw-r--r--src/core/hle/service/hid/errors.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp6
-rw-r--r--src/input_common/drivers/joycon.cpp49
-rw-r--r--src/input_common/drivers/joycon.h1
-rw-r--r--src/input_common/drivers/sdl_driver.cpp18
-rw-r--r--src/input_common/helpers/joycon_driver.cpp3
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp45
-rw-r--r--src/input_common/input_poller.cpp30
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp10
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp32
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp6
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp25
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h1
-rw-r--r--src/shader_recompiler/frontend/ir/value.h11
-rw-r--r--src/tests/video_core/buffer_base.cpp2
-rw-r--r--src/video_core/buffer_cache/buffer_base.h14
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/gpu_thread.cpp6
-rw-r--r--src/video_core/gpu_thread.h8
-rw-r--r--src/video_core/host1x/vic.cpp14
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp23
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.h10
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp7
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp17
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp26
-rw-r--r--src/yuzu/CMakeLists.txt2
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp2
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui22
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp17
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp35
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.h2
-rw-r--r--src/yuzu/discord_impl.cpp67
-rw-r--r--src/yuzu/main.cpp174
-rw-r--r--src/yuzu/main.h9
-rw-r--r--src/yuzu/multiplayer/lobby.cpp16
-rw-r--r--src/yuzu/multiplayer/lobby.h2
-rw-r--r--src/yuzu/multiplayer/lobby.ui7
-rw-r--r--src/yuzu/util/overlay_dialog.cpp2
-rw-r--r--src/yuzu_cmd/config.cpp4
-rw-r--r--src/yuzu_cmd/default_ini.h30
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp48
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h10
-rw-r--r--src/yuzu_cmd/yuzu.cpp55
114 files changed, 11422 insertions, 3600 deletions
diff --git a/src/audio_core/renderer/command/effect/biquad_filter.cpp b/src/audio_core/renderer/command/effect/biquad_filter.cpp
index edb30ce72..dea6423dc 100644
--- a/src/audio_core/renderer/command/effect/biquad_filter.cpp
+++ b/src/audio_core/renderer/command/effect/biquad_filter.cpp
@@ -4,6 +4,7 @@
4#include "audio_core/renderer/adsp/command_list_processor.h" 4#include "audio_core/renderer/adsp/command_list_processor.h"
5#include "audio_core/renderer/command/effect/biquad_filter.h" 5#include "audio_core/renderer/command/effect/biquad_filter.h"
6#include "audio_core/renderer/voice/voice_state.h" 6#include "audio_core/renderer/voice/voice_state.h"
7#include "common/bit_cast.h"
7 8
8namespace AudioCore::AudioRenderer { 9namespace AudioCore::AudioRenderer {
9/** 10/**
@@ -19,21 +20,21 @@ namespace AudioCore::AudioRenderer {
19void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input, 20void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
20 std::array<s16, 3>& b_, std::array<s16, 2>& a_, 21 std::array<s16, 3>& b_, std::array<s16, 2>& a_,
21 VoiceState::BiquadFilterState& state, const u32 sample_count) { 22 VoiceState::BiquadFilterState& state, const u32 sample_count) {
22 constexpr s64 min{std::numeric_limits<s32>::min()}; 23 constexpr f64 min{std::numeric_limits<s32>::min()};
23 constexpr s64 max{std::numeric_limits<s32>::max()}; 24 constexpr f64 max{std::numeric_limits<s32>::max()};
24 std::array<f64, 3> b{Common::FixedPoint<50, 14>::from_base(b_[0]).to_double(), 25 std::array<f64, 3> b{Common::FixedPoint<50, 14>::from_base(b_[0]).to_double(),
25 Common::FixedPoint<50, 14>::from_base(b_[1]).to_double(), 26 Common::FixedPoint<50, 14>::from_base(b_[1]).to_double(),
26 Common::FixedPoint<50, 14>::from_base(b_[2]).to_double()}; 27 Common::FixedPoint<50, 14>::from_base(b_[2]).to_double()};
27 std::array<f64, 2> a{Common::FixedPoint<50, 14>::from_base(a_[0]).to_double(), 28 std::array<f64, 2> a{Common::FixedPoint<50, 14>::from_base(a_[0]).to_double(),
28 Common::FixedPoint<50, 14>::from_base(a_[1]).to_double()}; 29 Common::FixedPoint<50, 14>::from_base(a_[1]).to_double()};
29 std::array<f64, 4> s{state.s0.to_double(), state.s1.to_double(), state.s2.to_double(), 30 std::array<f64, 4> s{Common::BitCast<f64>(state.s0), Common::BitCast<f64>(state.s1),
30 state.s3.to_double()}; 31 Common::BitCast<f64>(state.s2), Common::BitCast<f64>(state.s3)};
31 32
32 for (u32 i = 0; i < sample_count; i++) { 33 for (u32 i = 0; i < sample_count; i++) {
33 f64 in_sample{static_cast<f64>(input[i])}; 34 f64 in_sample{static_cast<f64>(input[i])};
34 auto sample{in_sample * b[0] + s[0] * b[1] + s[1] * b[2] + s[2] * a[0] + s[3] * a[1]}; 35 auto sample{in_sample * b[0] + s[0] * b[1] + s[1] * b[2] + s[2] * a[0] + s[3] * a[1]};
35 36
36 output[i] = static_cast<s32>(std::clamp(static_cast<s64>(sample), min, max)); 37 output[i] = static_cast<s32>(std::clamp(sample, min, max));
37 38
38 s[1] = s[0]; 39 s[1] = s[0];
39 s[0] = in_sample; 40 s[0] = in_sample;
@@ -41,10 +42,10 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
41 s[2] = sample; 42 s[2] = sample;
42 } 43 }
43 44
44 state.s0 = s[0]; 45 state.s0 = Common::BitCast<s64>(s[0]);
45 state.s1 = s[1]; 46 state.s1 = Common::BitCast<s64>(s[1]);
46 state.s2 = s[2]; 47 state.s2 = Common::BitCast<s64>(s[2]);
47 state.s3 = s[3]; 48 state.s3 = Common::BitCast<s64>(s[3]);
48} 49}
49 50
50/** 51/**
@@ -58,29 +59,20 @@ void ApplyBiquadFilterFloat(std::span<s32> output, std::span<const s32> input,
58 * @param sample_count - Number of samples to process. 59 * @param sample_count - Number of samples to process.
59 */ 60 */
60static void ApplyBiquadFilterInt(std::span<s32> output, std::span<const s32> input, 61static void ApplyBiquadFilterInt(std::span<s32> output, std::span<const s32> input,
61 std::array<s16, 3>& b_, std::array<s16, 2>& a_, 62 std::array<s16, 3>& b, std::array<s16, 2>& a,
62 VoiceState::BiquadFilterState& state, const u32 sample_count) { 63 VoiceState::BiquadFilterState& state, const u32 sample_count) {
63 constexpr s64 min{std::numeric_limits<s32>::min()}; 64 constexpr s64 min{std::numeric_limits<s32>::min()};
64 constexpr s64 max{std::numeric_limits<s32>::max()}; 65 constexpr s64 max{std::numeric_limits<s32>::max()};
65 std::array<Common::FixedPoint<50, 14>, 3> b{
66 Common::FixedPoint<50, 14>::from_base(b_[0]),
67 Common::FixedPoint<50, 14>::from_base(b_[1]),
68 Common::FixedPoint<50, 14>::from_base(b_[2]),
69 };
70 std::array<Common::FixedPoint<50, 14>, 3> a{
71 Common::FixedPoint<50, 14>::from_base(a_[0]),
72 Common::FixedPoint<50, 14>::from_base(a_[1]),
73 };
74 66
75 for (u32 i = 0; i < sample_count; i++) { 67 for (u32 i = 0; i < sample_count; i++) {
76 s64 in_sample{input[i]}; 68 const s64 in_sample{input[i]};
77 auto sample{in_sample * b[0] + state.s0}; 69 const s64 sample{in_sample * b[0] + state.s0};
78 const auto out_sample{std::clamp(sample.to_long(), min, max)}; 70 const s64 out_sample{std::clamp<s64>((sample + (1 << 13)) >> 14, min, max)};
79 71
80 output[i] = static_cast<s32>(out_sample); 72 output[i] = static_cast<s32>(out_sample);
81 73
82 state.s0 = state.s1 + b[1] * in_sample + a[0] * out_sample; 74 state.s0 = state.s1 + b[1] * in_sample + a[0] * out_sample;
83 state.s1 = 0 + b[2] * in_sample + a[1] * out_sample; 75 state.s1 = b[2] * in_sample + a[1] * out_sample;
84 } 76 }
85} 77}
86 78
diff --git a/src/audio_core/renderer/voice/voice_state.h b/src/audio_core/renderer/voice/voice_state.h
index d5497e2fb..ce947233f 100644
--- a/src/audio_core/renderer/voice/voice_state.h
+++ b/src/audio_core/renderer/voice/voice_state.h
@@ -19,10 +19,10 @@ struct VoiceState {
19 * State of the voice's biquad filter. 19 * State of the voice's biquad filter.
20 */ 20 */
21 struct BiquadFilterState { 21 struct BiquadFilterState {
22 Common::FixedPoint<50, 14> s0; 22 s64 s0;
23 Common::FixedPoint<50, 14> s1; 23 s64 s1;
24 Common::FixedPoint<50, 14> s2; 24 s64 s2;
25 Common::FixedPoint<50, 14> s3; 25 s64 s3;
26 }; 26 };
27 27
28 /** 28 /**
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp
index 32c1b1cb3..9133f5388 100644
--- a/src/audio_core/sink/cubeb_sink.cpp
+++ b/src/audio_core/sink/cubeb_sink.cpp
@@ -302,11 +302,21 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
302 std::vector<std::string> device_list; 302 std::vector<std::string> device_list;
303 cubeb* ctx; 303 cubeb* ctx;
304 304
305#ifdef _WIN32
306 auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
307#endif
308
305 if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) { 309 if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) {
306 LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); 310 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
307 return {}; 311 return {};
308 } 312 }
309 313
314#ifdef _WIN32
315 if (SUCCEEDED(com_init_result)) {
316 CoUninitialize();
317 }
318#endif
319
310 auto type{capture ? CUBEB_DEVICE_TYPE_INPUT : CUBEB_DEVICE_TYPE_OUTPUT}; 320 auto type{capture ? CUBEB_DEVICE_TYPE_INPUT : CUBEB_DEVICE_TYPE_OUTPUT};
311 cubeb_device_collection collection; 321 cubeb_device_collection collection;
312 if (cubeb_enumerate_devices(ctx, type, &collection) != CUBEB_OK) { 322 if (cubeb_enumerate_devices(ctx, type, &collection) != CUBEB_OK) {
@@ -329,12 +339,22 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) {
329u32 GetCubebLatency() { 339u32 GetCubebLatency() {
330 cubeb* ctx; 340 cubeb* ctx;
331 341
342#ifdef _WIN32
343 auto com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
344#endif
345
332 if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { 346 if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) {
333 LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); 347 LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
334 // Return a large latency so we choose SDL instead. 348 // Return a large latency so we choose SDL instead.
335 return 10000u; 349 return 10000u;
336 } 350 }
337 351
352#ifdef _WIN32
353 if (SUCCEEDED(com_init_result)) {
354 CoUninitialize();
355 }
356#endif
357
338 cubeb_stream_params params{}; 358 cubeb_stream_params params{};
339 params.rate = TargetSampleRate; 359 params.rate = TargetSampleRate;
340 params.channels = 2; 360 params.channels = 2;
diff --git a/src/common/input.h b/src/common/input.h
index d61cd7ca8..b5748a6c8 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -130,6 +130,8 @@ struct ButtonStatus {
130 bool inverted{}; 130 bool inverted{};
131 // Press once to activate, press again to release 131 // Press once to activate, press again to release
132 bool toggle{}; 132 bool toggle{};
133 // Spams the button when active
134 bool turbo{};
133 // Internal lock for the toggle status 135 // Internal lock for the toggle status
134 bool locked{}; 136 bool locked{};
135}; 137};
diff --git a/src/common/settings.h b/src/common/settings.h
index 64db66f37..6d27dd5ee 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -488,6 +488,7 @@ struct Values {
488 Setting<bool> enable_raw_input{false, "enable_raw_input"}; 488 Setting<bool> enable_raw_input{false, "enable_raw_input"};
489 Setting<bool> controller_navigation{true, "controller_navigation"}; 489 Setting<bool> controller_navigation{true, "controller_navigation"};
490 Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"}; 490 Setting<bool> enable_joycon_driver{true, "enable_joycon_driver"};
491 Setting<bool> enable_procon_driver{false, "enable_procon_driver"};
491 492
492 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"}; 493 SwitchableSetting<bool> vibration_enabled{true, "vibration_enabled"};
493 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"}; 494 SwitchableSetting<bool> enable_accurate_vibrations{false, "enable_accurate_vibrations"};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5afdeb5ff..8ef1fcaa8 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -182,6 +182,8 @@ add_library(core STATIC
182 hle/kernel/k_auto_object_container.cpp 182 hle/kernel/k_auto_object_container.cpp
183 hle/kernel/k_auto_object_container.h 183 hle/kernel/k_auto_object_container.h
184 hle/kernel/k_affinity_mask.h 184 hle/kernel/k_affinity_mask.h
185 hle/kernel/k_capabilities.cpp
186 hle/kernel/k_capabilities.h
185 hle/kernel/k_class_token.cpp 187 hle/kernel/k_class_token.cpp
186 hle/kernel/k_class_token.h 188 hle/kernel/k_class_token.h
187 hle/kernel/k_client_port.cpp 189 hle/kernel/k_client_port.cpp
@@ -193,6 +195,8 @@ add_library(core STATIC
193 hle/kernel/k_condition_variable.cpp 195 hle/kernel/k_condition_variable.cpp
194 hle/kernel/k_condition_variable.h 196 hle/kernel/k_condition_variable.h
195 hle/kernel/k_debug.h 197 hle/kernel/k_debug.h
198 hle/kernel/k_device_address_space.cpp
199 hle/kernel/k_device_address_space.h
196 hle/kernel/k_dynamic_page_manager.h 200 hle/kernel/k_dynamic_page_manager.h
197 hle/kernel/k_dynamic_resource_manager.h 201 hle/kernel/k_dynamic_resource_manager.h
198 hle/kernel/k_dynamic_slab_heap.h 202 hle/kernel/k_dynamic_slab_heap.h
@@ -294,7 +298,43 @@ add_library(core STATIC
294 hle/kernel/svc.h 298 hle/kernel/svc.h
295 hle/kernel/svc_common.h 299 hle/kernel/svc_common.h
296 hle/kernel/svc_types.h 300 hle/kernel/svc_types.h
297 hle/kernel/svc_wrap.h 301 hle/kernel/svc/svc_activity.cpp
302 hle/kernel/svc/svc_address_arbiter.cpp
303 hle/kernel/svc/svc_address_translation.cpp
304 hle/kernel/svc/svc_cache.cpp
305 hle/kernel/svc/svc_code_memory.cpp
306 hle/kernel/svc/svc_condition_variable.cpp
307 hle/kernel/svc/svc_debug.cpp
308 hle/kernel/svc/svc_debug_string.cpp
309 hle/kernel/svc/svc_device_address_space.cpp
310 hle/kernel/svc/svc_event.cpp
311 hle/kernel/svc/svc_exception.cpp
312 hle/kernel/svc/svc_info.cpp
313 hle/kernel/svc/svc_insecure_memory.cpp
314 hle/kernel/svc/svc_interrupt_event.cpp
315 hle/kernel/svc/svc_io_pool.cpp
316 hle/kernel/svc/svc_ipc.cpp
317 hle/kernel/svc/svc_kernel_debug.cpp
318 hle/kernel/svc/svc_light_ipc.cpp
319 hle/kernel/svc/svc_lock.cpp
320 hle/kernel/svc/svc_memory.cpp
321 hle/kernel/svc/svc_physical_memory.cpp
322 hle/kernel/svc/svc_port.cpp
323 hle/kernel/svc/svc_power_management.cpp
324 hle/kernel/svc/svc_process.cpp
325 hle/kernel/svc/svc_process_memory.cpp
326 hle/kernel/svc/svc_processor.cpp
327 hle/kernel/svc/svc_query_memory.cpp
328 hle/kernel/svc/svc_register.cpp
329 hle/kernel/svc/svc_resource_limit.cpp
330 hle/kernel/svc/svc_secure_monitor_call.cpp
331 hle/kernel/svc/svc_session.cpp
332 hle/kernel/svc/svc_shared_memory.cpp
333 hle/kernel/svc/svc_synchronization.cpp
334 hle/kernel/svc/svc_thread.cpp
335 hle/kernel/svc/svc_thread_profiler.cpp
336 hle/kernel/svc/svc_tick.cpp
337 hle/kernel/svc/svc_transfer_memory.cpp
298 hle/result.h 338 hle/result.h
299 hle/service/acc/acc.cpp 339 hle/service/acc/acc.cpp
300 hle/service/acc/acc.h 340 hle/service/acc/acc.h
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 7d62d030e..c40771c97 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <span> 7#include <span>
8#include <string>
8#include <vector> 9#include <vector>
9 10
10#include <dynarmic/interface/halt_reason.h> 11#include <dynarmic/interface/halt_reason.h>
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h
index 13cbdb734..45567b840 100644
--- a/src/core/hardware_properties.h
+++ b/src/core/hardware_properties.h
@@ -25,6 +25,26 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
26}; 26};
27 27
28static constexpr inline size_t NumVirtualCores = Common::BitSize<u64>();
29
30static constexpr inline u64 VirtualCoreMask = [] {
31 u64 mask = 0;
32 for (size_t i = 0; i < NumVirtualCores; ++i) {
33 mask |= (UINT64_C(1) << i);
34 }
35 return mask;
36}();
37
38static constexpr inline u64 ConvertVirtualCoreMaskToPhysical(u64 v_core_mask) {
39 u64 p_core_mask = 0;
40 while (v_core_mask != 0) {
41 const u64 next = std::countr_zero(v_core_mask);
42 v_core_mask &= ~(static_cast<u64>(1) << next);
43 p_core_mask |= (static_cast<u64>(1) << VirtualToPhysicalCoreMap[next]);
44 }
45 return p_core_mask;
46}
47
28// Cortex-A57 supports 4 memory watchpoints 48// Cortex-A57 supports 4 memory watchpoints
29constexpr u64 NUM_WATCHPOINTS = 4; 49constexpr u64 NUM_WATCHPOINTS = 4;
30 50
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 0e06468da..631aa6ad2 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -12,6 +12,7 @@
12namespace Core::HID { 12namespace Core::HID {
13constexpr s32 HID_JOYSTICK_MAX = 0x7fff; 13constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_TRIGGER_MAX = 0x7fff; 14constexpr s32 HID_TRIGGER_MAX = 0x7fff;
15constexpr u32 TURBO_BUTTON_DELAY = 4;
15// Use a common UUID for TAS and Virtual Gamepad 16// Use a common UUID for TAS and Virtual Gamepad
16constexpr Common::UUID TAS_UUID = 17constexpr Common::UUID TAS_UUID =
17 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}; 18 Common::UUID{{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -447,6 +448,7 @@ void EmulatedController::ReloadInput() {
447 }, 448 },
448 }); 449 });
449 } 450 }
451 turbo_button_state = 0;
450} 452}
451 453
452void EmulatedController::UnloadInput() { 454void EmulatedController::UnloadInput() {
@@ -687,6 +689,7 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
687 } 689 }
688 690
689 current_status.toggle = new_status.toggle; 691 current_status.toggle = new_status.toggle;
692 current_status.turbo = new_status.turbo;
690 current_status.uuid = uuid; 693 current_status.uuid = uuid;
691 694
692 // Update button status with current 695 // Update button status with current
@@ -1548,7 +1551,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
1548 if (is_configuring) { 1551 if (is_configuring) {
1549 return {}; 1552 return {};
1550 } 1553 }
1551 return controller.npad_button_state; 1554 return {controller.npad_button_state.raw & GetTurboButtonMask()};
1552} 1555}
1553 1556
1554DebugPadButton EmulatedController::GetDebugPadButtons() const { 1557DebugPadButton EmulatedController::GetDebugPadButtons() const {
@@ -1656,4 +1659,74 @@ void EmulatedController::DeleteCallback(int key) {
1656 } 1659 }
1657 callback_list.erase(iterator); 1660 callback_list.erase(iterator);
1658} 1661}
1662
1663void EmulatedController::TurboButtonUpdate() {
1664 turbo_button_state = (turbo_button_state + 1) % (TURBO_BUTTON_DELAY * 2);
1665}
1666
1667NpadButton EmulatedController::GetTurboButtonMask() const {
1668 // Apply no mask when disabled
1669 if (turbo_button_state < TURBO_BUTTON_DELAY) {
1670 return {NpadButton::All};
1671 }
1672
1673 NpadButtonState button_mask{};
1674 for (std::size_t index = 0; index < controller.button_values.size(); ++index) {
1675 if (!controller.button_values[index].turbo) {
1676 continue;
1677 }
1678
1679 switch (index) {
1680 case Settings::NativeButton::A:
1681 button_mask.a.Assign(1);
1682 break;
1683 case Settings::NativeButton::B:
1684 button_mask.b.Assign(1);
1685 break;
1686 case Settings::NativeButton::X:
1687 button_mask.x.Assign(1);
1688 break;
1689 case Settings::NativeButton::Y:
1690 button_mask.y.Assign(1);
1691 break;
1692 case Settings::NativeButton::L:
1693 button_mask.l.Assign(1);
1694 break;
1695 case Settings::NativeButton::R:
1696 button_mask.r.Assign(1);
1697 break;
1698 case Settings::NativeButton::ZL:
1699 button_mask.zl.Assign(1);
1700 break;
1701 case Settings::NativeButton::ZR:
1702 button_mask.zr.Assign(1);
1703 break;
1704 case Settings::NativeButton::DLeft:
1705 button_mask.left.Assign(1);
1706 break;
1707 case Settings::NativeButton::DUp:
1708 button_mask.up.Assign(1);
1709 break;
1710 case Settings::NativeButton::DRight:
1711 button_mask.right.Assign(1);
1712 break;
1713 case Settings::NativeButton::DDown:
1714 button_mask.down.Assign(1);
1715 break;
1716 case Settings::NativeButton::SL:
1717 button_mask.left_sl.Assign(1);
1718 button_mask.right_sl.Assign(1);
1719 break;
1720 case Settings::NativeButton::SR:
1721 button_mask.left_sr.Assign(1);
1722 button_mask.right_sr.Assign(1);
1723 break;
1724 default:
1725 break;
1726 }
1727 }
1728
1729 return static_cast<NpadButton>(~button_mask.raw);
1730}
1731
1659} // namespace Core::HID 1732} // namespace Core::HID
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 3ac77b2b5..b02bf35c4 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -411,6 +411,9 @@ public:
411 */ 411 */
412 void DeleteCallback(int key); 412 void DeleteCallback(int key);
413 413
414 /// Swaps the state of the turbo buttons
415 void TurboButtonUpdate();
416
414private: 417private:
415 /// creates input devices from params 418 /// creates input devices from params
416 void LoadDevices(); 419 void LoadDevices();
@@ -511,6 +514,8 @@ private:
511 */ 514 */
512 void TriggerOnChange(ControllerTriggerType type, bool is_service_update); 515 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
513 516
517 NpadButton GetTurboButtonMask() const;
518
514 const NpadIdType npad_id_type; 519 const NpadIdType npad_id_type;
515 NpadStyleIndex npad_type{NpadStyleIndex::None}; 520 NpadStyleIndex npad_type{NpadStyleIndex::None};
516 NpadStyleIndex original_npad_type{NpadStyleIndex::None}; 521 NpadStyleIndex original_npad_type{NpadStyleIndex::None};
@@ -520,6 +525,7 @@ private:
520 bool system_buttons_enabled{true}; 525 bool system_buttons_enabled{true};
521 f32 motion_sensitivity{0.01f}; 526 f32 motion_sensitivity{0.01f};
522 bool force_update_motion{false}; 527 bool force_update_motion{false};
528 u32 turbo_button_state{0};
523 529
524 // Temporary values to avoid doing changes while the controller is in configuring mode 530 // Temporary values to avoid doing changes while the controller is in configuring mode
525 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None}; 531 NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 7b363eb1e..571acf4b2 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/init/init_slab_setup.h" 11#include "core/hle/kernel/init/init_slab_setup.h"
12#include "core/hle/kernel/k_code_memory.h" 12#include "core/hle/kernel/k_code_memory.h"
13#include "core/hle/kernel/k_debug.h" 13#include "core/hle/kernel/k_debug.h"
14#include "core/hle/kernel/k_device_address_space.h"
14#include "core/hle/kernel/k_event.h" 15#include "core/hle/kernel/k_event.h"
15#include "core/hle/kernel/k_event_info.h" 16#include "core/hle/kernel/k_event_info.h"
16#include "core/hle/kernel/k_memory_layout.h" 17#include "core/hle/kernel/k_memory_layout.h"
@@ -43,6 +44,7 @@ namespace Kernel::Init {
43 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \ 44 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
44 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ 45 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
45 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ 46 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
47 HANDLER(KDeviceAddressSpace, (SLAB_COUNT(KDeviceAddressSpace)), ##__VA_ARGS__) \
46 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ 48 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
47 HANDLER(KThreadLocalPage, \ 49 HANDLER(KThreadLocalPage, \
48 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \ 50 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
diff --git a/src/core/hle/kernel/k_capabilities.cpp b/src/core/hle/kernel/k_capabilities.cpp
new file mode 100644
index 000000000..2907cc6e3
--- /dev/null
+++ b/src/core/hle/kernel/k_capabilities.cpp
@@ -0,0 +1,358 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hardware_properties.h"
5#include "core/hle/kernel/k_capabilities.h"
6#include "core/hle/kernel/k_memory_layout.h"
7#include "core/hle/kernel/k_page_table.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/svc_results.h"
10#include "core/hle/kernel/svc_version.h"
11
12namespace Kernel {
13
14Result KCapabilities::InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table) {
15 // We're initializing an initial process.
16 m_svc_access_flags.reset();
17 m_irq_access_flags.reset();
18 m_debug_capabilities = 0;
19 m_handle_table_size = 0;
20 m_intended_kernel_version = 0;
21 m_program_type = 0;
22
23 // Initial processes may run on all cores.
24 constexpr u64 VirtMask = Core::Hardware::VirtualCoreMask;
25 constexpr u64 PhysMask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(VirtMask);
26
27 m_core_mask = VirtMask;
28 m_phys_core_mask = PhysMask;
29
30 // Initial processes may use any user priority they like.
31 m_priority_mask = ~0xFULL;
32
33 // Here, Nintendo sets the kernel version to the current kernel version.
34 // We will follow suit and set the version to the highest supported kernel version.
35 KernelVersion intended_kernel_version{};
36 intended_kernel_version.major_version.Assign(Svc::SupportedKernelMajorVersion);
37 intended_kernel_version.minor_version.Assign(Svc::SupportedKernelMinorVersion);
38 m_intended_kernel_version = intended_kernel_version.raw;
39
40 // Parse the capabilities array.
41 R_RETURN(this->SetCapabilities(kern_caps, page_table));
42}
43
44Result KCapabilities::InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table) {
45 // We're initializing a user process.
46 m_svc_access_flags.reset();
47 m_irq_access_flags.reset();
48 m_debug_capabilities = 0;
49 m_handle_table_size = 0;
50 m_intended_kernel_version = 0;
51 m_program_type = 0;
52
53 // User processes must specify what cores/priorities they can use.
54 m_core_mask = 0;
55 m_priority_mask = 0;
56
57 // Parse the user capabilities array.
58 R_RETURN(this->SetCapabilities(user_caps, page_table));
59}
60
61Result KCapabilities::SetCorePriorityCapability(const u32 cap) {
62 // We can't set core/priority if we've already set them.
63 R_UNLESS(m_core_mask == 0, ResultInvalidArgument);
64 R_UNLESS(m_priority_mask == 0, ResultInvalidArgument);
65
66 // Validate the core/priority.
67 CorePriority pack{cap};
68 const u32 min_core = pack.minimum_core_id;
69 const u32 max_core = pack.maximum_core_id;
70 const u32 max_prio = pack.lowest_thread_priority;
71 const u32 min_prio = pack.highest_thread_priority;
72
73 R_UNLESS(min_core <= max_core, ResultInvalidCombination);
74 R_UNLESS(min_prio <= max_prio, ResultInvalidCombination);
75 R_UNLESS(max_core < Core::Hardware::NumVirtualCores, ResultInvalidCoreId);
76
77 ASSERT(max_prio < Common::BitSize<u64>());
78
79 // Set core mask.
80 for (auto core_id = min_core; core_id <= max_core; core_id++) {
81 m_core_mask |= (1ULL << core_id);
82 }
83 ASSERT((m_core_mask & Core::Hardware::VirtualCoreMask) == m_core_mask);
84
85 // Set physical core mask.
86 m_phys_core_mask = Core::Hardware::ConvertVirtualCoreMaskToPhysical(m_core_mask);
87
88 // Set priority mask.
89 for (auto prio = min_prio; prio <= max_prio; prio++) {
90 m_priority_mask |= (1ULL << prio);
91 }
92
93 // We must have some core/priority we can use.
94 R_UNLESS(m_core_mask != 0, ResultInvalidArgument);
95 R_UNLESS(m_priority_mask != 0, ResultInvalidArgument);
96
97 // Processes must not have access to kernel thread priorities.
98 R_UNLESS((m_priority_mask & 0xF) == 0, ResultInvalidArgument);
99
100 R_SUCCEED();
101}
102
103Result KCapabilities::SetSyscallMaskCapability(const u32 cap, u32& set_svc) {
104 // Validate the index.
105 SyscallMask pack{cap};
106 const u32 mask = pack.mask;
107 const u32 index = pack.index;
108
109 const u32 index_flag = (1U << index);
110 R_UNLESS((set_svc & index_flag) == 0, ResultInvalidCombination);
111 set_svc |= index_flag;
112
113 // Set SVCs.
114 for (size_t i = 0; i < decltype(SyscallMask::mask)::bits; i++) {
115 const u32 svc_id = static_cast<u32>(decltype(SyscallMask::mask)::bits * index + i);
116 if (mask & (1U << i)) {
117 R_UNLESS(this->SetSvcAllowed(svc_id), ResultOutOfRange);
118 }
119 }
120
121 R_SUCCEED();
122}
123
124Result KCapabilities::MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table) {
125 const auto range_pack = MapRange{cap};
126 const auto size_pack = MapRangeSize{size_cap};
127
128 // Get/validate address/size
129 const u64 phys_addr = range_pack.address.Value() * PageSize;
130
131 // Validate reserved bits are unused.
132 R_UNLESS(size_pack.reserved.Value() == 0, ResultOutOfRange);
133
134 const size_t num_pages = size_pack.pages;
135 const size_t size = num_pages * PageSize;
136 R_UNLESS(num_pages != 0, ResultInvalidSize);
137 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
138 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
139
140 // Do the mapping.
141 [[maybe_unused]] const KMemoryPermission perm = range_pack.read_only.Value()
142 ? KMemoryPermission::UserRead
143 : KMemoryPermission::UserReadWrite;
144 if (MapRangeSize{size_cap}.normal) {
145 // R_RETURN(page_table->MapStatic(phys_addr, size, perm));
146 } else {
147 // R_RETURN(page_table->MapIo(phys_addr, size, perm));
148 }
149
150 UNIMPLEMENTED();
151 R_SUCCEED();
152}
153
154Result KCapabilities::MapIoPage_(const u32 cap, KPageTable* page_table) {
155 // Get/validate address/size
156 const u64 phys_addr = MapIoPage{cap}.address.Value() * PageSize;
157 const size_t num_pages = 1;
158 const size_t size = num_pages * PageSize;
159 R_UNLESS(num_pages != 0, ResultInvalidSize);
160 R_UNLESS(phys_addr < phys_addr + size, ResultInvalidAddress);
161 R_UNLESS(((phys_addr + size - 1) & ~PhysicalMapAllowedMask) == 0, ResultInvalidAddress);
162
163 // Do the mapping.
164 // R_RETURN(page_table->MapIo(phys_addr, size, KMemoryPermission_UserReadWrite));
165
166 UNIMPLEMENTED();
167 R_SUCCEED();
168}
169
170template <typename F>
171Result KCapabilities::ProcessMapRegionCapability(const u32 cap, F f) {
172 // Define the allowed memory regions.
173 constexpr std::array<KMemoryRegionType, 4> MemoryRegions{
174 KMemoryRegionType_None,
175 KMemoryRegionType_KernelTraceBuffer,
176 KMemoryRegionType_OnMemoryBootImage,
177 KMemoryRegionType_DTB,
178 };
179
180 // Extract regions/read only.
181 const MapRegion pack{cap};
182 const std::array<RegionType, 3> types{pack.region0, pack.region1, pack.region2};
183 const std::array<u32, 3> ro{pack.read_only0, pack.read_only1, pack.read_only2};
184
185 for (size_t i = 0; i < types.size(); i++) {
186 const auto type = types[i];
187 const auto perm = ro[i] ? KMemoryPermission::UserRead : KMemoryPermission::UserReadWrite;
188 switch (type) {
189 case RegionType::NoMapping:
190 break;
191 case RegionType::KernelTraceBuffer:
192 case RegionType::OnMemoryBootImage:
193 case RegionType::DTB:
194 R_TRY(f(MemoryRegions[static_cast<u32>(type)], perm));
195 break;
196 default:
197 R_THROW(ResultNotFound);
198 }
199 }
200
201 R_SUCCEED();
202}
203
204Result KCapabilities::MapRegion_(const u32 cap, KPageTable* page_table) {
205 // Map each region into the process's page table.
206 return ProcessMapRegionCapability(
207 cap, [](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
208 // R_RETURN(page_table->MapRegion(region_type, perm));
209 UNIMPLEMENTED();
210 R_SUCCEED();
211 });
212}
213
214Result KCapabilities::CheckMapRegion(KernelCore& kernel, const u32 cap) {
215 // Check that each region has a physical backing store.
216 return ProcessMapRegionCapability(
217 cap, [&](KMemoryRegionType region_type, KMemoryPermission perm) -> Result {
218 R_UNLESS(kernel.MemoryLayout().GetPhysicalMemoryRegionTree().FindFirstDerived(
219 region_type) != nullptr,
220 ResultOutOfRange);
221 R_SUCCEED();
222 });
223}
224
225Result KCapabilities::SetInterruptPairCapability(const u32 cap) {
226 // Extract interrupts.
227 const InterruptPair pack{cap};
228 const std::array<u32, 2> ids{pack.interrupt_id0, pack.interrupt_id1};
229
230 for (size_t i = 0; i < ids.size(); i++) {
231 if (ids[i] != PaddingInterruptId) {
232 UNIMPLEMENTED();
233 // R_UNLESS(Kernel::GetInterruptManager().IsInterruptDefined(ids[i]), ResultOutOfRange);
234 // R_UNLESS(this->SetInterruptPermitted(ids[i]), ResultOutOfRange);
235 }
236 }
237
238 R_SUCCEED();
239}
240
241Result KCapabilities::SetProgramTypeCapability(const u32 cap) {
242 // Validate.
243 const ProgramType pack{cap};
244 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
245
246 m_program_type = pack.type;
247 R_SUCCEED();
248}
249
250Result KCapabilities::SetKernelVersionCapability(const u32 cap) {
251 // Ensure we haven't set our version before.
252 R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version == 0, ResultInvalidArgument);
253
254 // Set, ensure that we set a valid version.
255 m_intended_kernel_version = cap;
256 R_UNLESS(KernelVersion{m_intended_kernel_version}.major_version != 0, ResultInvalidArgument);
257
258 R_SUCCEED();
259}
260
261Result KCapabilities::SetHandleTableCapability(const u32 cap) {
262 // Validate.
263 const HandleTable pack{cap};
264 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
265
266 m_handle_table_size = pack.size;
267 R_SUCCEED();
268}
269
270Result KCapabilities::SetDebugFlagsCapability(const u32 cap) {
271 // Validate.
272 const DebugFlags pack{cap};
273 R_UNLESS(pack.reserved == 0, ResultReservedUsed);
274
275 DebugFlags debug_capabilities{m_debug_capabilities};
276 debug_capabilities.allow_debug.Assign(pack.allow_debug);
277 debug_capabilities.force_debug.Assign(pack.force_debug);
278 m_debug_capabilities = debug_capabilities.raw;
279
280 R_SUCCEED();
281}
282
283Result KCapabilities::SetCapability(const u32 cap, u32& set_flags, u32& set_svc,
284 KPageTable* page_table) {
285 // Validate this is a capability we can act on.
286 const auto type = GetCapabilityType(cap);
287 R_UNLESS(type != CapabilityType::Invalid, ResultInvalidArgument);
288
289 // If the type is padding, we have no work to do.
290 R_SUCCEED_IF(type == CapabilityType::Padding);
291
292 // Check that we haven't already processed this capability.
293 const auto flag = GetCapabilityFlag(type);
294 R_UNLESS(((set_flags & InitializeOnceFlags) & flag) == 0, ResultInvalidCombination);
295 set_flags |= flag;
296
297 // Process the capability.
298 switch (type) {
299 case CapabilityType::CorePriority:
300 R_RETURN(this->SetCorePriorityCapability(cap));
301 case CapabilityType::SyscallMask:
302 R_RETURN(this->SetSyscallMaskCapability(cap, set_svc));
303 case CapabilityType::MapIoPage:
304 R_RETURN(this->MapIoPage_(cap, page_table));
305 case CapabilityType::MapRegion:
306 R_RETURN(this->MapRegion_(cap, page_table));
307 case CapabilityType::InterruptPair:
308 R_RETURN(this->SetInterruptPairCapability(cap));
309 case CapabilityType::ProgramType:
310 R_RETURN(this->SetProgramTypeCapability(cap));
311 case CapabilityType::KernelVersion:
312 R_RETURN(this->SetKernelVersionCapability(cap));
313 case CapabilityType::HandleTable:
314 R_RETURN(this->SetHandleTableCapability(cap));
315 case CapabilityType::DebugFlags:
316 R_RETURN(this->SetDebugFlagsCapability(cap));
317 default:
318 R_THROW(ResultInvalidArgument);
319 }
320}
321
322Result KCapabilities::SetCapabilities(std::span<const u32> caps, KPageTable* page_table) {
323 u32 set_flags = 0, set_svc = 0;
324
325 for (size_t i = 0; i < caps.size(); i++) {
326 const u32 cap{caps[i]};
327
328 if (GetCapabilityType(cap) == CapabilityType::MapRange) {
329 // Check that the pair cap exists.
330 R_UNLESS((++i) < caps.size(), ResultInvalidCombination);
331
332 // Check the pair cap is a map range cap.
333 const u32 size_cap{caps[i]};
334 R_UNLESS(GetCapabilityType(size_cap) == CapabilityType::MapRange,
335 ResultInvalidCombination);
336
337 // Map the range.
338 R_TRY(this->MapRange_(cap, size_cap, page_table));
339 } else {
340 R_TRY(this->SetCapability(cap, set_flags, set_svc, page_table));
341 }
342 }
343
344 R_SUCCEED();
345}
346
347Result KCapabilities::CheckCapabilities(KernelCore& kernel, std::span<const u32> caps) {
348 for (auto cap : caps) {
349 // Check the capability refers to a valid region.
350 if (GetCapabilityType(cap) == CapabilityType::MapRegion) {
351 R_TRY(CheckMapRegion(kernel, cap));
352 }
353 }
354
355 R_SUCCEED();
356}
357
358} // namespace Kernel
diff --git a/src/core/hle/kernel/k_capabilities.h b/src/core/hle/kernel/k_capabilities.h
new file mode 100644
index 000000000..cd96f8d23
--- /dev/null
+++ b/src/core/hle/kernel/k_capabilities.h
@@ -0,0 +1,295 @@
1
2// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
3// SPDX-License-Identifier: GPL-2.0-or-later
4
5#pragma once
6
7#include <bitset>
8#include <span>
9
10#include "common/bit_field.h"
11#include "common/common_types.h"
12
13#include "core/hle/kernel/svc_types.h"
14#include "core/hle/result.h"
15
16namespace Kernel {
17
18class KPageTable;
19class KernelCore;
20
21class KCapabilities {
22public:
23 constexpr explicit KCapabilities() = default;
24
25 Result InitializeForKIP(std::span<const u32> kern_caps, KPageTable* page_table);
26 Result InitializeForUser(std::span<const u32> user_caps, KPageTable* page_table);
27
28 static Result CheckCapabilities(KernelCore& kernel, std::span<const u32> user_caps);
29
30 constexpr u64 GetCoreMask() const {
31 return m_core_mask;
32 }
33
34 constexpr u64 GetPhysicalCoreMask() const {
35 return m_phys_core_mask;
36 }
37
38 constexpr u64 GetPriorityMask() const {
39 return m_priority_mask;
40 }
41
42 constexpr s32 GetHandleTableSize() const {
43 return m_handle_table_size;
44 }
45
46 constexpr const Svc::SvcAccessFlagSet& GetSvcPermissions() const {
47 return m_svc_access_flags;
48 }
49
50 constexpr bool IsPermittedSvc(u32 id) const {
51 return (id < m_svc_access_flags.size()) && m_svc_access_flags[id];
52 }
53
54 constexpr bool IsPermittedInterrupt(u32 id) const {
55 return (id < m_irq_access_flags.size()) && m_irq_access_flags[id];
56 }
57
58 constexpr bool IsPermittedDebug() const {
59 return DebugFlags{m_debug_capabilities}.allow_debug.Value() != 0;
60 }
61
62 constexpr bool CanForceDebug() const {
63 return DebugFlags{m_debug_capabilities}.force_debug.Value() != 0;
64 }
65
66 constexpr u32 GetIntendedKernelMajorVersion() const {
67 return KernelVersion{m_intended_kernel_version}.major_version;
68 }
69
70 constexpr u32 GetIntendedKernelMinorVersion() const {
71 return KernelVersion{m_intended_kernel_version}.minor_version;
72 }
73
74private:
75 static constexpr size_t InterruptIdCount = 0x400;
76 using InterruptFlagSet = std::bitset<InterruptIdCount>;
77
78 enum class CapabilityType : u32 {
79 CorePriority = (1U << 3) - 1,
80 SyscallMask = (1U << 4) - 1,
81 MapRange = (1U << 6) - 1,
82 MapIoPage = (1U << 7) - 1,
83 MapRegion = (1U << 10) - 1,
84 InterruptPair = (1U << 11) - 1,
85 ProgramType = (1U << 13) - 1,
86 KernelVersion = (1U << 14) - 1,
87 HandleTable = (1U << 15) - 1,
88 DebugFlags = (1U << 16) - 1,
89
90 Invalid = 0U,
91 Padding = ~0U,
92 };
93
94 using RawCapabilityValue = u32;
95
96 static constexpr CapabilityType GetCapabilityType(const RawCapabilityValue value) {
97 return static_cast<CapabilityType>((~value & (value + 1)) - 1);
98 }
99
100 static constexpr u32 GetCapabilityFlag(CapabilityType type) {
101 return static_cast<u32>(type) + 1;
102 }
103
104 template <CapabilityType Type>
105 static constexpr inline u32 CapabilityFlag = static_cast<u32>(Type) + 1;
106
107 template <CapabilityType Type>
108 static constexpr inline u32 CapabilityId = std::countr_zero(CapabilityFlag<Type>);
109
110 union CorePriority {
111 static_assert(CapabilityId<CapabilityType::CorePriority> + 1 == 4);
112
113 RawCapabilityValue raw;
114 BitField<0, 4, CapabilityType> id;
115 BitField<4, 6, u32> lowest_thread_priority;
116 BitField<10, 6, u32> highest_thread_priority;
117 BitField<16, 8, u32> minimum_core_id;
118 BitField<24, 8, u32> maximum_core_id;
119 };
120
121 union SyscallMask {
122 static_assert(CapabilityId<CapabilityType::SyscallMask> + 1 == 5);
123
124 RawCapabilityValue raw;
125 BitField<0, 5, CapabilityType> id;
126 BitField<5, 24, u32> mask;
127 BitField<29, 3, u32> index;
128 };
129
130 // #undef MESOSPHERE_ENABLE_LARGE_PHYSICAL_ADDRESS_CAPABILITIES
131 static constexpr u64 PhysicalMapAllowedMask = (1ULL << 36) - 1;
132
133 union MapRange {
134 static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
135
136 RawCapabilityValue raw;
137 BitField<0, 7, CapabilityType> id;
138 BitField<7, 24, u32> address;
139 BitField<31, 1, u32> read_only;
140 };
141
142 union MapRangeSize {
143 static_assert(CapabilityId<CapabilityType::MapRange> + 1 == 7);
144
145 RawCapabilityValue raw;
146 BitField<0, 7, CapabilityType> id;
147 BitField<7, 20, u32> pages;
148 BitField<27, 4, u32> reserved;
149 BitField<31, 1, u32> normal;
150 };
151
152 union MapIoPage {
153 static_assert(CapabilityId<CapabilityType::MapIoPage> + 1 == 8);
154
155 RawCapabilityValue raw;
156 BitField<0, 8, CapabilityType> id;
157 BitField<8, 24, u32> address;
158 };
159
160 enum class RegionType : u32 {
161 NoMapping = 0,
162 KernelTraceBuffer = 1,
163 OnMemoryBootImage = 2,
164 DTB = 3,
165 };
166
167 union MapRegion {
168 static_assert(CapabilityId<CapabilityType::MapRegion> + 1 == 11);
169
170 RawCapabilityValue raw;
171 BitField<0, 11, CapabilityType> id;
172 BitField<11, 6, RegionType> region0;
173 BitField<17, 1, u32> read_only0;
174 BitField<18, 6, RegionType> region1;
175 BitField<24, 1, u32> read_only1;
176 BitField<25, 6, RegionType> region2;
177 BitField<31, 1, u32> read_only2;
178 };
179
180 union InterruptPair {
181 static_assert(CapabilityId<CapabilityType::InterruptPair> + 1 == 12);
182
183 RawCapabilityValue raw;
184 BitField<0, 12, CapabilityType> id;
185 BitField<12, 10, u32> interrupt_id0;
186 BitField<22, 10, u32> interrupt_id1;
187 };
188
189 union ProgramType {
190 static_assert(CapabilityId<CapabilityType::ProgramType> + 1 == 14);
191
192 RawCapabilityValue raw;
193 BitField<0, 14, CapabilityType> id;
194 BitField<14, 3, u32> type;
195 BitField<17, 15, u32> reserved;
196 };
197
198 union KernelVersion {
199 static_assert(CapabilityId<CapabilityType::KernelVersion> + 1 == 15);
200
201 RawCapabilityValue raw;
202 BitField<0, 15, CapabilityType> id;
203 BitField<15, 4, u32> major_version;
204 BitField<19, 13, u32> minor_version;
205 };
206
207 union HandleTable {
208 static_assert(CapabilityId<CapabilityType::HandleTable> + 1 == 16);
209
210 RawCapabilityValue raw;
211 BitField<0, 16, CapabilityType> id;
212 BitField<16, 10, u32> size;
213 BitField<26, 6, u32> reserved;
214 };
215
216 union DebugFlags {
217 static_assert(CapabilityId<CapabilityType::DebugFlags> + 1 == 17);
218
219 RawCapabilityValue raw;
220 BitField<0, 17, CapabilityType> id;
221 BitField<17, 1, u32> allow_debug;
222 BitField<18, 1, u32> force_debug;
223 BitField<19, 13, u32> reserved;
224 };
225
226 static_assert(sizeof(CorePriority) == 4);
227 static_assert(sizeof(SyscallMask) == 4);
228 static_assert(sizeof(MapRange) == 4);
229 static_assert(sizeof(MapRangeSize) == 4);
230 static_assert(sizeof(MapIoPage) == 4);
231 static_assert(sizeof(MapRegion) == 4);
232 static_assert(sizeof(InterruptPair) == 4);
233 static_assert(sizeof(ProgramType) == 4);
234 static_assert(sizeof(KernelVersion) == 4);
235 static_assert(sizeof(HandleTable) == 4);
236 static_assert(sizeof(DebugFlags) == 4);
237
238 static constexpr u32 InitializeOnceFlags =
239 CapabilityFlag<CapabilityType::CorePriority> | CapabilityFlag<CapabilityType::ProgramType> |
240 CapabilityFlag<CapabilityType::KernelVersion> |
241 CapabilityFlag<CapabilityType::HandleTable> | CapabilityFlag<CapabilityType::DebugFlags>;
242
243 static const u32 PaddingInterruptId = 0x3FF;
244 static_assert(PaddingInterruptId < InterruptIdCount);
245
246private:
247 constexpr bool SetSvcAllowed(u32 id) {
248 if (id < m_svc_access_flags.size()) [[likely]] {
249 m_svc_access_flags[id] = true;
250 return true;
251 } else {
252 return false;
253 }
254 }
255
256 constexpr bool SetInterruptPermitted(u32 id) {
257 if (id < m_irq_access_flags.size()) [[likely]] {
258 m_irq_access_flags[id] = true;
259 return true;
260 } else {
261 return false;
262 }
263 }
264
265 Result SetCorePriorityCapability(const u32 cap);
266 Result SetSyscallMaskCapability(const u32 cap, u32& set_svc);
267 Result MapRange_(const u32 cap, const u32 size_cap, KPageTable* page_table);
268 Result MapIoPage_(const u32 cap, KPageTable* page_table);
269 Result MapRegion_(const u32 cap, KPageTable* page_table);
270 Result SetInterruptPairCapability(const u32 cap);
271 Result SetProgramTypeCapability(const u32 cap);
272 Result SetKernelVersionCapability(const u32 cap);
273 Result SetHandleTableCapability(const u32 cap);
274 Result SetDebugFlagsCapability(const u32 cap);
275
276 template <typename F>
277 static Result ProcessMapRegionCapability(const u32 cap, F f);
278 static Result CheckMapRegion(KernelCore& kernel, const u32 cap);
279
280 Result SetCapability(const u32 cap, u32& set_flags, u32& set_svc, KPageTable* page_table);
281 Result SetCapabilities(std::span<const u32> caps, KPageTable* page_table);
282
283private:
284 Svc::SvcAccessFlagSet m_svc_access_flags{};
285 InterruptFlagSet m_irq_access_flags{};
286 u64 m_core_mask{};
287 u64 m_phys_core_mask{};
288 u64 m_priority_mask{};
289 u32 m_debug_capabilities{};
290 s32 m_handle_table_size{};
291 u32 m_intended_kernel_version{};
292 u32 m_program_type{};
293};
294
295} // namespace Kernel
diff --git a/src/core/hle/kernel/k_device_address_space.cpp b/src/core/hle/kernel/k_device_address_space.cpp
new file mode 100644
index 000000000..27659ea3b
--- /dev/null
+++ b/src/core/hle/kernel/k_device_address_space.cpp
@@ -0,0 +1,150 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/assert.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_device_address_space.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel {
11
12KDeviceAddressSpace::KDeviceAddressSpace(KernelCore& kernel_)
13 : KAutoObjectWithSlabHeapAndContainer(kernel_), m_lock(kernel_), m_is_initialized(false) {}
14KDeviceAddressSpace::~KDeviceAddressSpace() = default;
15
16void KDeviceAddressSpace::Initialize() {
17 // This just forwards to the device page table manager.
18 // KDevicePageTable::Initialize();
19}
20
21// Member functions.
22Result KDeviceAddressSpace::Initialize(u64 address, u64 size) {
23 // Initialize the device page table.
24 // R_TRY(m_table.Initialize(address, size));
25
26 // Set member variables.
27 m_space_address = address;
28 m_space_size = size;
29 m_is_initialized = true;
30
31 R_SUCCEED();
32}
33
34void KDeviceAddressSpace::Finalize() {
35 // Finalize the table.
36 // m_table.Finalize();
37}
38
39Result KDeviceAddressSpace::Attach(Svc::DeviceName device_name) {
40 // Lock the address space.
41 KScopedLightLock lk(m_lock);
42
43 // Attach.
44 // R_RETURN(m_table.Attach(device_name, m_space_address, m_space_size));
45 R_SUCCEED();
46}
47
48Result KDeviceAddressSpace::Detach(Svc::DeviceName device_name) {
49 // Lock the address space.
50 KScopedLightLock lk(m_lock);
51
52 // Detach.
53 // R_RETURN(m_table.Detach(device_name));
54 R_SUCCEED();
55}
56
57Result KDeviceAddressSpace::Map(KPageTable* page_table, VAddr process_address, size_t size,
58 u64 device_address, u32 option, bool is_aligned) {
59 // Check that the address falls within the space.
60 R_UNLESS((m_space_address <= device_address &&
61 device_address + size - 1 <= m_space_address + m_space_size - 1),
62 ResultInvalidCurrentMemory);
63
64 // Decode the option.
65 const Svc::MapDeviceAddressSpaceOption option_pack{option};
66 const auto device_perm = option_pack.permission.Value();
67 const auto flags = option_pack.flags.Value();
68 const auto reserved = option_pack.reserved.Value();
69
70 // Validate the option.
71 // TODO: It is likely that this check for flags == none is only on NX board.
72 R_UNLESS(flags == Svc::MapDeviceAddressSpaceFlag::None, ResultInvalidEnumValue);
73 R_UNLESS(reserved == 0, ResultInvalidEnumValue);
74
75 // Lock the address space.
76 KScopedLightLock lk(m_lock);
77
78 // Lock the page table to prevent concurrent device mapping operations.
79 // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
80
81 // Lock the pages.
82 bool is_io{};
83 R_TRY(page_table->LockForMapDeviceAddressSpace(std::addressof(is_io), process_address, size,
84 ConvertToKMemoryPermission(device_perm),
85 is_aligned, true));
86
87 // Ensure that if we fail, we don't keep unmapped pages locked.
88 ON_RESULT_FAILURE {
89 ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
90 };
91
92 // Check that the io status is allowable.
93 if (is_io) {
94 R_UNLESS(static_cast<u32>(flags & Svc::MapDeviceAddressSpaceFlag::NotIoRegister) == 0,
95 ResultInvalidCombination);
96 }
97
98 // Map the pages.
99 {
100 // Perform the mapping.
101 // R_TRY(m_table.Map(page_table, process_address, size, device_address, device_perm,
102 // is_aligned, is_io));
103
104 // Ensure that we unmap the pages if we fail to update the protections.
105 // NOTE: Nintendo does not check the result of this unmap call.
106 // ON_RESULT_FAILURE { m_table.Unmap(device_address, size); };
107
108 // Update the protections in accordance with how much we mapped.
109 // R_TRY(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size));
110 }
111
112 // We succeeded.
113 R_SUCCEED();
114}
115
116Result KDeviceAddressSpace::Unmap(KPageTable* page_table, VAddr process_address, size_t size,
117 u64 device_address) {
118 // Check that the address falls within the space.
119 R_UNLESS((m_space_address <= device_address &&
120 device_address + size - 1 <= m_space_address + m_space_size - 1),
121 ResultInvalidCurrentMemory);
122
123 // Lock the address space.
124 KScopedLightLock lk(m_lock);
125
126 // Lock the page table to prevent concurrent device mapping operations.
127 // KScopedLightLock pt_lk = page_table->AcquireDeviceMapLock();
128
129 // Lock the pages.
130 R_TRY(page_table->LockForUnmapDeviceAddressSpace(process_address, size, true));
131
132 // Unmap the pages.
133 {
134 // If we fail to unmap, we want to do a partial unlock.
135 // ON_RESULT_FAILURE {
136 // ASSERT(page_table->UnlockForDeviceAddressSpacePartialMap(process_address, size) ==
137 // ResultSuccess);
138 // };
139
140 // Perform the unmap.
141 // R_TRY(m_table.Unmap(page_table, process_address, size, device_address));
142 }
143
144 // Unlock the pages.
145 ASSERT(page_table->UnlockForDeviceAddressSpace(process_address, size) == ResultSuccess);
146
147 R_SUCCEED();
148}
149
150} // namespace Kernel
diff --git a/src/core/hle/kernel/k_device_address_space.h b/src/core/hle/kernel/k_device_address_space.h
new file mode 100644
index 000000000..4709df995
--- /dev/null
+++ b/src/core/hle/kernel/k_device_address_space.h
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <string>
7
8#include "common/common_types.h"
9#include "core/hle/kernel/k_page_table.h"
10#include "core/hle/kernel/slab_helpers.h"
11#include "core/hle/result.h"
12
13namespace Kernel {
14
15class KDeviceAddressSpace final
16 : public KAutoObjectWithSlabHeapAndContainer<KDeviceAddressSpace, KAutoObjectWithList> {
17 KERNEL_AUTOOBJECT_TRAITS(KDeviceAddressSpace, KAutoObject);
18
19public:
20 explicit KDeviceAddressSpace(KernelCore& kernel);
21 ~KDeviceAddressSpace();
22
23 Result Initialize(u64 address, u64 size);
24 void Finalize();
25
26 bool IsInitialized() const {
27 return m_is_initialized;
28 }
29 static void PostDestroy(uintptr_t arg) {}
30
31 Result Attach(Svc::DeviceName device_name);
32 Result Detach(Svc::DeviceName device_name);
33
34 Result MapByForce(KPageTable* page_table, VAddr process_address, size_t size,
35 u64 device_address, u32 option) {
36 R_RETURN(this->Map(page_table, process_address, size, device_address, option, false));
37 }
38
39 Result MapAligned(KPageTable* page_table, VAddr process_address, size_t size,
40 u64 device_address, u32 option) {
41 R_RETURN(this->Map(page_table, process_address, size, device_address, option, true));
42 }
43
44 Result Unmap(KPageTable* page_table, VAddr process_address, size_t size, u64 device_address);
45
46 static void Initialize();
47
48private:
49 Result Map(KPageTable* page_table, VAddr process_address, size_t size, u64 device_address,
50 u32 option, bool is_aligned);
51
52private:
53 KLightLock m_lock;
54 // KDevicePageTable m_table;
55 u64 m_space_address{};
56 u64 m_space_size{};
57 bool m_is_initialized{};
58};
59
60} // namespace Kernel
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index e201bb0cd..0e4283a0c 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -370,7 +370,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
370 // Initialize proces address space 370 // Initialize proces address space
371 if (const Result result{page_table.InitializeForProcess( 371 if (const Result result{page_table.InitializeForProcess(
372 metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application, 372 metadata.GetAddressSpaceType(), false, false, false, KMemoryManager::Pool::Application,
373 0x8000000, code_size, &kernel.GetSystemSystemResource(), resource_limit)}; 373 0x8000000, code_size, &kernel.GetAppSystemResource(), resource_limit)};
374 result.IsError()) { 374 result.IsError()) {
375 R_RETURN(result); 375 R_RETURN(result);
376 } 376 }
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index d9eafe261..5b72eaaa1 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1146,6 +1146,14 @@ const KMemoryManager& KernelCore::MemoryManager() const {
1146 return *impl->memory_manager; 1146 return *impl->memory_manager;
1147} 1147}
1148 1148
1149KSystemResource& KernelCore::GetAppSystemResource() {
1150 return *impl->app_system_resource;
1151}
1152
1153const KSystemResource& KernelCore::GetAppSystemResource() const {
1154 return *impl->app_system_resource;
1155}
1156
1149KSystemResource& KernelCore::GetSystemSystemResource() { 1157KSystemResource& KernelCore::GetSystemSystemResource() {
1150 return *impl->sys_system_resource; 1158 return *impl->sys_system_resource;
1151} 1159}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 8d22f8d2c..af0ae0e98 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -35,6 +35,7 @@ class GlobalSchedulerContext;
35class KAutoObjectWithListContainer; 35class KAutoObjectWithListContainer;
36class KClientSession; 36class KClientSession;
37class KDebug; 37class KDebug;
38class KDeviceAddressSpace;
38class KDynamicPageManager; 39class KDynamicPageManager;
39class KEvent; 40class KEvent;
40class KEventInfo; 41class KEventInfo;
@@ -245,6 +246,12 @@ public:
245 /// Gets the virtual memory manager for the kernel. 246 /// Gets the virtual memory manager for the kernel.
246 const KMemoryManager& MemoryManager() const; 247 const KMemoryManager& MemoryManager() const;
247 248
249 /// Gets the application resource manager.
250 KSystemResource& GetAppSystemResource();
251
252 /// Gets the application resource manager.
253 const KSystemResource& GetAppSystemResource() const;
254
248 /// Gets the system resource manager. 255 /// Gets the system resource manager.
249 KSystemResource& GetSystemSystemResource(); 256 KSystemResource& GetSystemSystemResource();
250 257
@@ -359,6 +366,8 @@ public:
359 return slab_heap_container->transfer_memory; 366 return slab_heap_container->transfer_memory;
360 } else if constexpr (std::is_same_v<T, KCodeMemory>) { 367 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
361 return slab_heap_container->code_memory; 368 return slab_heap_container->code_memory;
369 } else if constexpr (std::is_same_v<T, KDeviceAddressSpace>) {
370 return slab_heap_container->device_address_space;
362 } else if constexpr (std::is_same_v<T, KPageBuffer>) { 371 } else if constexpr (std::is_same_v<T, KPageBuffer>) {
363 return slab_heap_container->page_buffer; 372 return slab_heap_container->page_buffer;
364 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) { 373 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
@@ -431,6 +440,7 @@ private:
431 KSlabHeap<KThread> thread; 440 KSlabHeap<KThread> thread;
432 KSlabHeap<KTransferMemory> transfer_memory; 441 KSlabHeap<KTransferMemory> transfer_memory;
433 KSlabHeap<KCodeMemory> code_memory; 442 KSlabHeap<KCodeMemory> code_memory;
443 KSlabHeap<KDeviceAddressSpace> device_address_space;
434 KSlabHeap<KPageBuffer> page_buffer; 444 KSlabHeap<KPageBuffer> page_buffer;
435 KSlabHeap<KThreadLocalPage> thread_local_page; 445 KSlabHeap<KThreadLocalPage> thread_local_page;
436 KSlabHeap<KSessionRequest> session_request; 446 KSlabHeap<KSessionRequest> session_request;
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index fb2ba4c6b..fb8e7933e 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -3,6 +3,7 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <condition_variable>
6#include <cstddef> 7#include <cstddef>
7#include <memory> 8#include <memory>
8#include <mutex> 9#include <mutex>
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 67fa5d71c..4ef57356e 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1,3129 +1,4435 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4// This file is automatically generated using svc_generator.py.
5#include <cinttypes> 5
6#include <iterator> 6#include <type_traits>
7#include <mutex> 7
8#include <vector> 8#include "core/arm/arm_interface.h"
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_funcs.h"
13#include "common/fiber.h"
14#include "common/logging/log.h"
15#include "common/scope_exit.h"
16#include "core/core.h" 9#include "core/core.h"
17#include "core/core_timing.h"
18#include "core/debugger/debugger.h"
19#include "core/hle/kernel/k_client_port.h"
20#include "core/hle/kernel/k_client_session.h"
21#include "core/hle/kernel/k_code_memory.h"
22#include "core/hle/kernel/k_event.h"
23#include "core/hle/kernel/k_handle_table.h"
24#include "core/hle/kernel/k_memory_block.h"
25#include "core/hle/kernel/k_memory_layout.h"
26#include "core/hle/kernel/k_page_table.h"
27#include "core/hle/kernel/k_port.h"
28#include "core/hle/kernel/k_process.h" 10#include "core/hle/kernel/k_process.h"
29#include "core/hle/kernel/k_readable_event.h"
30#include "core/hle/kernel/k_resource_limit.h"
31#include "core/hle/kernel/k_scheduler.h"
32#include "core/hle/kernel/k_scoped_resource_reservation.h"
33#include "core/hle/kernel/k_session.h"
34#include "core/hle/kernel/k_shared_memory.h"
35#include "core/hle/kernel/k_synchronization_object.h"
36#include "core/hle/kernel/k_thread.h"
37#include "core/hle/kernel/k_thread_queue.h"
38#include "core/hle/kernel/k_transfer_memory.h"
39#include "core/hle/kernel/kernel.h"
40#include "core/hle/kernel/physical_core.h"
41#include "core/hle/kernel/svc.h" 11#include "core/hle/kernel/svc.h"
42#include "core/hle/kernel/svc_results.h"
43#include "core/hle/kernel/svc_types.h"
44#include "core/hle/kernel/svc_wrap.h"
45#include "core/hle/result.h"
46#include "core/memory.h"
47#include "core/reporter.h"
48 12
49namespace Kernel::Svc { 13namespace Kernel::Svc {
50namespace {
51
52// Checks if address + size is greater than the given address
53// This can return false if the size causes an overflow of a 64-bit type
54// or if the given size is zero.
55constexpr bool IsValidAddressRange(VAddr address, u64 size) {
56 return address + size > address;
57}
58
59// Helper function that performs the common sanity checks for svcMapMemory
60// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
61// in the same order.
62Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
63 u64 size) {
64 if (!Common::Is4KBAligned(dst_addr)) {
65 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
66 return ResultInvalidAddress;
67 }
68 14
69 if (!Common::Is4KBAligned(src_addr)) { 15static uint32_t GetReg32(Core::System& system, int n) {
70 LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr); 16 return static_cast<uint32_t>(system.CurrentArmInterface().GetReg(n));
71 return ResultInvalidSize; 17}
72 }
73 18
74 if (size == 0) { 19static void SetReg32(Core::System& system, int n, uint32_t result) {
75 LOG_ERROR(Kernel_SVC, "Size is 0"); 20 system.CurrentArmInterface().SetReg(n, static_cast<uint64_t>(result));
76 return ResultInvalidSize; 21}
77 }
78 22
79 if (!Common::Is4KBAligned(size)) { 23static uint64_t GetReg64(Core::System& system, int n) {
80 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size); 24 return system.CurrentArmInterface().GetReg(n);
81 return ResultInvalidSize; 25}
82 }
83 26
84 if (!IsValidAddressRange(dst_addr, size)) { 27static void SetReg64(Core::System& system, int n, uint64_t result) {
85 LOG_ERROR(Kernel_SVC, 28 system.CurrentArmInterface().SetReg(n, result);
86 "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}", 29}
87 dst_addr, size);
88 return ResultInvalidCurrentMemory;
89 }
90 30
91 if (!IsValidAddressRange(src_addr, size)) { 31// Like bit_cast, but handles the case when the source and dest
92 LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}", 32// are differently-sized.
93 src_addr, size); 33template <typename To, typename From>
94 return ResultInvalidCurrentMemory; 34 requires(std::is_trivial_v<To> && std::is_trivially_copyable_v<From>)
95 } 35static To Convert(const From& from) {
36 To to{};
96 37
97 if (!manager.IsInsideAddressSpace(src_addr, size)) { 38 if constexpr (sizeof(To) >= sizeof(From)) {
98 LOG_ERROR(Kernel_SVC, 39 std::memcpy(&to, &from, sizeof(From));
99 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", 40 } else {
100 src_addr, size); 41 std::memcpy(&to, &from, sizeof(To));
101 return ResultInvalidCurrentMemory;
102 } 42 }
103 43
104 if (manager.IsOutsideStackRegion(dst_addr, size)) { 44 return to;
105 LOG_ERROR(Kernel_SVC, 45}
106 "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
107 dst_addr, size);
108 return ResultInvalidMemoryRegion;
109 }
110 46
111 if (manager.IsInsideHeapRegion(dst_addr, size)) { 47// clang-format off
112 LOG_ERROR(Kernel_SVC, 48static_assert(sizeof(ArbitrationType) == 4);
113 "Destination does not fit within the heap region, addr=0x{:016X}, " 49static_assert(sizeof(BreakReason) == 4);
114 "size=0x{:016X}", 50static_assert(sizeof(CodeMemoryOperation) == 4);
115 dst_addr, size); 51static_assert(sizeof(DebugThreadParam) == 4);
116 return ResultInvalidMemoryRegion; 52static_assert(sizeof(DeviceName) == 4);
117 } 53static_assert(sizeof(HardwareBreakPointRegisterName) == 4);
54static_assert(sizeof(Handle) == 4);
55static_assert(sizeof(InfoType) == 4);
56static_assert(sizeof(InterruptType) == 4);
57static_assert(sizeof(IoPoolType) == 4);
58static_assert(sizeof(KernelDebugType) == 4);
59static_assert(sizeof(KernelTraceState) == 4);
60static_assert(sizeof(LimitableResource) == 4);
61static_assert(sizeof(MemoryMapping) == 4);
62static_assert(sizeof(MemoryPermission) == 4);
63static_assert(sizeof(PageInfo) == 4);
64static_assert(sizeof(ProcessActivity) == 4);
65static_assert(sizeof(ProcessInfoType) == 4);
66static_assert(sizeof(Result) == 4);
67static_assert(sizeof(SignalType) == 4);
68static_assert(sizeof(SystemInfoType) == 4);
69static_assert(sizeof(ThreadActivity) == 4);
70static_assert(sizeof(ilp32::LastThreadContext) == 16);
71static_assert(sizeof(ilp32::PhysicalMemoryInfo) == 16);
72static_assert(sizeof(ilp32::SecureMonitorArguments) == 32);
73static_assert(sizeof(lp64::LastThreadContext) == 32);
74static_assert(sizeof(lp64::PhysicalMemoryInfo) == 24);
75static_assert(sizeof(lp64::SecureMonitorArguments) == 64);
76static_assert(sizeof(bool) == 1);
77static_assert(sizeof(int32_t) == 4);
78static_assert(sizeof(int64_t) == 8);
79static_assert(sizeof(uint32_t) == 4);
80static_assert(sizeof(uint64_t) == 8);
118 81
119 if (manager.IsInsideAliasRegion(dst_addr, size)) { 82static void SvcWrap_SetHeapSize64From32(Core::System& system) {
120 LOG_ERROR(Kernel_SVC, 83 Result ret{};
121 "Destination does not fit within the map region, addr=0x{:016X}, " 84
122 "size=0x{:016X}", 85 uintptr_t out_address{};
123 dst_addr, size); 86 uint32_t size{};
124 return ResultInvalidMemoryRegion; 87
125 } 88 size = Convert<uint32_t>(GetReg32(system, 1));
89
90 ret = SetHeapSize64From32(system, &out_address, size);
126 91
127 return ResultSuccess; 92 SetReg32(system, 0, Convert<uint32_t>(ret));
93 SetReg32(system, 1, Convert<uint32_t>(out_address));
128} 94}
129 95
130enum class ResourceLimitValueType { 96static void SvcWrap_SetMemoryPermission64From32(Core::System& system) {
131 CurrentValue, 97 Result ret{};
132 LimitValue,
133 PeakValue,
134};
135 98
136} // Anonymous namespace 99 uint32_t address{};
100 uint32_t size{};
101 MemoryPermission perm{};
102
103 address = Convert<uint32_t>(GetReg32(system, 0));
104 size = Convert<uint32_t>(GetReg32(system, 1));
105 perm = Convert<MemoryPermission>(GetReg32(system, 2));
106
107 ret = SetMemoryPermission64From32(system, address, size, perm);
108
109 SetReg32(system, 0, Convert<uint32_t>(ret));
110}
137 111
138/// Set the process heap to a given Size. It can both extend and shrink the heap. 112static void SvcWrap_SetMemoryAttribute64From32(Core::System& system) {
139static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) { 113 Result ret{};
140 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
141 114
142 // Validate size. 115 uint32_t address{};
143 R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize); 116 uint32_t size{};
144 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); 117 uint32_t mask{};
118 uint32_t attr{};
145 119
146 // Set the heap size. 120 address = Convert<uint32_t>(GetReg32(system, 0));
147 R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size)); 121 size = Convert<uint32_t>(GetReg32(system, 1));
122 mask = Convert<uint32_t>(GetReg32(system, 2));
123 attr = Convert<uint32_t>(GetReg32(system, 3));
148 124
149 return ResultSuccess; 125 ret = SetMemoryAttribute64From32(system, address, size, mask, attr);
126
127 SetReg32(system, 0, Convert<uint32_t>(ret));
150} 128}
151 129
152static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) { 130static void SvcWrap_MapMemory64From32(Core::System& system) {
153 VAddr temp_heap_addr{}; 131 Result ret{};
154 const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)}; 132
155 *heap_addr = static_cast<u32>(temp_heap_addr); 133 uint32_t dst_address{};
156 return result; 134 uint32_t src_address{};
135 uint32_t size{};
136
137 dst_address = Convert<uint32_t>(GetReg32(system, 0));
138 src_address = Convert<uint32_t>(GetReg32(system, 1));
139 size = Convert<uint32_t>(GetReg32(system, 2));
140
141 ret = MapMemory64From32(system, dst_address, src_address, size);
142
143 SetReg32(system, 0, Convert<uint32_t>(ret));
157} 144}
158 145
159constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) { 146static void SvcWrap_UnmapMemory64From32(Core::System& system) {
160 switch (perm) { 147 Result ret{};
161 case MemoryPermission::None: 148
162 case MemoryPermission::Read: 149 uint32_t dst_address{};
163 case MemoryPermission::ReadWrite: 150 uint32_t src_address{};
164 return true; 151 uint32_t size{};
165 default: 152
166 return false; 153 dst_address = Convert<uint32_t>(GetReg32(system, 0));
167 } 154 src_address = Convert<uint32_t>(GetReg32(system, 1));
155 size = Convert<uint32_t>(GetReg32(system, 2));
156
157 ret = UnmapMemory64From32(system, dst_address, src_address, size);
158
159 SetReg32(system, 0, Convert<uint32_t>(ret));
168} 160}
169 161
170static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, 162static void SvcWrap_QueryMemory64From32(Core::System& system) {
171 MemoryPermission perm) { 163 Result ret{};
172 LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
173 perm);
174 164
175 // Validate address / size. 165 PageInfo out_page_info{};
176 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); 166 uint32_t out_memory_info{};
177 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); 167 uint32_t address{};
178 R_UNLESS(size > 0, ResultInvalidSize);
179 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
180 168
181 // Validate the permission. 169 out_memory_info = Convert<uint32_t>(GetReg32(system, 0));
182 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission); 170 address = Convert<uint32_t>(GetReg32(system, 2));
183 171
184 // Validate that the region is in range for the current process. 172 ret = QueryMemory64From32(system, out_memory_info, &out_page_info, address);
185 auto& page_table = system.Kernel().CurrentProcess()->PageTable();
186 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
187 173
188 // Set the memory attribute. 174 SetReg32(system, 0, Convert<uint32_t>(ret));
189 return page_table.SetMemoryPermission(address, size, perm); 175 SetReg32(system, 1, Convert<uint32_t>(out_page_info));
190} 176}
191 177
192static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, 178static void SvcWrap_ExitProcess64From32(Core::System& system) {
193 u32 attr) { 179 ExitProcess64From32(system);
194 LOG_DEBUG(Kernel_SVC, 180}
195 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address, 181
196 size, mask, attr); 182static void SvcWrap_CreateThread64From32(Core::System& system) {
183 Result ret{};
197 184
198 // Validate address / size. 185 Handle out_handle{};
199 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); 186 uint32_t func{};
200 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); 187 uint32_t arg{};
201 R_UNLESS(size > 0, ResultInvalidSize); 188 uint32_t stack_bottom{};
202 R_UNLESS((address < address + size), ResultInvalidCurrentMemory); 189 int32_t priority{};
190 int32_t core_id{};
203 191
204 // Validate the attribute and mask. 192 func = Convert<uint32_t>(GetReg32(system, 1));
205 constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached); 193 arg = Convert<uint32_t>(GetReg32(system, 2));
206 R_UNLESS((mask | attr) == mask, ResultInvalidCombination); 194 stack_bottom = Convert<uint32_t>(GetReg32(system, 3));
207 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); 195 priority = Convert<int32_t>(GetReg32(system, 0));
196 core_id = Convert<int32_t>(GetReg32(system, 4));
208 197
209 // Validate that the region is in range for the current process. 198 ret = CreateThread64From32(system, &out_handle, func, arg, stack_bottom, priority, core_id);
210 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
211 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
212 199
213 // Set the memory attribute. 200 SetReg32(system, 0, Convert<uint32_t>(ret));
214 return page_table.SetMemoryAttribute(address, size, mask, attr); 201 SetReg32(system, 1, Convert<uint32_t>(out_handle));
215} 202}
216 203
217static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask, 204static void SvcWrap_StartThread64From32(Core::System& system) {
218 u32 attr) { 205 Result ret{};
219 return SetMemoryAttribute(system, address, size, mask, attr); 206
207 Handle thread_handle{};
208
209 thread_handle = Convert<Handle>(GetReg32(system, 0));
210
211 ret = StartThread64From32(system, thread_handle);
212
213 SetReg32(system, 0, Convert<uint32_t>(ret));
220} 214}
221 215
222/// Maps a memory range into a different range. 216static void SvcWrap_ExitThread64From32(Core::System& system) {
223static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { 217 ExitThread64From32(system);
224 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, 218}
225 src_addr, size);
226 219
227 auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; 220static void SvcWrap_SleepThread64From32(Core::System& system) {
221 int64_t ns{};
228 222
229 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; 223 std::array<uint32_t, 2> ns_gather{};
230 result.IsError()) { 224 ns_gather[0] = GetReg32(system, 0);
231 return result; 225 ns_gather[1] = GetReg32(system, 1);
232 } 226 ns = Convert<int64_t>(ns_gather);
233 227
234 return page_table.MapMemory(dst_addr, src_addr, size); 228 SleepThread64From32(system, ns);
235} 229}
236 230
237static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { 231static void SvcWrap_GetThreadPriority64From32(Core::System& system) {
238 return MapMemory(system, dst_addr, src_addr, size); 232 Result ret{};
233
234 int32_t out_priority{};
235 Handle thread_handle{};
236
237 thread_handle = Convert<Handle>(GetReg32(system, 1));
238
239 ret = GetThreadPriority64From32(system, &out_priority, thread_handle);
240
241 SetReg32(system, 0, Convert<uint32_t>(ret));
242 SetReg32(system, 1, Convert<uint32_t>(out_priority));
239} 243}
240 244
241/// Unmaps a region that was previously mapped with svcMapMemory 245static void SvcWrap_SetThreadPriority64From32(Core::System& system) {
242static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) { 246 Result ret{};
243 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
244 src_addr, size);
245 247
246 auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; 248 Handle thread_handle{};
249 int32_t priority{};
247 250
248 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; 251 thread_handle = Convert<Handle>(GetReg32(system, 0));
249 result.IsError()) { 252 priority = Convert<int32_t>(GetReg32(system, 1));
250 return result;
251 }
252 253
253 return page_table.UnmapMemory(dst_addr, src_addr, size); 254 ret = SetThreadPriority64From32(system, thread_handle, priority);
255
256 SetReg32(system, 0, Convert<uint32_t>(ret));
254} 257}
255 258
256static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) { 259static void SvcWrap_GetThreadCoreMask64From32(Core::System& system) {
257 return UnmapMemory(system, dst_addr, src_addr, size); 260 Result ret{};
261
262 int32_t out_core_id{};
263 uint64_t out_affinity_mask{};
264 Handle thread_handle{};
265
266 thread_handle = Convert<Handle>(GetReg32(system, 2));
267
268 ret = GetThreadCoreMask64From32(system, &out_core_id, &out_affinity_mask, thread_handle);
269
270 SetReg32(system, 0, Convert<uint32_t>(ret));
271 SetReg32(system, 1, Convert<uint32_t>(out_core_id));
272 auto out_affinity_mask_scatter = Convert<std::array<uint32_t, 2>>(out_affinity_mask);
273 SetReg32(system, 2, out_affinity_mask_scatter[0]);
274 SetReg32(system, 3, out_affinity_mask_scatter[1]);
258} 275}
259 276
260template <typename T> 277static void SvcWrap_SetThreadCoreMask64From32(Core::System& system) {
261Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) { 278 Result ret{};
262 auto& process = *system.CurrentProcess();
263 auto& handle_table = process.GetHandleTable();
264 279
265 // Declare the session we're going to allocate. 280 Handle thread_handle{};
266 T* session; 281 int32_t core_id{};
282 uint64_t affinity_mask{};
267 283
268 // Reserve a new session from the process resource limit. 284 thread_handle = Convert<Handle>(GetReg32(system, 0));
269 // FIXME: LimitableResource_SessionCountMax 285 core_id = Convert<int32_t>(GetReg32(system, 1));
270 KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); 286 std::array<uint32_t, 2> affinity_mask_gather{};
271 if (session_reservation.Succeeded()) { 287 affinity_mask_gather[0] = GetReg32(system, 2);
272 session = T::Create(system.Kernel()); 288 affinity_mask_gather[1] = GetReg32(system, 3);
273 } else { 289 affinity_mask = Convert<uint64_t>(affinity_mask_gather);
274 return ResultLimitReached;
275
276 // // We couldn't reserve a session. Check that we support dynamically expanding the
277 // // resource limit.
278 // R_UNLESS(process.GetResourceLimit() ==
279 // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
280 // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
281
282 // // Try to allocate a session from unused slab memory.
283 // session = T::CreateFromUnusedSlabMemory();
284 // R_UNLESS(session != nullptr, ResultLimitReached);
285 // ON_RESULT_FAILURE { session->Close(); };
286
287 // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
288 // // prevent request exhaustion.
289 // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
290 // // no reason to not do this statically.
291 // if constexpr (std::same_as<T, KSession>) {
292 // for (size_t i = 0; i < 2; i++) {
293 // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
294 // R_UNLESS(request != nullptr, ResultLimitReached);
295 // request->Close();
296 // }
297 // }
298
299 // We successfully allocated a session, so add the object we allocated to the resource
300 // limit.
301 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
302 }
303 290
304 // Check that we successfully created a session. 291 ret = SetThreadCoreMask64From32(system, thread_handle, core_id, affinity_mask);
305 R_UNLESS(session != nullptr, ResultOutOfResource);
306 292
307 // Initialize the session. 293 SetReg32(system, 0, Convert<uint32_t>(ret));
308 session->Initialize(nullptr, fmt::format("{}", name)); 294}
309 295
310 // Commit the session reservation. 296static void SvcWrap_GetCurrentProcessorNumber64From32(Core::System& system) {
311 session_reservation.Commit(); 297 int32_t ret{};
312 298
313 // Ensure that we clean up the session (and its only references are handle table) on function 299 ret = GetCurrentProcessorNumber64From32(system);
314 // end.
315 SCOPE_EXIT({
316 session->GetClientSession().Close();
317 session->GetServerSession().Close();
318 });
319 300
320 // Register the session. 301 SetReg32(system, 0, Convert<uint32_t>(ret));
321 T::Register(system.Kernel(), session); 302}
322 303
323 // Add the server session to the handle table. 304static void SvcWrap_SignalEvent64From32(Core::System& system) {
324 R_TRY(handle_table.Add(out_server, &session->GetServerSession())); 305 Result ret{};
325 306
326 // Add the client session to the handle table. 307 Handle event_handle{};
327 const auto result = handle_table.Add(out_client, &session->GetClientSession());
328 308
329 if (!R_SUCCEEDED(result)) { 309 event_handle = Convert<Handle>(GetReg32(system, 0));
330 // Ensure that we maintaing a clean handle state on exit.
331 handle_table.Remove(*out_server);
332 }
333 310
334 return result; 311 ret = SignalEvent64From32(system, event_handle);
312
313 SetReg32(system, 0, Convert<uint32_t>(ret));
335} 314}
336 315
337static Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, 316static void SvcWrap_ClearEvent64From32(Core::System& system) {
338 u32 is_light, u64 name) { 317 Result ret{};
339 if (is_light) { 318
340 // return CreateSession<KLightSession>(system, out_server, out_client, name); 319 Handle event_handle{};
341 return ResultUnknown; 320
342 } else { 321 event_handle = Convert<Handle>(GetReg32(system, 0));
343 return CreateSession<KSession>(system, out_server, out_client, name); 322
344 } 323 ret = ClearEvent64From32(system, event_handle);
324
325 SetReg32(system, 0, Convert<uint32_t>(ret));
345} 326}
346 327
347/// Connect to an OS service given the port name, returns the handle to the port to out 328static void SvcWrap_MapSharedMemory64From32(Core::System& system) {
348static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) { 329 Result ret{};
349 auto& memory = system.Memory();
350 if (!memory.IsValidVirtualAddress(port_name_address)) {
351 LOG_ERROR(Kernel_SVC,
352 "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
353 port_name_address);
354 return ResultNotFound;
355 }
356 330
357 static constexpr std::size_t PortNameMaxLength = 11; 331 Handle shmem_handle{};
358 // Read 1 char beyond the max allowed port name to detect names that are too long. 332 uint32_t address{};
359 const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1); 333 uint32_t size{};
360 if (port_name.size() > PortNameMaxLength) { 334 MemoryPermission map_perm{};
361 LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
362 port_name.size());
363 return ResultOutOfRange;
364 }
365 335
366 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name); 336 shmem_handle = Convert<Handle>(GetReg32(system, 0));
337 address = Convert<uint32_t>(GetReg32(system, 1));
338 size = Convert<uint32_t>(GetReg32(system, 2));
339 map_perm = Convert<MemoryPermission>(GetReg32(system, 3));
367 340
368 // Get the current handle table. 341 ret = MapSharedMemory64From32(system, shmem_handle, address, size, map_perm);
369 auto& kernel = system.Kernel();
370 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
371 342
372 // Find the client port. 343 SetReg32(system, 0, Convert<uint32_t>(ret));
373 auto port = kernel.CreateNamedServicePort(port_name); 344}
374 if (!port) {
375 LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
376 return ResultNotFound;
377 }
378 345
379 // Reserve a handle for the port. 346static void SvcWrap_UnmapSharedMemory64From32(Core::System& system) {
380 // NOTE: Nintendo really does write directly to the output handle here. 347 Result ret{};
381 R_TRY(handle_table.Reserve(out));
382 auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
383 348
384 // Create a session. 349 Handle shmem_handle{};
385 KClientSession* session{}; 350 uint32_t address{};
386 R_TRY(port->CreateSession(std::addressof(session))); 351 uint32_t size{};
387 352
388 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort()); 353 shmem_handle = Convert<Handle>(GetReg32(system, 0));
354 address = Convert<uint32_t>(GetReg32(system, 1));
355 size = Convert<uint32_t>(GetReg32(system, 2));
389 356
390 // Register the session in the table, close the extra reference. 357 ret = UnmapSharedMemory64From32(system, shmem_handle, address, size);
391 handle_table.Register(*out, session);
392 session->Close();
393 358
394 // We succeeded. 359 SetReg32(system, 0, Convert<uint32_t>(ret));
395 handle_guard.Cancel();
396 return ResultSuccess;
397} 360}
398 361
399static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle, 362static void SvcWrap_CreateTransferMemory64From32(Core::System& system) {
400 u32 port_name_address) { 363 Result ret{};
364
365 Handle out_handle{};
366 uint32_t address{};
367 uint32_t size{};
368 MemoryPermission map_perm{};
369
370 address = Convert<uint32_t>(GetReg32(system, 1));
371 size = Convert<uint32_t>(GetReg32(system, 2));
372 map_perm = Convert<MemoryPermission>(GetReg32(system, 3));
373
374 ret = CreateTransferMemory64From32(system, &out_handle, address, size, map_perm);
401 375
402 return ConnectToNamedPort(system, out_handle, port_name_address); 376 SetReg32(system, 0, Convert<uint32_t>(ret));
377 SetReg32(system, 1, Convert<uint32_t>(out_handle));
403} 378}
404 379
405/// Makes a blocking IPC call to a service. 380static void SvcWrap_CloseHandle64From32(Core::System& system) {
406static Result SendSyncRequest(Core::System& system, Handle handle) { 381 Result ret{};
407 auto& kernel = system.Kernel();
408 382
409 // Create the wait queue. 383 Handle handle{};
410 KThreadQueue wait_queue(kernel);
411 384
412 // Get the client session from its handle. 385 handle = Convert<Handle>(GetReg32(system, 0));
413 KScopedAutoObject session =
414 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
415 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
416 386
417 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); 387 ret = CloseHandle64From32(system, handle);
418 388
419 return session->SendSyncRequest(); 389 SetReg32(system, 0, Convert<uint32_t>(ret));
420} 390}
421 391
422static Result SendSyncRequest32(Core::System& system, Handle handle) { 392static void SvcWrap_ResetSignal64From32(Core::System& system) {
423 return SendSyncRequest(system, handle); 393 Result ret{};
394
395 Handle handle{};
396
397 handle = Convert<Handle>(GetReg32(system, 0));
398
399 ret = ResetSignal64From32(system, handle);
400
401 SetReg32(system, 0, Convert<uint32_t>(ret));
424} 402}
425 403
426static Result ReplyAndReceive(Core::System& system, s32* out_index, Handle* handles, 404static void SvcWrap_WaitSynchronization64From32(Core::System& system) {
427 s32 num_handles, Handle reply_target, s64 timeout_ns) { 405 Result ret{};
428 auto& kernel = system.Kernel();
429 auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
430
431 // Convert handle list to object table.
432 std::vector<KSynchronizationObject*> objs(num_handles);
433 R_UNLESS(
434 handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, num_handles),
435 ResultInvalidHandle);
436
437 // Ensure handles are closed when we're done.
438 SCOPE_EXIT({
439 for (auto i = 0; i < num_handles; ++i) {
440 objs[i]->Close();
441 }
442 });
443
444 // Reply to the target, if one is specified.
445 if (reply_target != InvalidHandle) {
446 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
447 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
448
449 // If we fail to reply, we want to set the output index to -1.
450 // ON_RESULT_FAILURE { *out_index = -1; };
451
452 // Send the reply.
453 // R_TRY(session->SendReply());
454
455 Result rc = session->SendReply();
456 if (!R_SUCCEEDED(rc)) {
457 *out_index = -1;
458 return rc;
459 }
460 }
461 406
462 // Wait for a message. 407 int32_t out_index{};
463 while (true) { 408 uint32_t handles{};
464 // Wait for an object. 409 int32_t num_handles{};
465 s32 index; 410 int64_t timeout_ns{};
466 Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(), 411
467 static_cast<s32>(objs.size()), timeout_ns); 412 handles = Convert<uint32_t>(GetReg32(system, 1));
468 if (result == ResultTimedOut) { 413 num_handles = Convert<int32_t>(GetReg32(system, 2));
469 return result; 414 std::array<uint32_t, 2> timeout_ns_gather{};
470 } 415 timeout_ns_gather[0] = GetReg32(system, 0);
471 416 timeout_ns_gather[1] = GetReg32(system, 3);
472 // Receive the request. 417 timeout_ns = Convert<int64_t>(timeout_ns_gather);
473 if (R_SUCCEEDED(result)) { 418
474 KServerSession* session = objs[index]->DynamicCast<KServerSession*>(); 419 ret = WaitSynchronization64From32(system, &out_index, handles, num_handles, timeout_ns);
475 if (session != nullptr) { 420
476 result = session->ReceiveRequest(); 421 SetReg32(system, 0, Convert<uint32_t>(ret));
477 if (result == ResultNotFound) { 422 SetReg32(system, 1, Convert<uint32_t>(out_index));
478 continue;
479 }
480 }
481 }
482
483 *out_index = index;
484 return result;
485 }
486} 423}
487 424
488/// Get the ID for the specified thread. 425static void SvcWrap_CancelSynchronization64From32(Core::System& system) {
489static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { 426 Result ret{};
490 // Get the thread from its handle.
491 KScopedAutoObject thread =
492 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
493 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
494 427
495 // Get the thread's id. 428 Handle handle{};
496 *out_thread_id = thread->GetId(); 429
497 return ResultSuccess; 430 handle = Convert<Handle>(GetReg32(system, 0));
431
432 ret = CancelSynchronization64From32(system, handle);
433
434 SetReg32(system, 0, Convert<uint32_t>(ret));
498} 435}
499 436
500static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high, 437static void SvcWrap_ArbitrateLock64From32(Core::System& system) {
501 Handle thread_handle) { 438 Result ret{};
502 u64 out_thread_id{}; 439
503 const Result result{GetThreadId(system, &out_thread_id, thread_handle)}; 440 Handle thread_handle{};
441 uint32_t address{};
442 uint32_t tag{};
504 443
505 *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); 444 thread_handle = Convert<Handle>(GetReg32(system, 0));
506 *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); 445 address = Convert<uint32_t>(GetReg32(system, 1));
446 tag = Convert<uint32_t>(GetReg32(system, 2));
507 447
508 return result; 448 ret = ArbitrateLock64From32(system, thread_handle, address, tag);
449
450 SetReg32(system, 0, Convert<uint32_t>(ret));
509} 451}
510 452
511/// Gets the ID of the specified process or a specified thread's owning process. 453static void SvcWrap_ArbitrateUnlock64From32(Core::System& system) {
512static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) { 454 Result ret{};
513 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
514 455
515 // Get the object from the handle table. 456 uint32_t address{};
516 KScopedAutoObject obj =
517 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>(
518 static_cast<Handle>(handle));
519 R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
520 457
521 // Get the process from the object. 458 address = Convert<uint32_t>(GetReg32(system, 0));
522 KProcess* process = nullptr;
523 if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
524 // The object is a process, so we can use it directly.
525 process = p;
526 } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
527 // The object is a thread, so we want to use its parent.
528 process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
529 } else {
530 // TODO(bunnei): This should also handle debug objects before returning.
531 UNIMPLEMENTED_MSG("Debug objects not implemented");
532 }
533 459
534 // Make sure the target process exists. 460 ret = ArbitrateUnlock64From32(system, address);
535 R_UNLESS(process != nullptr, ResultInvalidHandle);
536 461
537 // Get the process id. 462 SetReg32(system, 0, Convert<uint32_t>(ret));
538 *out_process_id = process->GetId(); 463}
464
465static void SvcWrap_WaitProcessWideKeyAtomic64From32(Core::System& system) {
466 Result ret{};
467
468 uint32_t address{};
469 uint32_t cv_key{};
470 uint32_t tag{};
471 int64_t timeout_ns{};
472
473 address = Convert<uint32_t>(GetReg32(system, 0));
474 cv_key = Convert<uint32_t>(GetReg32(system, 1));
475 tag = Convert<uint32_t>(GetReg32(system, 2));
476 std::array<uint32_t, 2> timeout_ns_gather{};
477 timeout_ns_gather[0] = GetReg32(system, 3);
478 timeout_ns_gather[1] = GetReg32(system, 4);
479 timeout_ns = Convert<int64_t>(timeout_ns_gather);
539 480
540 return ResultSuccess; 481 ret = WaitProcessWideKeyAtomic64From32(system, address, cv_key, tag, timeout_ns);
482
483 SetReg32(system, 0, Convert<uint32_t>(ret));
541} 484}
542 485
543static Result GetProcessId32(Core::System& system, u32* out_process_id_low, 486static void SvcWrap_SignalProcessWideKey64From32(Core::System& system) {
544 u32* out_process_id_high, Handle handle) { 487 uint32_t cv_key{};
545 u64 out_process_id{}; 488 int32_t count{};
546 const auto result = GetProcessId(system, &out_process_id, handle); 489
547 *out_process_id_low = static_cast<u32>(out_process_id); 490 cv_key = Convert<uint32_t>(GetReg32(system, 0));
548 *out_process_id_high = static_cast<u32>(out_process_id >> 32); 491 count = Convert<int32_t>(GetReg32(system, 1));
549 return result; 492
493 SignalProcessWideKey64From32(system, cv_key, count);
550} 494}
551 495
552/// Wait for the given handles to synchronize, timeout after the specified nanoseconds 496static void SvcWrap_GetSystemTick64From32(Core::System& system) {
553static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, 497 int64_t ret{};
554 s32 num_handles, s64 nano_seconds) {
555 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
556 handles_address, num_handles, nano_seconds);
557 498
558 // Ensure number of handles is valid. 499 ret = GetSystemTick64From32(system);
559 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
560 500
561 auto& kernel = system.Kernel(); 501 auto ret_scatter = Convert<std::array<uint32_t, 2>>(ret);
562 std::vector<KSynchronizationObject*> objs(num_handles); 502 SetReg32(system, 0, ret_scatter[0]);
563 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); 503 SetReg32(system, 1, ret_scatter[1]);
564 Handle* handles = system.Memory().GetPointer<Handle>(handles_address); 504}
565 505
566 // Copy user handles. 506static void SvcWrap_ConnectToNamedPort64From32(Core::System& system) {
567 if (num_handles > 0) { 507 Result ret{};
568 // Convert the handles to objects. 508
569 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles, 509 Handle out_handle{};
570 num_handles), 510 uint32_t name{};
571 ResultInvalidHandle);
572 for (const auto& obj : objs) {
573 kernel.RegisterInUseObject(obj);
574 }
575 }
576 511
577 // Ensure handles are closed when we're done. 512 name = Convert<uint32_t>(GetReg32(system, 1));
578 SCOPE_EXIT({
579 for (s32 i = 0; i < num_handles; ++i) {
580 kernel.UnregisterInUseObject(objs[i]);
581 objs[i]->Close();
582 }
583 });
584 513
585 return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()), 514 ret = ConnectToNamedPort64From32(system, &out_handle, name);
586 nano_seconds); 515
516 SetReg32(system, 0, Convert<uint32_t>(ret));
517 SetReg32(system, 1, Convert<uint32_t>(out_handle));
587} 518}
588 519
589static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, 520static void SvcWrap_SendSyncRequest64From32(Core::System& system) {
590 s32 num_handles, u32 timeout_high, s32* index) { 521 Result ret{};
591 const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)}; 522
592 return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds); 523 Handle session_handle{};
524
525 session_handle = Convert<Handle>(GetReg32(system, 0));
526
527 ret = SendSyncRequest64From32(system, session_handle);
528
529 SetReg32(system, 0, Convert<uint32_t>(ret));
593} 530}
594 531
595/// Resumes a thread waiting on WaitSynchronization 532static void SvcWrap_SendSyncRequestWithUserBuffer64From32(Core::System& system) {
596static Result CancelSynchronization(Core::System& system, Handle handle) { 533 Result ret{};
597 LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle); 534
535 uint32_t message_buffer{};
536 uint32_t message_buffer_size{};
537 Handle session_handle{};
538
539 message_buffer = Convert<uint32_t>(GetReg32(system, 0));
540 message_buffer_size = Convert<uint32_t>(GetReg32(system, 1));
541 session_handle = Convert<Handle>(GetReg32(system, 2));
598 542
599 // Get the thread from its handle. 543 ret = SendSyncRequestWithUserBuffer64From32(system, message_buffer, message_buffer_size, session_handle);
600 KScopedAutoObject thread =
601 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
602 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
603 544
604 // Cancel the thread's wait. 545 SetReg32(system, 0, Convert<uint32_t>(ret));
605 thread->WaitCancel();
606 return ResultSuccess;
607} 546}
608 547
609static Result CancelSynchronization32(Core::System& system, Handle handle) { 548static void SvcWrap_SendAsyncRequestWithUserBuffer64From32(Core::System& system) {
610 return CancelSynchronization(system, handle); 549 Result ret{};
550
551 Handle out_event_handle{};
552 uint32_t message_buffer{};
553 uint32_t message_buffer_size{};
554 Handle session_handle{};
555
556 message_buffer = Convert<uint32_t>(GetReg32(system, 1));
557 message_buffer_size = Convert<uint32_t>(GetReg32(system, 2));
558 session_handle = Convert<Handle>(GetReg32(system, 3));
559
560 ret = SendAsyncRequestWithUserBuffer64From32(system, &out_event_handle, message_buffer, message_buffer_size, session_handle);
561
562 SetReg32(system, 0, Convert<uint32_t>(ret));
563 SetReg32(system, 1, Convert<uint32_t>(out_event_handle));
611} 564}
612 565
613/// Attempts to locks a mutex 566static void SvcWrap_GetProcessId64From32(Core::System& system) {
614static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) { 567 Result ret{};
615 LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
616 thread_handle, address, tag);
617 568
618 // Validate the input address. 569 uint64_t out_process_id{};
619 if (IsKernelAddress(address)) { 570 Handle process_handle{};
620 LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})", 571
621 address); 572 process_handle = Convert<Handle>(GetReg32(system, 1));
622 return ResultInvalidCurrentMemory; 573
623 } 574 ret = GetProcessId64From32(system, &out_process_id, process_handle);
624 if (!Common::IsAligned(address, sizeof(u32))) {
625 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
626 return ResultInvalidAddress;
627 }
628 575
629 return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); 576 SetReg32(system, 0, Convert<uint32_t>(ret));
577 auto out_process_id_scatter = Convert<std::array<uint32_t, 2>>(out_process_id);
578 SetReg32(system, 1, out_process_id_scatter[0]);
579 SetReg32(system, 2, out_process_id_scatter[1]);
630} 580}
631 581
632static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) { 582static void SvcWrap_GetThreadId64From32(Core::System& system) {
633 return ArbitrateLock(system, thread_handle, address, tag); 583 Result ret{};
584
585 uint64_t out_thread_id{};
586 Handle thread_handle{};
587
588 thread_handle = Convert<Handle>(GetReg32(system, 1));
589
590 ret = GetThreadId64From32(system, &out_thread_id, thread_handle);
591
592 SetReg32(system, 0, Convert<uint32_t>(ret));
593 auto out_thread_id_scatter = Convert<std::array<uint32_t, 2>>(out_thread_id);
594 SetReg32(system, 1, out_thread_id_scatter[0]);
595 SetReg32(system, 2, out_thread_id_scatter[1]);
634} 596}
635 597
636/// Unlock a mutex 598static void SvcWrap_Break64From32(Core::System& system) {
637static Result ArbitrateUnlock(Core::System& system, VAddr address) { 599 BreakReason break_reason{};
638 LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); 600 uint32_t arg{};
601 uint32_t size{};
639 602
640 // Validate the input address. 603 break_reason = Convert<BreakReason>(GetReg32(system, 0));
641 if (IsKernelAddress(address)) { 604 arg = Convert<uint32_t>(GetReg32(system, 1));
642 LOG_ERROR(Kernel_SVC, 605 size = Convert<uint32_t>(GetReg32(system, 2));
643 "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
644 address);
645 return ResultInvalidCurrentMemory;
646 }
647 if (!Common::IsAligned(address, sizeof(u32))) {
648 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
649 return ResultInvalidAddress;
650 }
651 606
652 return system.Kernel().CurrentProcess()->SignalToAddress(address); 607 Break64From32(system, break_reason, arg, size);
653} 608}
654
655static Result ArbitrateUnlock32(Core::System& system, u32 address) {
656 return ArbitrateUnlock(system, address);
657}
658
659/// Break program execution
660static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
661 BreakReason break_reason =
662 static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag));
663 bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0;
664
665 bool has_dumped_buffer{};
666 std::vector<u8> debug_buffer;
667
668 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
669 if (sz == 0 || addr == 0 || has_dumped_buffer) {
670 return;
671 }
672
673 auto& memory = system.Memory();
674
675 // This typically is an error code so we're going to assume this is the case
676 if (sz == sizeof(u32)) {
677 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
678 } else {
679 // We don't know what's in here so we'll hexdump it
680 debug_buffer.resize(sz);
681 memory.ReadBlock(addr, debug_buffer.data(), sz);
682 std::string hexdump;
683 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
684 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
685 if (i != 0 && i % 16 == 0) {
686 hexdump += '\n';
687 }
688 }
689 LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
690 }
691 has_dumped_buffer = true;
692 };
693 switch (break_reason) {
694 case BreakReason::Panic:
695 LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
696 info2);
697 handle_debug_buffer(info1, info2);
698 break;
699 case BreakReason::Assert:
700 LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
701 info1, info2);
702 handle_debug_buffer(info1, info2);
703 break;
704 case BreakReason::User:
705 LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
706 handle_debug_buffer(info1, info2);
707 break;
708 case BreakReason::PreLoadDll:
709 LOG_INFO(Debug_Emulated,
710 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
711 info2);
712 break;
713 case BreakReason::PostLoadDll:
714 LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
715 info2);
716 break;
717 case BreakReason::PreUnloadDll:
718 LOG_INFO(Debug_Emulated,
719 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
720 info2);
721 break;
722 case BreakReason::PostUnloadDll:
723 LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
724 info1, info2);
725 break;
726 case BreakReason::CppException:
727 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
728 break;
729 default:
730 LOG_WARNING(
731 Debug_Emulated,
732 "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
733 reason, info1, info2);
734 handle_debug_buffer(info1, info2);
735 break;
736 }
737 609
738 system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, 610static void SvcWrap_OutputDebugString64From32(Core::System& system) {
739 has_dumped_buffer ? std::make_optional(debug_buffer) 611 Result ret{};
740 : std::nullopt);
741 612
742 if (!notification_only) { 613 uint32_t debug_str{};
743 LOG_CRITICAL( 614 uint32_t len{};
744 Debug_Emulated,
745 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
746 reason, info1, info2);
747 615
748 handle_debug_buffer(info1, info2); 616 debug_str = Convert<uint32_t>(GetReg32(system, 0));
617 len = Convert<uint32_t>(GetReg32(system, 1));
749 618
750 auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); 619 ret = OutputDebugString64From32(system, debug_str, len);
751 const auto thread_processor_id = current_thread->GetActiveCore();
752 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
753 }
754 620
755 if (system.DebuggerEnabled()) { 621 SetReg32(system, 0, Convert<uint32_t>(ret));
756 auto* thread = system.Kernel().GetCurrentEmuThread();
757 system.GetDebugger().NotifyThreadStopped(thread);
758 thread->RequestSuspend(Kernel::SuspendType::Debug);
759 }
760} 622}
761 623
762static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) { 624static void SvcWrap_ReturnFromException64From32(Core::System& system) {
763 Break(system, reason, info1, info2); 625 Result result{};
626
627 result = Convert<Result>(GetReg32(system, 0));
628
629 ReturnFromException64From32(system, result);
764} 630}
765 631
766/// Used to output a message on a debug hardware unit - does nothing on a retail unit 632static void SvcWrap_GetInfo64From32(Core::System& system) {
767static void OutputDebugString(Core::System& system, VAddr address, u64 len) { 633 Result ret{};
768 if (len == 0) {
769 return;
770 }
771 634
772 std::string str(len, '\0'); 635 uint64_t out{};
773 system.Memory().ReadBlock(address, str.data(), str.size()); 636 InfoType info_type{};
774 LOG_DEBUG(Debug_Emulated, "{}", str); 637 Handle handle{};
775} 638 uint64_t info_subtype{};
776
777static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
778 OutputDebugString(system, address, len);
779}
780
781/// Gets system/memory information for the current process
782static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
783 u64 info_sub_id) {
784 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
785 info_sub_id, handle);
786
787 const auto info_id_type = static_cast<InfoType>(info_id);
788
789 switch (info_id_type) {
790 case InfoType::CoreMask:
791 case InfoType::PriorityMask:
792 case InfoType::AliasRegionAddress:
793 case InfoType::AliasRegionSize:
794 case InfoType::HeapRegionAddress:
795 case InfoType::HeapRegionSize:
796 case InfoType::AslrRegionAddress:
797 case InfoType::AslrRegionSize:
798 case InfoType::StackRegionAddress:
799 case InfoType::StackRegionSize:
800 case InfoType::TotalMemorySize:
801 case InfoType::UsedMemorySize:
802 case InfoType::SystemResourceSizeTotal:
803 case InfoType::SystemResourceSizeUsed:
804 case InfoType::ProgramId:
805 case InfoType::UserExceptionContextAddress:
806 case InfoType::TotalNonSystemMemorySize:
807 case InfoType::UsedNonSystemMemorySize:
808 case InfoType::IsApplication:
809 case InfoType::FreeThreadCount: {
810 if (info_sub_id != 0) {
811 LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
812 info_sub_id);
813 return ResultInvalidEnumValue;
814 }
815
816 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
817 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
818 if (process.IsNull()) {
819 LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
820 info_id, info_sub_id, handle);
821 return ResultInvalidHandle;
822 }
823
824 switch (info_id_type) {
825 case InfoType::CoreMask:
826 *result = process->GetCoreMask();
827 return ResultSuccess;
828
829 case InfoType::PriorityMask:
830 *result = process->GetPriorityMask();
831 return ResultSuccess;
832
833 case InfoType::AliasRegionAddress:
834 *result = process->PageTable().GetAliasRegionStart();
835 return ResultSuccess;
836
837 case InfoType::AliasRegionSize:
838 *result = process->PageTable().GetAliasRegionSize();
839 return ResultSuccess;
840
841 case InfoType::HeapRegionAddress:
842 *result = process->PageTable().GetHeapRegionStart();
843 return ResultSuccess;
844
845 case InfoType::HeapRegionSize:
846 *result = process->PageTable().GetHeapRegionSize();
847 return ResultSuccess;
848
849 case InfoType::AslrRegionAddress:
850 *result = process->PageTable().GetAliasCodeRegionStart();
851 return ResultSuccess;
852
853 case InfoType::AslrRegionSize:
854 *result = process->PageTable().GetAliasCodeRegionSize();
855 return ResultSuccess;
856
857 case InfoType::StackRegionAddress:
858 *result = process->PageTable().GetStackRegionStart();
859 return ResultSuccess;
860
861 case InfoType::StackRegionSize:
862 *result = process->PageTable().GetStackRegionSize();
863 return ResultSuccess;
864
865 case InfoType::TotalMemorySize:
866 *result = process->GetTotalPhysicalMemoryAvailable();
867 return ResultSuccess;
868
869 case InfoType::UsedMemorySize:
870 *result = process->GetTotalPhysicalMemoryUsed();
871 return ResultSuccess;
872
873 case InfoType::SystemResourceSizeTotal:
874 *result = process->GetSystemResourceSize();
875 return ResultSuccess;
876
877 case InfoType::SystemResourceSizeUsed:
878 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
879 *result = process->GetSystemResourceUsage();
880 return ResultSuccess;
881
882 case InfoType::ProgramId:
883 *result = process->GetProgramID();
884 return ResultSuccess;
885
886 case InfoType::UserExceptionContextAddress:
887 *result = process->GetProcessLocalRegionAddress();
888 return ResultSuccess;
889
890 case InfoType::TotalNonSystemMemorySize:
891 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
892 return ResultSuccess;
893
894 case InfoType::UsedNonSystemMemorySize:
895 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
896 return ResultSuccess;
897
898 case InfoType::FreeThreadCount:
899 *result = process->GetFreeThreadCount();
900 return ResultSuccess;
901
902 default:
903 break;
904 }
905
906 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
907 return ResultInvalidEnumValue;
908 }
909 639
910 case InfoType::DebuggerAttached: 640 info_type = Convert<InfoType>(GetReg32(system, 1));
911 *result = 0; 641 handle = Convert<Handle>(GetReg32(system, 2));
912 return ResultSuccess; 642 std::array<uint32_t, 2> info_subtype_gather{};
913 643 info_subtype_gather[0] = GetReg32(system, 0);
914 case InfoType::ResourceLimit: { 644 info_subtype_gather[1] = GetReg32(system, 3);
915 if (handle != 0) { 645 info_subtype = Convert<uint64_t>(info_subtype_gather);
916 LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
917 return ResultInvalidHandle;
918 }
919
920 if (info_sub_id != 0) {
921 LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
922 info_sub_id);
923 return ResultInvalidCombination;
924 }
925
926 KProcess* const current_process = system.Kernel().CurrentProcess();
927 KHandleTable& handle_table = current_process->GetHandleTable();
928 const auto resource_limit = current_process->GetResourceLimit();
929 if (!resource_limit) {
930 *result = Svc::InvalidHandle;
931 // Yes, the kernel considers this a successful operation.
932 return ResultSuccess;
933 }
934
935 Handle resource_handle{};
936 R_TRY(handle_table.Add(&resource_handle, resource_limit));
937
938 *result = resource_handle;
939 return ResultSuccess;
940 }
941 646
942 case InfoType::RandomEntropy: 647 ret = GetInfo64From32(system, &out, info_type, handle, info_subtype);
943 if (handle != 0) {
944 LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
945 handle);
946 return ResultInvalidHandle;
947 }
948
949 if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
950 LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
951 KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
952 return ResultInvalidCombination;
953 }
954
955 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
956 return ResultSuccess;
957
958 case InfoType::InitialProcessIdRange:
959 LOG_WARNING(Kernel_SVC,
960 "(STUBBED) Attempted to query privileged process id bounds, returned 0");
961 *result = 0;
962 return ResultSuccess;
963
964 case InfoType::ThreadTickCount: {
965 constexpr u64 num_cpus = 4;
966 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
967 LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
968 info_sub_id);
969 return ResultInvalidCombination;
970 }
971
972 KScopedAutoObject thread =
973 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
974 static_cast<Handle>(handle));
975 if (thread.IsNull()) {
976 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
977 static_cast<Handle>(handle));
978 return ResultInvalidHandle;
979 }
980
981 const auto& core_timing = system.CoreTiming();
982 const auto& scheduler = *system.Kernel().CurrentScheduler();
983 const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
984 const bool same_thread = current_thread == thread.GetPointerUnsafe();
985
986 const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
987 u64 out_ticks = 0;
988 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
989 const u64 thread_ticks = current_thread->GetCpuTime();
990
991 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
992 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
993 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
994 }
995
996 *result = out_ticks;
997 return ResultSuccess;
998 }
999 case InfoType::IdleTickCount: {
1000 // Verify the input handle is invalid.
1001 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
1002
1003 // Verify the requested core is valid.
1004 const bool core_valid =
1005 (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
1006 (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
1007 R_UNLESS(core_valid, ResultInvalidCombination);
1008
1009 // Get the idle tick count.
1010 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
1011 return ResultSuccess;
1012 }
1013 case InfoType::MesosphereCurrentProcess: {
1014 // Verify the input handle is invalid.
1015 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
1016 648
1017 // Verify the sub-type is valid. 649 SetReg32(system, 0, Convert<uint32_t>(ret));
1018 R_UNLESS(info_sub_id == 0, ResultInvalidCombination); 650 auto out_scatter = Convert<std::array<uint32_t, 2>>(out);
651 SetReg32(system, 1, out_scatter[0]);
652 SetReg32(system, 2, out_scatter[1]);
653}
1019 654
1020 // Get the handle table. 655static void SvcWrap_FlushEntireDataCache64From32(Core::System& system) {
1021 KProcess* current_process = system.Kernel().CurrentProcess(); 656 FlushEntireDataCache64From32(system);
1022 KHandleTable& handle_table = current_process->GetHandleTable(); 657}
1023 658
1024 // Get a new handle for the current process. 659static void SvcWrap_FlushDataCache64From32(Core::System& system) {
1025 Handle tmp; 660 Result ret{};
1026 R_TRY(handle_table.Add(&tmp, current_process));
1027 661
1028 // Set the output. 662 uint32_t address{};
1029 *result = tmp; 663 uint32_t size{};
1030 664
1031 // We succeeded. 665 address = Convert<uint32_t>(GetReg32(system, 0));
1032 return ResultSuccess; 666 size = Convert<uint32_t>(GetReg32(system, 1));
1033 } 667
1034 default: 668 ret = FlushDataCache64From32(system, address, size);
1035 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id); 669
1036 return ResultInvalidEnumValue; 670 SetReg32(system, 0, Convert<uint32_t>(ret));
1037 }
1038} 671}
1039 672
1040static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low, 673static void SvcWrap_MapPhysicalMemory64From32(Core::System& system) {
1041 u32 info_id, u32 handle, u32 sub_id_high) { 674 Result ret{};
1042 const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)}; 675
1043 u64 res_value{}; 676 uint32_t address{};
677 uint32_t size{};
1044 678
1045 const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)}; 679 address = Convert<uint32_t>(GetReg32(system, 0));
1046 *result_high = static_cast<u32>(res_value >> 32); 680 size = Convert<uint32_t>(GetReg32(system, 1));
1047 *result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
1048 681
1049 return result; 682 ret = MapPhysicalMemory64From32(system, address, size);
683
684 SetReg32(system, 0, Convert<uint32_t>(ret));
1050} 685}
1051 686
1052/// Maps memory at a desired address 687static void SvcWrap_UnmapPhysicalMemory64From32(Core::System& system) {
1053static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { 688 Result ret{};
1054 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
1055 689
1056 if (!Common::Is4KBAligned(addr)) { 690 uint32_t address{};
1057 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); 691 uint32_t size{};
1058 return ResultInvalidAddress;
1059 }
1060 692
1061 if (!Common::Is4KBAligned(size)) { 693 address = Convert<uint32_t>(GetReg32(system, 0));
1062 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); 694 size = Convert<uint32_t>(GetReg32(system, 1));
1063 return ResultInvalidSize;
1064 }
1065 695
1066 if (size == 0) { 696 ret = UnmapPhysicalMemory64From32(system, address, size);
1067 LOG_ERROR(Kernel_SVC, "Size is zero");
1068 return ResultInvalidSize;
1069 }
1070 697
1071 if (!(addr < addr + size)) { 698 SetReg32(system, 0, Convert<uint32_t>(ret));
1072 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); 699}
1073 return ResultInvalidMemoryRegion;
1074 }
1075 700
1076 KProcess* const current_process{system.Kernel().CurrentProcess()}; 701static void SvcWrap_GetDebugFutureThreadInfo64From32(Core::System& system) {
1077 auto& page_table{current_process->PageTable()}; 702 Result ret{};
1078 703
1079 if (current_process->GetSystemResourceSize() == 0) { 704 ilp32::LastThreadContext out_context{};
1080 LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); 705 uint64_t out_thread_id{};
1081 return ResultInvalidState; 706 Handle debug_handle{};
1082 } 707 int64_t ns{};
1083 708
1084 if (!page_table.IsInsideAddressSpace(addr, size)) { 709 debug_handle = Convert<Handle>(GetReg32(system, 2));
1085 LOG_ERROR(Kernel_SVC, 710 std::array<uint32_t, 2> ns_gather{};
1086 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, 711 ns_gather[0] = GetReg32(system, 0);
1087 size); 712 ns_gather[1] = GetReg32(system, 1);
1088 return ResultInvalidMemoryRegion; 713 ns = Convert<int64_t>(ns_gather);
1089 }
1090 714
1091 if (page_table.IsOutsideAliasRegion(addr, size)) { 715 ret = GetDebugFutureThreadInfo64From32(system, &out_context, &out_thread_id, debug_handle, ns);
1092 LOG_ERROR(Kernel_SVC,
1093 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
1094 size);
1095 return ResultInvalidMemoryRegion;
1096 }
1097 716
1098 return page_table.MapPhysicalMemory(addr, size); 717 SetReg32(system, 0, Convert<uint32_t>(ret));
718 auto out_context_scatter = Convert<std::array<uint32_t, 4>>(out_context);
719 SetReg32(system, 1, out_context_scatter[0]);
720 SetReg32(system, 2, out_context_scatter[1]);
721 SetReg32(system, 3, out_context_scatter[2]);
722 SetReg32(system, 4, out_context_scatter[3]);
723 auto out_thread_id_scatter = Convert<std::array<uint32_t, 2>>(out_thread_id);
724 SetReg32(system, 5, out_thread_id_scatter[0]);
725 SetReg32(system, 6, out_thread_id_scatter[1]);
1099} 726}
1100 727
1101static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { 728static void SvcWrap_GetLastThreadInfo64From32(Core::System& system) {
1102 return MapPhysicalMemory(system, addr, size); 729 Result ret{};
730
731 ilp32::LastThreadContext out_context{};
732 uintptr_t out_tls_address{};
733 uint32_t out_flags{};
734
735 ret = GetLastThreadInfo64From32(system, &out_context, &out_tls_address, &out_flags);
736
737 SetReg32(system, 0, Convert<uint32_t>(ret));
738 auto out_context_scatter = Convert<std::array<uint32_t, 4>>(out_context);
739 SetReg32(system, 1, out_context_scatter[0]);
740 SetReg32(system, 2, out_context_scatter[1]);
741 SetReg32(system, 3, out_context_scatter[2]);
742 SetReg32(system, 4, out_context_scatter[3]);
743 SetReg32(system, 5, Convert<uint32_t>(out_tls_address));
744 SetReg32(system, 6, Convert<uint32_t>(out_flags));
1103} 745}
1104 746
1105/// Unmaps memory previously mapped via MapPhysicalMemory 747static void SvcWrap_GetResourceLimitLimitValue64From32(Core::System& system) {
1106static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) { 748 Result ret{};
1107 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
1108 749
1109 if (!Common::Is4KBAligned(addr)) { 750 int64_t out_limit_value{};
1110 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr); 751 Handle resource_limit_handle{};
1111 return ResultInvalidAddress; 752 LimitableResource which{};
1112 }
1113 753
1114 if (!Common::Is4KBAligned(size)) { 754 resource_limit_handle = Convert<Handle>(GetReg32(system, 1));
1115 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size); 755 which = Convert<LimitableResource>(GetReg32(system, 2));
1116 return ResultInvalidSize;
1117 }
1118 756
1119 if (size == 0) { 757 ret = GetResourceLimitLimitValue64From32(system, &out_limit_value, resource_limit_handle, which);
1120 LOG_ERROR(Kernel_SVC, "Size is zero");
1121 return ResultInvalidSize;
1122 }
1123 758
1124 if (!(addr < addr + size)) { 759 SetReg32(system, 0, Convert<uint32_t>(ret));
1125 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address"); 760 auto out_limit_value_scatter = Convert<std::array<uint32_t, 2>>(out_limit_value);
1126 return ResultInvalidMemoryRegion; 761 SetReg32(system, 1, out_limit_value_scatter[0]);
1127 } 762 SetReg32(system, 2, out_limit_value_scatter[1]);
763}
1128 764
1129 KProcess* const current_process{system.Kernel().CurrentProcess()}; 765static void SvcWrap_GetResourceLimitCurrentValue64From32(Core::System& system) {
1130 auto& page_table{current_process->PageTable()}; 766 Result ret{};
1131 767
1132 if (current_process->GetSystemResourceSize() == 0) { 768 int64_t out_current_value{};
1133 LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); 769 Handle resource_limit_handle{};
1134 return ResultInvalidState; 770 LimitableResource which{};
1135 }
1136 771
1137 if (!page_table.IsInsideAddressSpace(addr, size)) { 772 resource_limit_handle = Convert<Handle>(GetReg32(system, 1));
1138 LOG_ERROR(Kernel_SVC, 773 which = Convert<LimitableResource>(GetReg32(system, 2));
1139 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
1140 size);
1141 return ResultInvalidMemoryRegion;
1142 }
1143 774
1144 if (page_table.IsOutsideAliasRegion(addr, size)) { 775 ret = GetResourceLimitCurrentValue64From32(system, &out_current_value, resource_limit_handle, which);
1145 LOG_ERROR(Kernel_SVC,
1146 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
1147 size);
1148 return ResultInvalidMemoryRegion;
1149 }
1150 776
1151 return page_table.UnmapPhysicalMemory(addr, size); 777 SetReg32(system, 0, Convert<uint32_t>(ret));
778 auto out_current_value_scatter = Convert<std::array<uint32_t, 2>>(out_current_value);
779 SetReg32(system, 1, out_current_value_scatter[0]);
780 SetReg32(system, 2, out_current_value_scatter[1]);
1152} 781}
1153 782
1154static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) { 783static void SvcWrap_SetThreadActivity64From32(Core::System& system) {
1155 return UnmapPhysicalMemory(system, addr, size); 784 Result ret{};
785
786 Handle thread_handle{};
787 ThreadActivity thread_activity{};
788
789 thread_handle = Convert<Handle>(GetReg32(system, 0));
790 thread_activity = Convert<ThreadActivity>(GetReg32(system, 1));
791
792 ret = SetThreadActivity64From32(system, thread_handle, thread_activity);
793
794 SetReg32(system, 0, Convert<uint32_t>(ret));
795}
796
797static void SvcWrap_GetThreadContext364From32(Core::System& system) {
798 Result ret{};
799
800 uint32_t out_context{};
801 Handle thread_handle{};
802
803 out_context = Convert<uint32_t>(GetReg32(system, 0));
804 thread_handle = Convert<Handle>(GetReg32(system, 1));
805
806 ret = GetThreadContext364From32(system, out_context, thread_handle);
807
808 SetReg32(system, 0, Convert<uint32_t>(ret));
1156} 809}
1157 810
1158/// Sets the thread activity 811static void SvcWrap_WaitForAddress64From32(Core::System& system) {
1159static Result SetThreadActivity(Core::System& system, Handle thread_handle, 812 Result ret{};
1160 ThreadActivity thread_activity) { 813
1161 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, 814 uint32_t address{};
1162 thread_activity); 815 ArbitrationType arb_type{};
816 int32_t value{};
817 int64_t timeout_ns{};
818
819 address = Convert<uint32_t>(GetReg32(system, 0));
820 arb_type = Convert<ArbitrationType>(GetReg32(system, 1));
821 value = Convert<int32_t>(GetReg32(system, 2));
822 std::array<uint32_t, 2> timeout_ns_gather{};
823 timeout_ns_gather[0] = GetReg32(system, 3);
824 timeout_ns_gather[1] = GetReg32(system, 4);
825 timeout_ns = Convert<int64_t>(timeout_ns_gather);
1163 826
1164 // Validate the activity. 827 ret = WaitForAddress64From32(system, address, arb_type, value, timeout_ns);
1165 constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
1166 return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
1167 };
1168 R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
1169 828
1170 // Get the thread from its handle. 829 SetReg32(system, 0, Convert<uint32_t>(ret));
1171 KScopedAutoObject thread = 830}
1172 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); 831
1173 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); 832static void SvcWrap_SignalToAddress64From32(Core::System& system) {
833 Result ret{};
1174 834
1175 // Check that the activity is being set on a non-current thread for the current process. 835 uint32_t address{};
1176 R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle); 836 SignalType signal_type{};
1177 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy); 837 int32_t value{};
838 int32_t count{};
1178 839
1179 // Set the activity. 840 address = Convert<uint32_t>(GetReg32(system, 0));
1180 R_TRY(thread->SetActivity(thread_activity)); 841 signal_type = Convert<SignalType>(GetReg32(system, 1));
842 value = Convert<int32_t>(GetReg32(system, 2));
843 count = Convert<int32_t>(GetReg32(system, 3));
1181 844
1182 return ResultSuccess; 845 ret = SignalToAddress64From32(system, address, signal_type, value, count);
846
847 SetReg32(system, 0, Convert<uint32_t>(ret));
1183} 848}
1184 849
1185static Result SetThreadActivity32(Core::System& system, Handle thread_handle, 850static void SvcWrap_SynchronizePreemptionState64From32(Core::System& system) {
1186 Svc::ThreadActivity thread_activity) { 851 SynchronizePreemptionState64From32(system);
1187 return SetThreadActivity(system, thread_handle, thread_activity);
1188} 852}
1189 853
1190/// Gets the thread context 854static void SvcWrap_GetResourceLimitPeakValue64From32(Core::System& system) {
1191static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { 855 Result ret{};
1192 LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
1193 thread_handle);
1194 856
1195 auto& kernel = system.Kernel(); 857 int64_t out_peak_value{};
858 Handle resource_limit_handle{};
859 LimitableResource which{};
1196 860
1197 // Get the thread from its handle. 861 resource_limit_handle = Convert<Handle>(GetReg32(system, 1));
1198 KScopedAutoObject thread = 862 which = Convert<LimitableResource>(GetReg32(system, 2));
1199 kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
1200 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1201 863
1202 // Require the handle be to a non-current thread in the current process. 864 ret = GetResourceLimitPeakValue64From32(system, &out_peak_value, resource_limit_handle, which);
1203 const auto* current_process = kernel.CurrentProcess();
1204 R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
1205 865
1206 // Verify that the thread isn't terminated. 866 SetReg32(system, 0, Convert<uint32_t>(ret));
1207 R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested); 867 auto out_peak_value_scatter = Convert<std::array<uint32_t, 2>>(out_peak_value);
868 SetReg32(system, 1, out_peak_value_scatter[0]);
869 SetReg32(system, 2, out_peak_value_scatter[1]);
870}
871
872static void SvcWrap_CreateIoPool64From32(Core::System& system) {
873 Result ret{};
1208 874
1209 /// Check that the thread is not the current one. 875 Handle out_handle{};
1210 /// NOTE: Nintendo does not check this, and thus the following loop will deadlock. 876 IoPoolType which{};
1211 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
1212 877
1213 // Try to get the thread context until the thread isn't current on any core. 878 which = Convert<IoPoolType>(GetReg32(system, 1));
1214 while (true) {
1215 KScopedSchedulerLock sl{kernel};
1216 879
1217 // TODO(bunnei): Enforce that thread is suspended for debug here. 880 ret = CreateIoPool64From32(system, &out_handle, which);
1218 881
1219 // If the thread's raw state isn't runnable, check if it's current on some core. 882 SetReg32(system, 0, Convert<uint32_t>(ret));
1220 if (thread->GetRawState() != ThreadState::Runnable) { 883 SetReg32(system, 1, Convert<uint32_t>(out_handle));
1221 bool current = false; 884}
1222 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1223 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
1224 current = true;
1225 break;
1226 }
1227 }
1228 885
1229 // If the thread is current, retry until it isn't. 886static void SvcWrap_CreateIoRegion64From32(Core::System& system) {
1230 if (current) { 887 Result ret{};
1231 continue;
1232 }
1233 }
1234 888
1235 // Get the thread context. 889 Handle out_handle{};
1236 std::vector<u8> context; 890 Handle io_pool{};
1237 R_TRY(thread->GetThreadContext3(context)); 891 uint64_t physical_address{};
892 uint32_t size{};
893 MemoryMapping mapping{};
894 MemoryPermission perm{};
1238 895
1239 // Copy the thread context to user space. 896 io_pool = Convert<Handle>(GetReg32(system, 1));
1240 system.Memory().WriteBlock(out_context, context.data(), context.size()); 897 std::array<uint32_t, 2> physical_address_gather{};
898 physical_address_gather[0] = GetReg32(system, 2);
899 physical_address_gather[1] = GetReg32(system, 3);
900 physical_address = Convert<uint64_t>(physical_address_gather);
901 size = Convert<uint32_t>(GetReg32(system, 0));
902 mapping = Convert<MemoryMapping>(GetReg32(system, 4));
903 perm = Convert<MemoryPermission>(GetReg32(system, 5));
1241 904
1242 return ResultSuccess; 905 ret = CreateIoRegion64From32(system, &out_handle, io_pool, physical_address, size, mapping, perm);
1243 }
1244 906
1245 return ResultSuccess; 907 SetReg32(system, 0, Convert<uint32_t>(ret));
908 SetReg32(system, 1, Convert<uint32_t>(out_handle));
1246} 909}
1247 910
1248static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { 911static void SvcWrap_KernelDebug64From32(Core::System& system) {
1249 return GetThreadContext(system, out_context, thread_handle); 912 KernelDebugType kern_debug_type{};
913 uint64_t arg0{};
914 uint64_t arg1{};
915 uint64_t arg2{};
916
917 kern_debug_type = Convert<KernelDebugType>(GetReg32(system, 0));
918 std::array<uint32_t, 2> arg0_gather{};
919 arg0_gather[0] = GetReg32(system, 2);
920 arg0_gather[1] = GetReg32(system, 3);
921 arg0 = Convert<uint64_t>(arg0_gather);
922 std::array<uint32_t, 2> arg1_gather{};
923 arg1_gather[0] = GetReg32(system, 1);
924 arg1_gather[1] = GetReg32(system, 4);
925 arg1 = Convert<uint64_t>(arg1_gather);
926 std::array<uint32_t, 2> arg2_gather{};
927 arg2_gather[0] = GetReg32(system, 5);
928 arg2_gather[1] = GetReg32(system, 6);
929 arg2 = Convert<uint64_t>(arg2_gather);
930
931 KernelDebug64From32(system, kern_debug_type, arg0, arg1, arg2);
1250} 932}
1251 933
1252/// Gets the priority for the specified thread 934static void SvcWrap_ChangeKernelTraceState64From32(Core::System& system) {
1253static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { 935 KernelTraceState kern_trace_state{};
1254 LOG_TRACE(Kernel_SVC, "called");
1255 936
1256 // Get the thread from its handle. 937 kern_trace_state = Convert<KernelTraceState>(GetReg32(system, 0));
1257 KScopedAutoObject thread =
1258 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
1259 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1260 938
1261 // Get the thread's priority. 939 ChangeKernelTraceState64From32(system, kern_trace_state);
1262 *out_priority = thread->GetPriority();
1263 return ResultSuccess;
1264} 940}
1265 941
1266static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { 942static void SvcWrap_CreateSession64From32(Core::System& system) {
1267 return GetThreadPriority(system, out_priority, handle); 943 Result ret{};
944
945 Handle out_server_session_handle{};
946 Handle out_client_session_handle{};
947 bool is_light{};
948 uint32_t name{};
949
950 is_light = Convert<bool>(GetReg32(system, 2));
951 name = Convert<uint32_t>(GetReg32(system, 3));
952
953 ret = CreateSession64From32(system, &out_server_session_handle, &out_client_session_handle, is_light, name);
954
955 SetReg32(system, 0, Convert<uint32_t>(ret));
956 SetReg32(system, 1, Convert<uint32_t>(out_server_session_handle));
957 SetReg32(system, 2, Convert<uint32_t>(out_client_session_handle));
1268} 958}
1269 959
1270/// Sets the priority for the specified thread 960static void SvcWrap_AcceptSession64From32(Core::System& system) {
1271static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) { 961 Result ret{};
1272 // Get the current process. 962
1273 KProcess& process = *system.Kernel().CurrentProcess(); 963 Handle out_handle{};
964 Handle port{};
1274 965
1275 // Validate the priority. 966 port = Convert<Handle>(GetReg32(system, 1));
1276 R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
1277 ResultInvalidPriority);
1278 R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
1279 967
1280 // Get the thread from its handle. 968 ret = AcceptSession64From32(system, &out_handle, port);
1281 KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
1282 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
1283 969
1284 // Set the thread priority. 970 SetReg32(system, 0, Convert<uint32_t>(ret));
1285 thread->SetBasePriority(priority); 971 SetReg32(system, 1, Convert<uint32_t>(out_handle));
1286 return ResultSuccess;
1287} 972}
1288 973
1289static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) { 974static void SvcWrap_ReplyAndReceive64From32(Core::System& system) {
1290 return SetThreadPriority(system, thread_handle, priority); 975 Result ret{};
976
977 int32_t out_index{};
978 uint32_t handles{};
979 int32_t num_handles{};
980 Handle reply_target{};
981 int64_t timeout_ns{};
982
983 handles = Convert<uint32_t>(GetReg32(system, 1));
984 num_handles = Convert<int32_t>(GetReg32(system, 2));
985 reply_target = Convert<Handle>(GetReg32(system, 3));
986 std::array<uint32_t, 2> timeout_ns_gather{};
987 timeout_ns_gather[0] = GetReg32(system, 0);
988 timeout_ns_gather[1] = GetReg32(system, 4);
989 timeout_ns = Convert<int64_t>(timeout_ns_gather);
990
991 ret = ReplyAndReceive64From32(system, &out_index, handles, num_handles, reply_target, timeout_ns);
992
993 SetReg32(system, 0, Convert<uint32_t>(ret));
994 SetReg32(system, 1, Convert<uint32_t>(out_index));
1291} 995}
1292 996
1293/// Get which CPU core is executing the current thread 997static void SvcWrap_ReplyAndReceiveWithUserBuffer64From32(Core::System& system) {
1294static u32 GetCurrentProcessorNumber(Core::System& system) { 998 Result ret{};
1295 LOG_TRACE(Kernel_SVC, "called"); 999
1296 return static_cast<u32>(system.CurrentPhysicalCore().CoreIndex()); 1000 int32_t out_index{};
1001 uint32_t message_buffer{};
1002 uint32_t message_buffer_size{};
1003 uint32_t handles{};
1004 int32_t num_handles{};
1005 Handle reply_target{};
1006 int64_t timeout_ns{};
1007
1008 message_buffer = Convert<uint32_t>(GetReg32(system, 1));
1009 message_buffer_size = Convert<uint32_t>(GetReg32(system, 2));
1010 handles = Convert<uint32_t>(GetReg32(system, 3));
1011 num_handles = Convert<int32_t>(GetReg32(system, 0));
1012 reply_target = Convert<Handle>(GetReg32(system, 4));
1013 std::array<uint32_t, 2> timeout_ns_gather{};
1014 timeout_ns_gather[0] = GetReg32(system, 5);
1015 timeout_ns_gather[1] = GetReg32(system, 6);
1016 timeout_ns = Convert<int64_t>(timeout_ns_gather);
1017
1018 ret = ReplyAndReceiveWithUserBuffer64From32(system, &out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns);
1019
1020 SetReg32(system, 0, Convert<uint32_t>(ret));
1021 SetReg32(system, 1, Convert<uint32_t>(out_index));
1297} 1022}
1298 1023
1299static u32 GetCurrentProcessorNumber32(Core::System& system) { 1024static void SvcWrap_CreateEvent64From32(Core::System& system) {
1300 return GetCurrentProcessorNumber(system); 1025 Result ret{};
1026
1027 Handle out_write_handle{};
1028 Handle out_read_handle{};
1029
1030 ret = CreateEvent64From32(system, &out_write_handle, &out_read_handle);
1031
1032 SetReg32(system, 0, Convert<uint32_t>(ret));
1033 SetReg32(system, 1, Convert<uint32_t>(out_write_handle));
1034 SetReg32(system, 2, Convert<uint32_t>(out_read_handle));
1301} 1035}
1302 1036
1303namespace { 1037static void SvcWrap_MapIoRegion64From32(Core::System& system) {
1038 Result ret{};
1304 1039
1305constexpr bool IsValidSharedMemoryPermission(Svc::MemoryPermission perm) { 1040 Handle io_region{};
1306 switch (perm) { 1041 uint32_t address{};
1307 case Svc::MemoryPermission::Read: 1042 uint32_t size{};
1308 case Svc::MemoryPermission::ReadWrite: 1043 MemoryPermission perm{};
1309 return true; 1044
1310 default: 1045 io_region = Convert<Handle>(GetReg32(system, 0));
1311 return false; 1046 address = Convert<uint32_t>(GetReg32(system, 1));
1312 } 1047 size = Convert<uint32_t>(GetReg32(system, 2));
1048 perm = Convert<MemoryPermission>(GetReg32(system, 3));
1049
1050 ret = MapIoRegion64From32(system, io_region, address, size, perm);
1051
1052 SetReg32(system, 0, Convert<uint32_t>(ret));
1313} 1053}
1314 1054
1315[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(Svc::MemoryPermission perm) { 1055static void SvcWrap_UnmapIoRegion64From32(Core::System& system) {
1316 return IsValidSharedMemoryPermission(perm) || perm == Svc::MemoryPermission::DontCare; 1056 Result ret{};
1057
1058 Handle io_region{};
1059 uint32_t address{};
1060 uint32_t size{};
1061
1062 io_region = Convert<Handle>(GetReg32(system, 0));
1063 address = Convert<uint32_t>(GetReg32(system, 1));
1064 size = Convert<uint32_t>(GetReg32(system, 2));
1065
1066 ret = UnmapIoRegion64From32(system, io_region, address, size);
1067
1068 SetReg32(system, 0, Convert<uint32_t>(ret));
1317} 1069}
1318 1070
1319constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) { 1071static void SvcWrap_MapPhysicalMemoryUnsafe64From32(Core::System& system) {
1320 switch (perm) { 1072 Result ret{};
1321 case Svc::MemoryPermission::None: 1073
1322 case Svc::MemoryPermission::Read: 1074 uint32_t address{};
1323 case Svc::MemoryPermission::ReadWrite: 1075 uint32_t size{};
1324 case Svc::MemoryPermission::ReadExecute: 1076
1325 return true; 1077 address = Convert<uint32_t>(GetReg32(system, 0));
1326 default: 1078 size = Convert<uint32_t>(GetReg32(system, 1));
1327 return false; 1079
1328 } 1080 ret = MapPhysicalMemoryUnsafe64From32(system, address, size);
1081
1082 SetReg32(system, 0, Convert<uint32_t>(ret));
1329} 1083}
1330 1084
1331constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) { 1085static void SvcWrap_UnmapPhysicalMemoryUnsafe64From32(Core::System& system) {
1332 return perm == Svc::MemoryPermission::ReadWrite; 1086 Result ret{};
1087
1088 uint32_t address{};
1089 uint32_t size{};
1090
1091 address = Convert<uint32_t>(GetReg32(system, 0));
1092 size = Convert<uint32_t>(GetReg32(system, 1));
1093
1094 ret = UnmapPhysicalMemoryUnsafe64From32(system, address, size);
1095
1096 SetReg32(system, 0, Convert<uint32_t>(ret));
1333} 1097}
1334 1098
1335constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { 1099static void SvcWrap_SetUnsafeLimit64From32(Core::System& system) {
1336 return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute; 1100 Result ret{};
1101
1102 uint32_t limit{};
1103
1104 limit = Convert<uint32_t>(GetReg32(system, 0));
1105
1106 ret = SetUnsafeLimit64From32(system, limit);
1107
1108 SetReg32(system, 0, Convert<uint32_t>(ret));
1337} 1109}
1338 1110
1339constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) { 1111static void SvcWrap_CreateCodeMemory64From32(Core::System& system) {
1340 return perm == Svc::MemoryPermission::None; 1112 Result ret{};
1113
1114 Handle out_handle{};
1115 uint32_t address{};
1116 uint32_t size{};
1117
1118 address = Convert<uint32_t>(GetReg32(system, 1));
1119 size = Convert<uint32_t>(GetReg32(system, 2));
1120
1121 ret = CreateCodeMemory64From32(system, &out_handle, address, size);
1122
1123 SetReg32(system, 0, Convert<uint32_t>(ret));
1124 SetReg32(system, 1, Convert<uint32_t>(out_handle));
1341} 1125}
1342 1126
1343constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) { 1127static void SvcWrap_ControlCodeMemory64From32(Core::System& system) {
1344 return perm == Svc::MemoryPermission::None; 1128 Result ret{};
1129
1130 Handle code_memory_handle{};
1131 CodeMemoryOperation operation{};
1132 uint64_t address{};
1133 uint64_t size{};
1134 MemoryPermission perm{};
1135
1136 code_memory_handle = Convert<Handle>(GetReg32(system, 0));
1137 operation = Convert<CodeMemoryOperation>(GetReg32(system, 1));
1138 std::array<uint32_t, 2> address_gather{};
1139 address_gather[0] = GetReg32(system, 2);
1140 address_gather[1] = GetReg32(system, 3);
1141 address = Convert<uint64_t>(address_gather);
1142 std::array<uint32_t, 2> size_gather{};
1143 size_gather[0] = GetReg32(system, 4);
1144 size_gather[1] = GetReg32(system, 5);
1145 size = Convert<uint64_t>(size_gather);
1146 perm = Convert<MemoryPermission>(GetReg32(system, 6));
1147
1148 ret = ControlCodeMemory64From32(system, code_memory_handle, operation, address, size, perm);
1149
1150 SetReg32(system, 0, Convert<uint32_t>(ret));
1345} 1151}
1346 1152
1347} // Anonymous namespace 1153static void SvcWrap_SleepSystem64From32(Core::System& system) {
1154 SleepSystem64From32(system);
1155}
1348 1156
1349static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size, 1157static void SvcWrap_ReadWriteRegister64From32(Core::System& system) {
1350 Svc::MemoryPermission map_perm) { 1158 Result ret{};
1351 LOG_TRACE(Kernel_SVC,
1352 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1353 shmem_handle, address, size, map_perm);
1354 1159
1355 // Validate the address/size. 1160 uint32_t out_value{};
1356 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); 1161 uint64_t address{};
1357 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); 1162 uint32_t mask{};
1358 R_UNLESS(size > 0, ResultInvalidSize); 1163 uint32_t value{};
1359 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1360 1164
1361 // Validate the permission. 1165 std::array<uint32_t, 2> address_gather{};
1362 R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission); 1166 address_gather[0] = GetReg32(system, 2);
1167 address_gather[1] = GetReg32(system, 3);
1168 address = Convert<uint64_t>(address_gather);
1169 mask = Convert<uint32_t>(GetReg32(system, 0));
1170 value = Convert<uint32_t>(GetReg32(system, 1));
1363 1171
1364 // Get the current process. 1172 ret = ReadWriteRegister64From32(system, &out_value, address, mask, value);
1365 auto& process = *system.Kernel().CurrentProcess();
1366 auto& page_table = process.PageTable();
1367 1173
1368 // Get the shared memory. 1174 SetReg32(system, 0, Convert<uint32_t>(ret));
1369 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); 1175 SetReg32(system, 1, Convert<uint32_t>(out_value));
1370 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle); 1176}
1371 1177
1372 // Verify that the mapping is in range. 1178static void SvcWrap_SetProcessActivity64From32(Core::System& system) {
1373 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); 1179 Result ret{};
1374 1180
1375 // Add the shared memory to the process. 1181 Handle process_handle{};
1376 R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size)); 1182 ProcessActivity process_activity{};
1377 1183
1378 // Ensure that we clean up the shared memory if we fail to map it. 1184 process_handle = Convert<Handle>(GetReg32(system, 0));
1379 auto guard = 1185 process_activity = Convert<ProcessActivity>(GetReg32(system, 1));
1380 SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
1381 1186
1382 // Map the shared memory. 1187 ret = SetProcessActivity64From32(system, process_handle, process_activity);
1383 R_TRY(shmem->Map(process, address, size, map_perm));
1384 1188
1385 // We succeeded. 1189 SetReg32(system, 0, Convert<uint32_t>(ret));
1386 guard.Cancel();
1387 return ResultSuccess;
1388} 1190}
1389 1191
1390static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size, 1192static void SvcWrap_CreateSharedMemory64From32(Core::System& system) {
1391 Svc::MemoryPermission map_perm) { 1193 Result ret{};
1392 return MapSharedMemory(system, shmem_handle, address, size, map_perm); 1194
1195 Handle out_handle{};
1196 uint32_t size{};
1197 MemoryPermission owner_perm{};
1198 MemoryPermission remote_perm{};
1199
1200 size = Convert<uint32_t>(GetReg32(system, 1));
1201 owner_perm = Convert<MemoryPermission>(GetReg32(system, 2));
1202 remote_perm = Convert<MemoryPermission>(GetReg32(system, 3));
1203
1204 ret = CreateSharedMemory64From32(system, &out_handle, size, owner_perm, remote_perm);
1205
1206 SetReg32(system, 0, Convert<uint32_t>(ret));
1207 SetReg32(system, 1, Convert<uint32_t>(out_handle));
1393} 1208}
1394 1209
1395static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, 1210static void SvcWrap_MapTransferMemory64From32(Core::System& system) {
1396 u64 size) { 1211 Result ret{};
1397 // Validate the address/size. 1212
1398 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); 1213 Handle trmem_handle{};
1399 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); 1214 uint32_t address{};
1400 R_UNLESS(size > 0, ResultInvalidSize); 1215 uint32_t size{};
1401 R_UNLESS((address < address + size), ResultInvalidCurrentMemory); 1216 MemoryPermission owner_perm{};
1402 1217
1403 // Get the current process. 1218 trmem_handle = Convert<Handle>(GetReg32(system, 0));
1404 auto& process = *system.Kernel().CurrentProcess(); 1219 address = Convert<uint32_t>(GetReg32(system, 1));
1405 auto& page_table = process.PageTable(); 1220 size = Convert<uint32_t>(GetReg32(system, 2));
1221 owner_perm = Convert<MemoryPermission>(GetReg32(system, 3));
1406 1222
1407 // Get the shared memory. 1223 ret = MapTransferMemory64From32(system, trmem_handle, address, size, owner_perm);
1408 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
1409 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
1410 1224
1411 // Verify that the mapping is in range. 1225 SetReg32(system, 0, Convert<uint32_t>(ret));
1412 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion); 1226}
1227
1228static void SvcWrap_UnmapTransferMemory64From32(Core::System& system) {
1229 Result ret{};
1413 1230
1414 // Unmap the shared memory. 1231 Handle trmem_handle{};
1415 R_TRY(shmem->Unmap(process, address, size)); 1232 uint32_t address{};
1233 uint32_t size{};
1416 1234
1417 // Remove the shared memory from the process. 1235 trmem_handle = Convert<Handle>(GetReg32(system, 0));
1418 process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); 1236 address = Convert<uint32_t>(GetReg32(system, 1));
1237 size = Convert<uint32_t>(GetReg32(system, 2));
1419 1238
1420 return ResultSuccess; 1239 ret = UnmapTransferMemory64From32(system, trmem_handle, address, size);
1240
1241 SetReg32(system, 0, Convert<uint32_t>(ret));
1421} 1242}
1422 1243
1423static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, 1244static void SvcWrap_CreateInterruptEvent64From32(Core::System& system) {
1424 u32 size) { 1245 Result ret{};
1425 return UnmapSharedMemory(system, shmem_handle, address, size); 1246
1247 Handle out_read_handle{};
1248 int32_t interrupt_id{};
1249 InterruptType interrupt_type{};
1250
1251 interrupt_id = Convert<int32_t>(GetReg32(system, 1));
1252 interrupt_type = Convert<InterruptType>(GetReg32(system, 2));
1253
1254 ret = CreateInterruptEvent64From32(system, &out_read_handle, interrupt_id, interrupt_type);
1255
1256 SetReg32(system, 0, Convert<uint32_t>(ret));
1257 SetReg32(system, 1, Convert<uint32_t>(out_read_handle));
1426} 1258}
1427 1259
1428static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address, 1260static void SvcWrap_QueryPhysicalAddress64From32(Core::System& system) {
1429 u64 size, Svc::MemoryPermission perm) { 1261 Result ret{};
1430 LOG_TRACE(Kernel_SVC,
1431 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
1432 process_handle, address, size, perm);
1433 1262
1434 // Validate the address/size. 1263 ilp32::PhysicalMemoryInfo out_info{};
1435 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); 1264 uint32_t address{};
1436 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1437 R_UNLESS(size > 0, ResultInvalidSize);
1438 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1439 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
1440 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
1441 1265
1442 // Validate the memory permission. 1266 address = Convert<uint32_t>(GetReg32(system, 1));
1443 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1444 1267
1445 // Get the process from its handle. 1268 ret = QueryPhysicalAddress64From32(system, &out_info, address);
1446 KScopedAutoObject process =
1447 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
1448 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
1449 1269
1450 // Validate that the address is in range. 1270 SetReg32(system, 0, Convert<uint32_t>(ret));
1451 auto& page_table = process->PageTable(); 1271 auto out_info_scatter = Convert<std::array<uint32_t, 4>>(out_info);
1452 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 1272 SetReg32(system, 1, out_info_scatter[0]);
1273 SetReg32(system, 2, out_info_scatter[1]);
1274 SetReg32(system, 3, out_info_scatter[2]);
1275 SetReg32(system, 4, out_info_scatter[3]);
1276}
1453 1277
1454 // Set the memory permission. 1278static void SvcWrap_QueryIoMapping64From32(Core::System& system) {
1455 return page_table.SetProcessMemoryPermission(address, size, perm); 1279 Result ret{};
1280
1281 uintptr_t out_address{};
1282 uintptr_t out_size{};
1283 uint64_t physical_address{};
1284 uint32_t size{};
1285
1286 std::array<uint32_t, 2> physical_address_gather{};
1287 physical_address_gather[0] = GetReg32(system, 2);
1288 physical_address_gather[1] = GetReg32(system, 3);
1289 physical_address = Convert<uint64_t>(physical_address_gather);
1290 size = Convert<uint32_t>(GetReg32(system, 0));
1291
1292 ret = QueryIoMapping64From32(system, &out_address, &out_size, physical_address, size);
1293
1294 SetReg32(system, 0, Convert<uint32_t>(ret));
1295 SetReg32(system, 1, Convert<uint32_t>(out_address));
1296 SetReg32(system, 2, Convert<uint32_t>(out_size));
1456} 1297}
1457 1298
1458static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, 1299static void SvcWrap_CreateDeviceAddressSpace64From32(Core::System& system) {
1459 VAddr src_address, u64 size) { 1300 Result ret{};
1460 LOG_TRACE(Kernel_SVC,
1461 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1462 dst_address, process_handle, src_address, size);
1463 1301
1464 // Validate the address/size. 1302 Handle out_handle{};
1465 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); 1303 uint64_t das_address{};
1466 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress); 1304 uint64_t das_size{};
1467 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1468 R_UNLESS(size > 0, ResultInvalidSize);
1469 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1470 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1471 1305
1472 // Get the processes. 1306 std::array<uint32_t, 2> das_address_gather{};
1473 KProcess* dst_process = system.CurrentProcess(); 1307 das_address_gather[0] = GetReg32(system, 2);
1474 KScopedAutoObject src_process = 1308 das_address_gather[1] = GetReg32(system, 3);
1475 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle); 1309 das_address = Convert<uint64_t>(das_address_gather);
1476 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); 1310 std::array<uint32_t, 2> das_size_gather{};
1311 das_size_gather[0] = GetReg32(system, 0);
1312 das_size_gather[1] = GetReg32(system, 1);
1313 das_size = Convert<uint64_t>(das_size_gather);
1477 1314
1478 // Get the page tables. 1315 ret = CreateDeviceAddressSpace64From32(system, &out_handle, das_address, das_size);
1479 auto& dst_pt = dst_process->PageTable();
1480 auto& src_pt = src_process->PageTable();
1481 1316
1482 // Validate that the mapping is in range. 1317 SetReg32(system, 0, Convert<uint32_t>(ret));
1483 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); 1318 SetReg32(system, 1, Convert<uint32_t>(out_handle));
1484 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode), 1319}
1485 ResultInvalidMemoryRegion); 1320
1321static void SvcWrap_AttachDeviceAddressSpace64From32(Core::System& system) {
1322 Result ret{};
1486 1323
1487 // Create a new page group. 1324 DeviceName device_name{};
1488 KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()}; 1325 Handle das_handle{};
1489 R_TRY(src_pt.MakeAndOpenPageGroup(
1490 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1491 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1492 KMemoryAttribute::All, KMemoryAttribute::None));
1493 1326
1494 // Map the group. 1327 device_name = Convert<DeviceName>(GetReg32(system, 0));
1495 R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode, 1328 das_handle = Convert<Handle>(GetReg32(system, 1));
1496 KMemoryPermission::UserReadWrite));
1497 1329
1498 return ResultSuccess; 1330 ret = AttachDeviceAddressSpace64From32(system, device_name, das_handle);
1331
1332 SetReg32(system, 0, Convert<uint32_t>(ret));
1499} 1333}
1500 1334
1501static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle, 1335static void SvcWrap_DetachDeviceAddressSpace64From32(Core::System& system) {
1502 VAddr src_address, u64 size) { 1336 Result ret{};
1503 LOG_TRACE(Kernel_SVC,
1504 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
1505 dst_address, process_handle, src_address, size);
1506 1337
1507 // Validate the address/size. 1338 DeviceName device_name{};
1508 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress); 1339 Handle das_handle{};
1509 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
1510 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1511 R_UNLESS(size > 0, ResultInvalidSize);
1512 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
1513 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
1514 1340
1515 // Get the processes. 1341 device_name = Convert<DeviceName>(GetReg32(system, 0));
1516 KProcess* dst_process = system.CurrentProcess(); 1342 das_handle = Convert<Handle>(GetReg32(system, 1));
1517 KScopedAutoObject src_process =
1518 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
1519 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
1520
1521 // Get the page tables.
1522 auto& dst_pt = dst_process->PageTable();
1523 auto& src_pt = src_process->PageTable();
1524
1525 // Validate that the mapping is in range.
1526 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
1527 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
1528 ResultInvalidMemoryRegion);
1529
1530 // Unmap the memory.
1531 R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
1532
1533 return ResultSuccess;
1534}
1535
1536static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1537 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
1538
1539 // Get kernel instance.
1540 auto& kernel = system.Kernel();
1541 1343
1542 // Validate address / size. 1344 ret = DetachDeviceAddressSpace64From32(system, device_name, das_handle);
1543 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
1544 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
1545 R_UNLESS(size > 0, ResultInvalidSize);
1546 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
1547 1345
1548 // Create the code memory. 1346 SetReg32(system, 0, Convert<uint32_t>(ret));
1347}
1348
1349static void SvcWrap_MapDeviceAddressSpaceByForce64From32(Core::System& system) {
1350 Result ret{};
1549 1351
1550 KCodeMemory* code_mem = KCodeMemory::Create(kernel); 1352 Handle das_handle{};
1551 R_UNLESS(code_mem != nullptr, ResultOutOfResource); 1353 Handle process_handle{};
1354 uint64_t process_address{};
1355 uint32_t size{};
1356 uint64_t device_address{};
1357 uint32_t option{};
1552 1358
1553 // Verify that the region is in range. 1359 das_handle = Convert<Handle>(GetReg32(system, 0));
1554 R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size), 1360 process_handle = Convert<Handle>(GetReg32(system, 1));
1555 ResultInvalidCurrentMemory); 1361 std::array<uint32_t, 2> process_address_gather{};
1362 process_address_gather[0] = GetReg32(system, 2);
1363 process_address_gather[1] = GetReg32(system, 3);
1364 process_address = Convert<uint64_t>(process_address_gather);
1365 size = Convert<uint32_t>(GetReg32(system, 4));
1366 std::array<uint32_t, 2> device_address_gather{};
1367 device_address_gather[0] = GetReg32(system, 5);
1368 device_address_gather[1] = GetReg32(system, 6);
1369 device_address = Convert<uint64_t>(device_address_gather);
1370 option = Convert<uint32_t>(GetReg32(system, 7));
1556 1371
1557 // Initialize the code memory. 1372 ret = MapDeviceAddressSpaceByForce64From32(system, das_handle, process_handle, process_address, size, device_address, option);
1558 R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size)); 1373
1374 SetReg32(system, 0, Convert<uint32_t>(ret));
1375}
1559 1376
1560 // Register the code memory. 1377static void SvcWrap_MapDeviceAddressSpaceAligned64From32(Core::System& system) {
1561 KCodeMemory::Register(kernel, code_mem); 1378 Result ret{};
1562 1379
1563 // Add the code memory to the handle table. 1380 Handle das_handle{};
1564 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem)); 1381 Handle process_handle{};
1382 uint64_t process_address{};
1383 uint32_t size{};
1384 uint64_t device_address{};
1385 uint32_t option{};
1565 1386
1566 code_mem->Close(); 1387 das_handle = Convert<Handle>(GetReg32(system, 0));
1388 process_handle = Convert<Handle>(GetReg32(system, 1));
1389 std::array<uint32_t, 2> process_address_gather{};
1390 process_address_gather[0] = GetReg32(system, 2);
1391 process_address_gather[1] = GetReg32(system, 3);
1392 process_address = Convert<uint64_t>(process_address_gather);
1393 size = Convert<uint32_t>(GetReg32(system, 4));
1394 std::array<uint32_t, 2> device_address_gather{};
1395 device_address_gather[0] = GetReg32(system, 5);
1396 device_address_gather[1] = GetReg32(system, 6);
1397 device_address = Convert<uint64_t>(device_address_gather);
1398 option = Convert<uint32_t>(GetReg32(system, 7));
1567 1399
1568 return ResultSuccess; 1400 ret = MapDeviceAddressSpaceAligned64From32(system, das_handle, process_handle, process_address, size, device_address, option);
1401
1402 SetReg32(system, 0, Convert<uint32_t>(ret));
1569} 1403}
1570 1404
1571static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) { 1405static void SvcWrap_UnmapDeviceAddressSpace64From32(Core::System& system) {
1572 return CreateCodeMemory(system, out, address, size); 1406 Result ret{};
1407
1408 Handle das_handle{};
1409 Handle process_handle{};
1410 uint64_t process_address{};
1411 uint32_t size{};
1412 uint64_t device_address{};
1413
1414 das_handle = Convert<Handle>(GetReg32(system, 0));
1415 process_handle = Convert<Handle>(GetReg32(system, 1));
1416 std::array<uint32_t, 2> process_address_gather{};
1417 process_address_gather[0] = GetReg32(system, 2);
1418 process_address_gather[1] = GetReg32(system, 3);
1419 process_address = Convert<uint64_t>(process_address_gather);
1420 size = Convert<uint32_t>(GetReg32(system, 4));
1421 std::array<uint32_t, 2> device_address_gather{};
1422 device_address_gather[0] = GetReg32(system, 5);
1423 device_address_gather[1] = GetReg32(system, 6);
1424 device_address = Convert<uint64_t>(device_address_gather);
1425
1426 ret = UnmapDeviceAddressSpace64From32(system, das_handle, process_handle, process_address, size, device_address);
1427
1428 SetReg32(system, 0, Convert<uint32_t>(ret));
1573} 1429}
1574 1430
1575static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation, 1431static void SvcWrap_InvalidateProcessDataCache64From32(Core::System& system) {
1576 VAddr address, size_t size, Svc::MemoryPermission perm) { 1432 Result ret{};
1577 1433
1578 LOG_TRACE(Kernel_SVC, 1434 Handle process_handle{};
1579 "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, " 1435 uint64_t address{};
1580 "permission=0x{:X}", 1436 uint64_t size{};
1581 code_memory_handle, operation, address, size, perm);
1582 1437
1583 // Validate the address / size. 1438 process_handle = Convert<Handle>(GetReg32(system, 0));
1584 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); 1439 std::array<uint32_t, 2> address_gather{};
1585 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); 1440 address_gather[0] = GetReg32(system, 2);
1586 R_UNLESS(size > 0, ResultInvalidSize); 1441 address_gather[1] = GetReg32(system, 3);
1587 R_UNLESS((address < address + size), ResultInvalidCurrentMemory); 1442 address = Convert<uint64_t>(address_gather);
1443 std::array<uint32_t, 2> size_gather{};
1444 size_gather[0] = GetReg32(system, 1);
1445 size_gather[1] = GetReg32(system, 4);
1446 size = Convert<uint64_t>(size_gather);
1588 1447
1589 // Get the code memory from its handle. 1448 ret = InvalidateProcessDataCache64From32(system, process_handle, address, size);
1590 KScopedAutoObject code_mem = 1449
1591 system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle); 1450 SetReg32(system, 0, Convert<uint32_t>(ret));
1592 R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle); 1451}
1593 1452
1594 // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process. 1453static void SvcWrap_StoreProcessDataCache64From32(Core::System& system) {
1595 // This enables homebrew usage of these SVCs for JIT. 1454 Result ret{};
1596 1455
1597 // Perform the operation. 1456 Handle process_handle{};
1598 switch (static_cast<CodeMemoryOperation>(operation)) { 1457 uint64_t address{};
1599 case CodeMemoryOperation::Map: { 1458 uint64_t size{};
1600 // Check that the region is in range.
1601 R_UNLESS(
1602 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1603 ResultInvalidMemoryRegion);
1604 1459
1605 // Check the memory permission. 1460 process_handle = Convert<Handle>(GetReg32(system, 0));
1606 R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); 1461 std::array<uint32_t, 2> address_gather{};
1462 address_gather[0] = GetReg32(system, 2);
1463 address_gather[1] = GetReg32(system, 3);
1464 address = Convert<uint64_t>(address_gather);
1465 std::array<uint32_t, 2> size_gather{};
1466 size_gather[0] = GetReg32(system, 1);
1467 size_gather[1] = GetReg32(system, 4);
1468 size = Convert<uint64_t>(size_gather);
1607 1469
1608 // Map the memory. 1470 ret = StoreProcessDataCache64From32(system, process_handle, address, size);
1609 R_TRY(code_mem->Map(address, size));
1610 } break;
1611 case CodeMemoryOperation::Unmap: {
1612 // Check that the region is in range.
1613 R_UNLESS(
1614 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
1615 ResultInvalidMemoryRegion);
1616 1471
1617 // Check the memory permission. 1472 SetReg32(system, 0, Convert<uint32_t>(ret));
1618 R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); 1473}
1619 1474
1620 // Unmap the memory. 1475static void SvcWrap_FlushProcessDataCache64From32(Core::System& system) {
1621 R_TRY(code_mem->Unmap(address, size)); 1476 Result ret{};
1622 } break;
1623 case CodeMemoryOperation::MapToOwner: {
1624 // Check that the region is in range.
1625 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
1626 KMemoryState::GeneratedCode),
1627 ResultInvalidMemoryRegion);
1628 1477
1629 // Check the memory permission. 1478 Handle process_handle{};
1630 R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission); 1479 uint64_t address{};
1480 uint64_t size{};
1631 1481
1632 // Map the memory to its owner. 1482 process_handle = Convert<Handle>(GetReg32(system, 0));
1633 R_TRY(code_mem->MapToOwner(address, size, perm)); 1483 std::array<uint32_t, 2> address_gather{};
1634 } break; 1484 address_gather[0] = GetReg32(system, 2);
1635 case CodeMemoryOperation::UnmapFromOwner: { 1485 address_gather[1] = GetReg32(system, 3);
1636 // Check that the region is in range. 1486 address = Convert<uint64_t>(address_gather);
1637 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, 1487 std::array<uint32_t, 2> size_gather{};
1638 KMemoryState::GeneratedCode), 1488 size_gather[0] = GetReg32(system, 1);
1639 ResultInvalidMemoryRegion); 1489 size_gather[1] = GetReg32(system, 4);
1490 size = Convert<uint64_t>(size_gather);
1640 1491
1641 // Check the memory permission. 1492 ret = FlushProcessDataCache64From32(system, process_handle, address, size);
1642 R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
1643
1644 // Unmap the memory from its owner.
1645 R_TRY(code_mem->UnmapFromOwner(address, size));
1646 } break;
1647 default:
1648 return ResultInvalidEnumValue;
1649 }
1650 1493
1651 return ResultSuccess; 1494 SetReg32(system, 0, Convert<uint32_t>(ret));
1652} 1495}
1653 1496
1654static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation, 1497static void SvcWrap_DebugActiveProcess64From32(Core::System& system) {
1655 u64 address, u64 size, Svc::MemoryPermission perm) { 1498 Result ret{};
1656 return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm); 1499
1500 Handle out_handle{};
1501 uint64_t process_id{};
1502
1503 std::array<uint32_t, 2> process_id_gather{};
1504 process_id_gather[0] = GetReg32(system, 2);
1505 process_id_gather[1] = GetReg32(system, 3);
1506 process_id = Convert<uint64_t>(process_id_gather);
1507
1508 ret = DebugActiveProcess64From32(system, &out_handle, process_id);
1509
1510 SetReg32(system, 0, Convert<uint32_t>(ret));
1511 SetReg32(system, 1, Convert<uint32_t>(out_handle));
1657} 1512}
1658 1513
1659static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address, 1514static void SvcWrap_BreakDebugProcess64From32(Core::System& system) {
1660 VAddr page_info_address, Handle process_handle, VAddr address) { 1515 Result ret{};
1661 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
1662 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
1663 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
1664 if (process.IsNull()) {
1665 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
1666 process_handle);
1667 return ResultInvalidHandle;
1668 }
1669 1516
1670 auto& memory{system.Memory()}; 1517 Handle debug_handle{};
1671 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
1672 1518
1673 memory.Write64(memory_info_address + 0x00, memory_info.base_address); 1519 debug_handle = Convert<Handle>(GetReg32(system, 0));
1674 memory.Write64(memory_info_address + 0x08, memory_info.size);
1675 memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff);
1676 memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute));
1677 memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission));
1678 memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count);
1679 memory.Write32(memory_info_address + 0x20, memory_info.device_count);
1680 memory.Write32(memory_info_address + 0x24, 0);
1681 1520
1682 // Page info appears to be currently unused by the kernel and is always set to zero. 1521 ret = BreakDebugProcess64From32(system, debug_handle);
1683 memory.Write32(page_info_address, 0);
1684 1522
1685 return ResultSuccess; 1523 SetReg32(system, 0, Convert<uint32_t>(ret));
1686} 1524}
1687 1525
1688static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address, 1526static void SvcWrap_TerminateDebugProcess64From32(Core::System& system) {
1689 VAddr query_address) { 1527 Result ret{};
1690 LOG_TRACE(Kernel_SVC, 1528
1691 "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " 1529 Handle debug_handle{};
1692 "query_address=0x{:016X}", 1530
1693 memory_info_address, page_info_address, query_address); 1531 debug_handle = Convert<Handle>(GetReg32(system, 0));
1532
1533 ret = TerminateDebugProcess64From32(system, debug_handle);
1694 1534
1695 return QueryProcessMemory(system, memory_info_address, page_info_address, CurrentProcess, 1535 SetReg32(system, 0, Convert<uint32_t>(ret));
1696 query_address);
1697} 1536}
1698 1537
1699static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address, 1538static void SvcWrap_GetDebugEvent64From32(Core::System& system) {
1700 u32 query_address) { 1539 Result ret{};
1701 return QueryMemory(system, memory_info_address, page_info_address, query_address); 1540
1541 uint32_t out_info{};
1542 Handle debug_handle{};
1543
1544 out_info = Convert<uint32_t>(GetReg32(system, 0));
1545 debug_handle = Convert<Handle>(GetReg32(system, 1));
1546
1547 ret = GetDebugEvent64From32(system, out_info, debug_handle);
1548
1549 SetReg32(system, 0, Convert<uint32_t>(ret));
1702} 1550}
1703 1551
1704static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, 1552static void SvcWrap_ContinueDebugEvent64From32(Core::System& system) {
1705 u64 src_address, u64 size) { 1553 Result ret{};
1706 LOG_DEBUG(Kernel_SVC,
1707 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
1708 "src_address=0x{:016X}, size=0x{:016X}",
1709 process_handle, dst_address, src_address, size);
1710 1554
1711 if (!Common::Is4KBAligned(src_address)) { 1555 Handle debug_handle{};
1712 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", 1556 uint32_t flags{};
1713 src_address); 1557 uint32_t thread_ids{};
1714 return ResultInvalidAddress; 1558 int32_t num_thread_ids{};
1715 }
1716 1559
1717 if (!Common::Is4KBAligned(dst_address)) { 1560 debug_handle = Convert<Handle>(GetReg32(system, 0));
1718 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", 1561 flags = Convert<uint32_t>(GetReg32(system, 1));
1719 dst_address); 1562 thread_ids = Convert<uint32_t>(GetReg32(system, 2));
1720 return ResultInvalidAddress; 1563 num_thread_ids = Convert<int32_t>(GetReg32(system, 3));
1721 }
1722 1564
1723 if (size == 0 || !Common::Is4KBAligned(size)) { 1565 ret = ContinueDebugEvent64From32(system, debug_handle, flags, thread_ids, num_thread_ids);
1724 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
1725 return ResultInvalidSize;
1726 }
1727 1566
1728 if (!IsValidAddressRange(dst_address, size)) { 1567 SetReg32(system, 0, Convert<uint32_t>(ret));
1729 LOG_ERROR(Kernel_SVC, 1568}
1730 "Destination address range overflows the address space (dst_address=0x{:016X}, "
1731 "size=0x{:016X}).",
1732 dst_address, size);
1733 return ResultInvalidCurrentMemory;
1734 }
1735 1569
1736 if (!IsValidAddressRange(src_address, size)) { 1570static void SvcWrap_GetProcessList64From32(Core::System& system) {
1737 LOG_ERROR(Kernel_SVC, 1571 Result ret{};
1738 "Source address range overflows the address space (src_address=0x{:016X}, "
1739 "size=0x{:016X}).",
1740 src_address, size);
1741 return ResultInvalidCurrentMemory;
1742 }
1743 1572
1744 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 1573 int32_t out_num_processes{};
1745 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); 1574 uint32_t out_process_ids{};
1746 if (process.IsNull()) { 1575 int32_t max_out_count{};
1747 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
1748 process_handle);
1749 return ResultInvalidHandle;
1750 }
1751 1576
1752 auto& page_table = process->PageTable(); 1577 out_process_ids = Convert<uint32_t>(GetReg32(system, 1));
1753 if (!page_table.IsInsideAddressSpace(src_address, size)) { 1578 max_out_count = Convert<int32_t>(GetReg32(system, 2));
1754 LOG_ERROR(Kernel_SVC,
1755 "Source address range is not within the address space (src_address=0x{:016X}, "
1756 "size=0x{:016X}).",
1757 src_address, size);
1758 return ResultInvalidCurrentMemory;
1759 }
1760 1579
1761 if (!page_table.IsInsideASLRRegion(dst_address, size)) { 1580 ret = GetProcessList64From32(system, &out_num_processes, out_process_ids, max_out_count);
1762 LOG_ERROR(Kernel_SVC,
1763 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1764 "size=0x{:016X}).",
1765 dst_address, size);
1766 return ResultInvalidMemoryRegion;
1767 }
1768 1581
1769 return page_table.MapCodeMemory(dst_address, src_address, size); 1582 SetReg32(system, 0, Convert<uint32_t>(ret));
1583 SetReg32(system, 1, Convert<uint32_t>(out_num_processes));
1770} 1584}
1771 1585
1772static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, 1586static void SvcWrap_GetThreadList64From32(Core::System& system) {
1773 u64 src_address, u64 size) { 1587 Result ret{};
1774 LOG_DEBUG(Kernel_SVC,
1775 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
1776 "size=0x{:016X}",
1777 process_handle, dst_address, src_address, size);
1778 1588
1779 if (!Common::Is4KBAligned(dst_address)) { 1589 int32_t out_num_threads{};
1780 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", 1590 uint32_t out_thread_ids{};
1781 dst_address); 1591 int32_t max_out_count{};
1782 return ResultInvalidAddress; 1592 Handle debug_handle{};
1783 }
1784 1593
1785 if (!Common::Is4KBAligned(src_address)) { 1594 out_thread_ids = Convert<uint32_t>(GetReg32(system, 1));
1786 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", 1595 max_out_count = Convert<int32_t>(GetReg32(system, 2));
1787 src_address); 1596 debug_handle = Convert<Handle>(GetReg32(system, 3));
1788 return ResultInvalidAddress;
1789 }
1790 1597
1791 if (size == 0 || !Common::Is4KBAligned(size)) { 1598 ret = GetThreadList64From32(system, &out_num_threads, out_thread_ids, max_out_count, debug_handle);
1792 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1793 return ResultInvalidSize;
1794 }
1795 1599
1796 if (!IsValidAddressRange(dst_address, size)) { 1600 SetReg32(system, 0, Convert<uint32_t>(ret));
1797 LOG_ERROR(Kernel_SVC, 1601 SetReg32(system, 1, Convert<uint32_t>(out_num_threads));
1798 "Destination address range overflows the address space (dst_address=0x{:016X}, " 1602}
1799 "size=0x{:016X}).",
1800 dst_address, size);
1801 return ResultInvalidCurrentMemory;
1802 }
1803 1603
1804 if (!IsValidAddressRange(src_address, size)) { 1604static void SvcWrap_GetDebugThreadContext64From32(Core::System& system) {
1805 LOG_ERROR(Kernel_SVC, 1605 Result ret{};
1806 "Source address range overflows the address space (src_address=0x{:016X}, "
1807 "size=0x{:016X}).",
1808 src_address, size);
1809 return ResultInvalidCurrentMemory;
1810 }
1811 1606
1812 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 1607 uint32_t out_context{};
1813 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); 1608 Handle debug_handle{};
1814 if (process.IsNull()) { 1609 uint64_t thread_id{};
1815 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", 1610 uint32_t context_flags{};
1816 process_handle);
1817 return ResultInvalidHandle;
1818 }
1819 1611
1820 auto& page_table = process->PageTable(); 1612 out_context = Convert<uint32_t>(GetReg32(system, 0));
1821 if (!page_table.IsInsideAddressSpace(src_address, size)) { 1613 debug_handle = Convert<Handle>(GetReg32(system, 1));
1822 LOG_ERROR(Kernel_SVC, 1614 std::array<uint32_t, 2> thread_id_gather{};
1823 "Source address range is not within the address space (src_address=0x{:016X}, " 1615 thread_id_gather[0] = GetReg32(system, 2);
1824 "size=0x{:016X}).", 1616 thread_id_gather[1] = GetReg32(system, 3);
1825 src_address, size); 1617 thread_id = Convert<uint64_t>(thread_id_gather);
1826 return ResultInvalidCurrentMemory; 1618 context_flags = Convert<uint32_t>(GetReg32(system, 4));
1827 }
1828 1619
1829 if (!page_table.IsInsideASLRRegion(dst_address, size)) { 1620 ret = GetDebugThreadContext64From32(system, out_context, debug_handle, thread_id, context_flags);
1830 LOG_ERROR(Kernel_SVC,
1831 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
1832 "size=0x{:016X}).",
1833 dst_address, size);
1834 return ResultInvalidMemoryRegion;
1835 }
1836 1621
1837 return page_table.UnmapCodeMemory(dst_address, src_address, size, 1622 SetReg32(system, 0, Convert<uint32_t>(ret));
1838 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
1839} 1623}
1840 1624
1841/// Exits the current process 1625static void SvcWrap_SetDebugThreadContext64From32(Core::System& system) {
1842static void ExitProcess(Core::System& system) { 1626 Result ret{};
1843 auto* current_process = system.Kernel().CurrentProcess(); 1627
1628 Handle debug_handle{};
1629 uint64_t thread_id{};
1630 uint32_t context{};
1631 uint32_t context_flags{};
1844 1632
1845 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); 1633 debug_handle = Convert<Handle>(GetReg32(system, 0));
1846 ASSERT_MSG(current_process->GetState() == KProcess::State::Running, 1634 std::array<uint32_t, 2> thread_id_gather{};
1847 "Process has already exited"); 1635 thread_id_gather[0] = GetReg32(system, 2);
1636 thread_id_gather[1] = GetReg32(system, 3);
1637 thread_id = Convert<uint64_t>(thread_id_gather);
1638 context = Convert<uint32_t>(GetReg32(system, 1));
1639 context_flags = Convert<uint32_t>(GetReg32(system, 4));
1848 1640
1849 system.Exit(); 1641 ret = SetDebugThreadContext64From32(system, debug_handle, thread_id, context, context_flags);
1642
1643 SetReg32(system, 0, Convert<uint32_t>(ret));
1850} 1644}
1851 1645
1852static void ExitProcess32(Core::System& system) { 1646static void SvcWrap_QueryDebugProcessMemory64From32(Core::System& system) {
1853 ExitProcess(system); 1647 Result ret{};
1648
1649 PageInfo out_page_info{};
1650 uint32_t out_memory_info{};
1651 Handle process_handle{};
1652 uint32_t address{};
1653
1654 out_memory_info = Convert<uint32_t>(GetReg32(system, 0));
1655 process_handle = Convert<Handle>(GetReg32(system, 2));
1656 address = Convert<uint32_t>(GetReg32(system, 3));
1657
1658 ret = QueryDebugProcessMemory64From32(system, out_memory_info, &out_page_info, process_handle, address);
1659
1660 SetReg32(system, 0, Convert<uint32_t>(ret));
1661 SetReg32(system, 1, Convert<uint32_t>(out_page_info));
1854} 1662}
1855 1663
1856namespace { 1664static void SvcWrap_ReadDebugProcessMemory64From32(Core::System& system) {
1665 Result ret{};
1666
1667 uint32_t buffer{};
1668 Handle debug_handle{};
1669 uint32_t address{};
1670 uint32_t size{};
1671
1672 buffer = Convert<uint32_t>(GetReg32(system, 0));
1673 debug_handle = Convert<Handle>(GetReg32(system, 1));
1674 address = Convert<uint32_t>(GetReg32(system, 2));
1675 size = Convert<uint32_t>(GetReg32(system, 3));
1857 1676
1858constexpr bool IsValidVirtualCoreId(int32_t core_id) { 1677 ret = ReadDebugProcessMemory64From32(system, buffer, debug_handle, address, size);
1859 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); 1678
1679 SetReg32(system, 0, Convert<uint32_t>(ret));
1860} 1680}
1861 1681
1862} // Anonymous namespace 1682static void SvcWrap_WriteDebugProcessMemory64From32(Core::System& system) {
1683 Result ret{};
1863 1684
1864/// Creates a new thread 1685 Handle debug_handle{};
1865static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, 1686 uint32_t buffer{};
1866 VAddr stack_bottom, u32 priority, s32 core_id) { 1687 uint32_t address{};
1867 LOG_DEBUG(Kernel_SVC, 1688 uint32_t size{};
1868 "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
1869 "priority=0x{:08X}, core_id=0x{:08X}",
1870 entry_point, arg, stack_bottom, priority, core_id);
1871 1689
1872 // Adjust core id, if it's the default magic. 1690 debug_handle = Convert<Handle>(GetReg32(system, 0));
1873 auto& kernel = system.Kernel(); 1691 buffer = Convert<uint32_t>(GetReg32(system, 1));
1874 auto& process = *kernel.CurrentProcess(); 1692 address = Convert<uint32_t>(GetReg32(system, 2));
1875 if (core_id == IdealCoreUseProcessValue) { 1693 size = Convert<uint32_t>(GetReg32(system, 3));
1876 core_id = process.GetIdealCoreId();
1877 }
1878 1694
1879 // Validate arguments. 1695 ret = WriteDebugProcessMemory64From32(system, debug_handle, buffer, address, size);
1880 if (!IsValidVirtualCoreId(core_id)) {
1881 LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
1882 return ResultInvalidCoreId;
1883 }
1884 if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
1885 LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
1886 return ResultInvalidCoreId;
1887 }
1888 1696
1889 if (HighestThreadPriority > priority || priority > LowestThreadPriority) { 1697 SetReg32(system, 0, Convert<uint32_t>(ret));
1890 LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority); 1698}
1891 return ResultInvalidPriority;
1892 }
1893 if (!process.CheckThreadPriority(priority)) {
1894 LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
1895 return ResultInvalidPriority;
1896 }
1897 1699
1898 // Reserve a new thread from the process resource limit (waiting up to 100ms). 1700static void SvcWrap_SetHardwareBreakPoint64From32(Core::System& system) {
1899 KScopedResourceReservation thread_reservation( 1701 Result ret{};
1900 kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
1901 system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
1902 if (!thread_reservation.Succeeded()) {
1903 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
1904 return ResultLimitReached;
1905 }
1906 1702
1907 // Create the thread. 1703 HardwareBreakPointRegisterName name{};
1908 KThread* thread = KThread::Create(kernel); 1704 uint64_t flags{};
1909 if (!thread) { 1705 uint64_t value{};
1910 LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
1911 return ResultOutOfResource;
1912 }
1913 SCOPE_EXIT({ thread->Close(); });
1914 1706
1915 // Initialize the thread. 1707 name = Convert<HardwareBreakPointRegisterName>(GetReg32(system, 0));
1916 { 1708 std::array<uint32_t, 2> flags_gather{};
1917 KScopedLightLock lk{process.GetStateLock()}; 1709 flags_gather[0] = GetReg32(system, 2);
1918 R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom, 1710 flags_gather[1] = GetReg32(system, 3);
1919 priority, core_id, &process)); 1711 flags = Convert<uint64_t>(flags_gather);
1920 } 1712 std::array<uint32_t, 2> value_gather{};
1713 value_gather[0] = GetReg32(system, 1);
1714 value_gather[1] = GetReg32(system, 4);
1715 value = Convert<uint64_t>(value_gather);
1921 1716
1922 // Set the thread name for debugging purposes. 1717 ret = SetHardwareBreakPoint64From32(system, name, flags, value);
1923 thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
1924 1718
1925 // Commit the thread reservation. 1719 SetReg32(system, 0, Convert<uint32_t>(ret));
1926 thread_reservation.Commit(); 1720}
1927 1721
1928 // Register the new thread. 1722static void SvcWrap_GetDebugThreadParam64From32(Core::System& system) {
1929 KThread::Register(kernel, thread); 1723 Result ret{};
1930 1724
1931 // Add the thread to the handle table. 1725 uint64_t out_64{};
1932 R_TRY(process.GetHandleTable().Add(out_handle, thread)); 1726 uint32_t out_32{};
1727 Handle debug_handle{};
1728 uint64_t thread_id{};
1729 DebugThreadParam param{};
1933 1730
1934 return ResultSuccess; 1731 debug_handle = Convert<Handle>(GetReg32(system, 2));
1732 std::array<uint32_t, 2> thread_id_gather{};
1733 thread_id_gather[0] = GetReg32(system, 0);
1734 thread_id_gather[1] = GetReg32(system, 1);
1735 thread_id = Convert<uint64_t>(thread_id_gather);
1736 param = Convert<DebugThreadParam>(GetReg32(system, 3));
1737
1738 ret = GetDebugThreadParam64From32(system, &out_64, &out_32, debug_handle, thread_id, param);
1739
1740 SetReg32(system, 0, Convert<uint32_t>(ret));
1741 auto out_64_scatter = Convert<std::array<uint32_t, 2>>(out_64);
1742 SetReg32(system, 1, out_64_scatter[0]);
1743 SetReg32(system, 2, out_64_scatter[1]);
1744 SetReg32(system, 3, Convert<uint32_t>(out_32));
1935} 1745}
1936 1746
1937static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority, 1747static void SvcWrap_GetSystemInfo64From32(Core::System& system) {
1938 u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) { 1748 Result ret{};
1939 return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id); 1749
1750 uint64_t out{};
1751 SystemInfoType info_type{};
1752 Handle handle{};
1753 uint64_t info_subtype{};
1754
1755 info_type = Convert<SystemInfoType>(GetReg32(system, 1));
1756 handle = Convert<Handle>(GetReg32(system, 2));
1757 std::array<uint32_t, 2> info_subtype_gather{};
1758 info_subtype_gather[0] = GetReg32(system, 0);
1759 info_subtype_gather[1] = GetReg32(system, 3);
1760 info_subtype = Convert<uint64_t>(info_subtype_gather);
1761
1762 ret = GetSystemInfo64From32(system, &out, info_type, handle, info_subtype);
1763
1764 SetReg32(system, 0, Convert<uint32_t>(ret));
1765 auto out_scatter = Convert<std::array<uint32_t, 2>>(out);
1766 SetReg32(system, 1, out_scatter[0]);
1767 SetReg32(system, 2, out_scatter[1]);
1940} 1768}
1941 1769
1942/// Starts the thread for the provided handle 1770static void SvcWrap_CreatePort64From32(Core::System& system) {
1943static Result StartThread(Core::System& system, Handle thread_handle) { 1771 Result ret{};
1944 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
1945 1772
1946 // Get the thread from its handle. 1773 Handle out_server_handle{};
1947 KScopedAutoObject thread = 1774 Handle out_client_handle{};
1948 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); 1775 int32_t max_sessions{};
1949 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); 1776 bool is_light{};
1777 uint32_t name{};
1950 1778
1951 // Try to start the thread. 1779 max_sessions = Convert<int32_t>(GetReg32(system, 2));
1952 R_TRY(thread->Run()); 1780 is_light = Convert<bool>(GetReg32(system, 3));
1781 name = Convert<uint32_t>(GetReg32(system, 0));
1953 1782
1954 // If we succeeded, persist a reference to the thread. 1783 ret = CreatePort64From32(system, &out_server_handle, &out_client_handle, max_sessions, is_light, name);
1955 thread->Open();
1956 system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
1957 1784
1958 return ResultSuccess; 1785 SetReg32(system, 0, Convert<uint32_t>(ret));
1786 SetReg32(system, 1, Convert<uint32_t>(out_server_handle));
1787 SetReg32(system, 2, Convert<uint32_t>(out_client_handle));
1959} 1788}
1960 1789
1961static Result StartThread32(Core::System& system, Handle thread_handle) { 1790static void SvcWrap_ManageNamedPort64From32(Core::System& system) {
1962 return StartThread(system, thread_handle); 1791 Result ret{};
1792
1793 Handle out_server_handle{};
1794 uint32_t name{};
1795 int32_t max_sessions{};
1796
1797 name = Convert<uint32_t>(GetReg32(system, 1));
1798 max_sessions = Convert<int32_t>(GetReg32(system, 2));
1799
1800 ret = ManageNamedPort64From32(system, &out_server_handle, name, max_sessions);
1801
1802 SetReg32(system, 0, Convert<uint32_t>(ret));
1803 SetReg32(system, 1, Convert<uint32_t>(out_server_handle));
1963} 1804}
1964 1805
1965/// Called when a thread exits 1806static void SvcWrap_ConnectToPort64From32(Core::System& system) {
1966static void ExitThread(Core::System& system) { 1807 Result ret{};
1967 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC()); 1808
1809 Handle out_handle{};
1810 Handle port{};
1968 1811
1969 auto* const current_thread = GetCurrentThreadPointer(system.Kernel()); 1812 port = Convert<Handle>(GetReg32(system, 1));
1970 system.GlobalSchedulerContext().RemoveThread(current_thread); 1813
1971 current_thread->Exit(); 1814 ret = ConnectToPort64From32(system, &out_handle, port);
1972 system.Kernel().UnregisterInUseObject(current_thread); 1815
1816 SetReg32(system, 0, Convert<uint32_t>(ret));
1817 SetReg32(system, 1, Convert<uint32_t>(out_handle));
1973} 1818}
1974 1819
1975static void ExitThread32(Core::System& system) { 1820static void SvcWrap_SetProcessMemoryPermission64From32(Core::System& system) {
1976 ExitThread(system); 1821 Result ret{};
1822
1823 Handle process_handle{};
1824 uint64_t address{};
1825 uint64_t size{};
1826 MemoryPermission perm{};
1827
1828 process_handle = Convert<Handle>(GetReg32(system, 0));
1829 std::array<uint32_t, 2> address_gather{};
1830 address_gather[0] = GetReg32(system, 2);
1831 address_gather[1] = GetReg32(system, 3);
1832 address = Convert<uint64_t>(address_gather);
1833 std::array<uint32_t, 2> size_gather{};
1834 size_gather[0] = GetReg32(system, 1);
1835 size_gather[1] = GetReg32(system, 4);
1836 size = Convert<uint64_t>(size_gather);
1837 perm = Convert<MemoryPermission>(GetReg32(system, 5));
1838
1839 ret = SetProcessMemoryPermission64From32(system, process_handle, address, size, perm);
1840
1841 SetReg32(system, 0, Convert<uint32_t>(ret));
1977} 1842}
1978 1843
1979/// Sleep the current thread 1844static void SvcWrap_MapProcessMemory64From32(Core::System& system) {
1980static void SleepThread(Core::System& system, s64 nanoseconds) { 1845 Result ret{};
1981 auto& kernel = system.Kernel(); 1846
1982 const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); 1847 uint32_t dst_address{};
1983 1848 Handle process_handle{};
1984 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); 1849 uint64_t src_address{};
1985 1850 uint32_t size{};
1986 // When the input tick is positive, sleep. 1851
1987 if (nanoseconds > 0) { 1852 dst_address = Convert<uint32_t>(GetReg32(system, 0));
1988 // Convert the timeout from nanoseconds to ticks. 1853 process_handle = Convert<Handle>(GetReg32(system, 1));
1989 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... 1854 std::array<uint32_t, 2> src_address_gather{};
1990 1855 src_address_gather[0] = GetReg32(system, 2);
1991 // Sleep. 1856 src_address_gather[1] = GetReg32(system, 3);
1992 // NOTE: Nintendo does not check the result of this sleep. 1857 src_address = Convert<uint64_t>(src_address_gather);
1993 static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); 1858 size = Convert<uint32_t>(GetReg32(system, 4));
1994 } else if (yield_type == Svc::YieldType::WithoutCoreMigration) { 1859
1995 KScheduler::YieldWithoutCoreMigration(kernel); 1860 ret = MapProcessMemory64From32(system, dst_address, process_handle, src_address, size);
1996 } else if (yield_type == Svc::YieldType::WithCoreMigration) { 1861
1997 KScheduler::YieldWithCoreMigration(kernel); 1862 SetReg32(system, 0, Convert<uint32_t>(ret));
1998 } else if (yield_type == Svc::YieldType::ToAnyThread) {
1999 KScheduler::YieldToAnyThread(kernel);
2000 } else {
2001 // Nintendo does nothing at all if an otherwise invalid value is passed.
2002 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
2003 }
2004} 1863}
2005 1864
2006static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanoseconds_high) { 1865static void SvcWrap_UnmapProcessMemory64From32(Core::System& system) {
2007 const auto nanoseconds = static_cast<s64>(u64{nanoseconds_low} | (u64{nanoseconds_high} << 32)); 1866 Result ret{};
2008 SleepThread(system, nanoseconds); 1867
1868 uint32_t dst_address{};
1869 Handle process_handle{};
1870 uint64_t src_address{};
1871 uint32_t size{};
1872
1873 dst_address = Convert<uint32_t>(GetReg32(system, 0));
1874 process_handle = Convert<Handle>(GetReg32(system, 1));
1875 std::array<uint32_t, 2> src_address_gather{};
1876 src_address_gather[0] = GetReg32(system, 2);
1877 src_address_gather[1] = GetReg32(system, 3);
1878 src_address = Convert<uint64_t>(src_address_gather);
1879 size = Convert<uint32_t>(GetReg32(system, 4));
1880
1881 ret = UnmapProcessMemory64From32(system, dst_address, process_handle, src_address, size);
1882
1883 SetReg32(system, 0, Convert<uint32_t>(ret));
2009} 1884}
2010 1885
2011/// Wait process wide key atomic 1886static void SvcWrap_QueryProcessMemory64From32(Core::System& system) {
2012static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag, 1887 Result ret{};
2013 s64 timeout_ns) {
2014 LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
2015 cv_key, tag, timeout_ns);
2016 1888
2017 // Validate input. 1889 PageInfo out_page_info{};
2018 if (IsKernelAddress(address)) { 1890 uint32_t out_memory_info{};
2019 LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address); 1891 Handle process_handle{};
2020 return ResultInvalidCurrentMemory; 1892 uint64_t address{};
2021 }
2022 if (!Common::IsAligned(address, sizeof(s32))) {
2023 LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
2024 return ResultInvalidAddress;
2025 }
2026 1893
2027 // Convert timeout from nanoseconds to ticks. 1894 out_memory_info = Convert<uint32_t>(GetReg32(system, 0));
2028 s64 timeout{}; 1895 process_handle = Convert<Handle>(GetReg32(system, 2));
2029 if (timeout_ns > 0) { 1896 std::array<uint32_t, 2> address_gather{};
2030 const s64 offset_tick(timeout_ns); 1897 address_gather[0] = GetReg32(system, 1);
2031 if (offset_tick > 0) { 1898 address_gather[1] = GetReg32(system, 3);
2032 timeout = offset_tick + 2; 1899 address = Convert<uint64_t>(address_gather);
2033 if (timeout <= 0) { 1900
2034 timeout = std::numeric_limits<s64>::max(); 1901 ret = QueryProcessMemory64From32(system, out_memory_info, &out_page_info, process_handle, address);
2035 }
2036 } else {
2037 timeout = std::numeric_limits<s64>::max();
2038 }
2039 } else {
2040 timeout = timeout_ns;
2041 }
2042 1902
2043 // Wait on the condition variable. 1903 SetReg32(system, 0, Convert<uint32_t>(ret));
2044 return system.Kernel().CurrentProcess()->WaitConditionVariable( 1904 SetReg32(system, 1, Convert<uint32_t>(out_page_info));
2045 address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
2046} 1905}
2047 1906
2048static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, 1907static void SvcWrap_MapProcessCodeMemory64From32(Core::System& system) {
2049 u32 timeout_ns_low, u32 timeout_ns_high) { 1908 Result ret{};
2050 const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); 1909
2051 return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); 1910 Handle process_handle{};
1911 uint64_t dst_address{};
1912 uint64_t src_address{};
1913 uint64_t size{};
1914
1915 process_handle = Convert<Handle>(GetReg32(system, 0));
1916 std::array<uint32_t, 2> dst_address_gather{};
1917 dst_address_gather[0] = GetReg32(system, 2);
1918 dst_address_gather[1] = GetReg32(system, 3);
1919 dst_address = Convert<uint64_t>(dst_address_gather);
1920 std::array<uint32_t, 2> src_address_gather{};
1921 src_address_gather[0] = GetReg32(system, 1);
1922 src_address_gather[1] = GetReg32(system, 4);
1923 src_address = Convert<uint64_t>(src_address_gather);
1924 std::array<uint32_t, 2> size_gather{};
1925 size_gather[0] = GetReg32(system, 5);
1926 size_gather[1] = GetReg32(system, 6);
1927 size = Convert<uint64_t>(size_gather);
1928
1929 ret = MapProcessCodeMemory64From32(system, process_handle, dst_address, src_address, size);
1930
1931 SetReg32(system, 0, Convert<uint32_t>(ret));
2052} 1932}
2053 1933
2054/// Signal process wide key 1934static void SvcWrap_UnmapProcessCodeMemory64From32(Core::System& system) {
2055static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { 1935 Result ret{};
2056 LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
2057 1936
2058 // Signal the condition variable. 1937 Handle process_handle{};
2059 return system.Kernel().CurrentProcess()->SignalConditionVariable( 1938 uint64_t dst_address{};
2060 Common::AlignDown(cv_key, sizeof(u32)), count); 1939 uint64_t src_address{};
1940 uint64_t size{};
1941
1942 process_handle = Convert<Handle>(GetReg32(system, 0));
1943 std::array<uint32_t, 2> dst_address_gather{};
1944 dst_address_gather[0] = GetReg32(system, 2);
1945 dst_address_gather[1] = GetReg32(system, 3);
1946 dst_address = Convert<uint64_t>(dst_address_gather);
1947 std::array<uint32_t, 2> src_address_gather{};
1948 src_address_gather[0] = GetReg32(system, 1);
1949 src_address_gather[1] = GetReg32(system, 4);
1950 src_address = Convert<uint64_t>(src_address_gather);
1951 std::array<uint32_t, 2> size_gather{};
1952 size_gather[0] = GetReg32(system, 5);
1953 size_gather[1] = GetReg32(system, 6);
1954 size = Convert<uint64_t>(size_gather);
1955
1956 ret = UnmapProcessCodeMemory64From32(system, process_handle, dst_address, src_address, size);
1957
1958 SetReg32(system, 0, Convert<uint32_t>(ret));
2061} 1959}
2062 1960
2063static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { 1961static void SvcWrap_CreateProcess64From32(Core::System& system) {
2064 SignalProcessWideKey(system, cv_key, count); 1962 Result ret{};
1963
1964 Handle out_handle{};
1965 uint32_t parameters{};
1966 uint32_t caps{};
1967 int32_t num_caps{};
1968
1969 parameters = Convert<uint32_t>(GetReg32(system, 1));
1970 caps = Convert<uint32_t>(GetReg32(system, 2));
1971 num_caps = Convert<int32_t>(GetReg32(system, 3));
1972
1973 ret = CreateProcess64From32(system, &out_handle, parameters, caps, num_caps);
1974
1975 SetReg32(system, 0, Convert<uint32_t>(ret));
1976 SetReg32(system, 1, Convert<uint32_t>(out_handle));
2065} 1977}
2066 1978
2067namespace { 1979static void SvcWrap_StartProcess64From32(Core::System& system) {
1980 Result ret{};
2068 1981
2069constexpr bool IsValidSignalType(Svc::SignalType type) { 1982 Handle process_handle{};
2070 switch (type) { 1983 int32_t priority{};
2071 case Svc::SignalType::Signal: 1984 int32_t core_id{};
2072 case Svc::SignalType::SignalAndIncrementIfEqual: 1985 uint64_t main_thread_stack_size{};
2073 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: 1986
2074 return true; 1987 process_handle = Convert<Handle>(GetReg32(system, 0));
2075 default: 1988 priority = Convert<int32_t>(GetReg32(system, 1));
2076 return false; 1989 core_id = Convert<int32_t>(GetReg32(system, 2));
2077 } 1990 std::array<uint32_t, 2> main_thread_stack_size_gather{};
1991 main_thread_stack_size_gather[0] = GetReg32(system, 3);
1992 main_thread_stack_size_gather[1] = GetReg32(system, 4);
1993 main_thread_stack_size = Convert<uint64_t>(main_thread_stack_size_gather);
1994
1995 ret = StartProcess64From32(system, process_handle, priority, core_id, main_thread_stack_size);
1996
1997 SetReg32(system, 0, Convert<uint32_t>(ret));
2078} 1998}
2079 1999
2080constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { 2000static void SvcWrap_TerminateProcess64From32(Core::System& system) {
2081 switch (type) { 2001 Result ret{};
2082 case Svc::ArbitrationType::WaitIfLessThan: 2002
2083 case Svc::ArbitrationType::DecrementAndWaitIfLessThan: 2003 Handle process_handle{};
2084 case Svc::ArbitrationType::WaitIfEqual: 2004
2085 return true; 2005 process_handle = Convert<Handle>(GetReg32(system, 0));
2086 default: 2006
2087 return false; 2007 ret = TerminateProcess64From32(system, process_handle);
2088 } 2008
2009 SetReg32(system, 0, Convert<uint32_t>(ret));
2089} 2010}
2090 2011
2091} // namespace 2012static void SvcWrap_GetProcessInfo64From32(Core::System& system) {
2013 Result ret{};
2092 2014
2093// Wait for an address (via Address Arbiter) 2015 int64_t out_info{};
2094static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, 2016 Handle process_handle{};
2095 s32 value, s64 timeout_ns) { 2017 ProcessInfoType info_type{};
2096 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
2097 address, arb_type, value, timeout_ns);
2098 2018
2099 // Validate input. 2019 process_handle = Convert<Handle>(GetReg32(system, 1));
2100 if (IsKernelAddress(address)) { 2020 info_type = Convert<ProcessInfoType>(GetReg32(system, 2));
2101 LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
2102 return ResultInvalidCurrentMemory;
2103 }
2104 if (!Common::IsAligned(address, sizeof(s32))) {
2105 LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
2106 return ResultInvalidAddress;
2107 }
2108 if (!IsValidArbitrationType(arb_type)) {
2109 LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
2110 return ResultInvalidEnumValue;
2111 }
2112 2021
2113 // Convert timeout from nanoseconds to ticks. 2022 ret = GetProcessInfo64From32(system, &out_info, process_handle, info_type);
2114 s64 timeout{}; 2023
2115 if (timeout_ns > 0) { 2024 SetReg32(system, 0, Convert<uint32_t>(ret));
2116 const s64 offset_tick(timeout_ns); 2025 auto out_info_scatter = Convert<std::array<uint32_t, 2>>(out_info);
2117 if (offset_tick > 0) { 2026 SetReg32(system, 1, out_info_scatter[0]);
2118 timeout = offset_tick + 2; 2027 SetReg32(system, 2, out_info_scatter[1]);
2119 if (timeout <= 0) { 2028}
2120 timeout = std::numeric_limits<s64>::max(); 2029
2121 } 2030static void SvcWrap_CreateResourceLimit64From32(Core::System& system) {
2122 } else { 2031 Result ret{};
2123 timeout = std::numeric_limits<s64>::max();
2124 }
2125 } else {
2126 timeout = timeout_ns;
2127 }
2128 2032
2129 return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); 2033 Handle out_handle{};
2034
2035 ret = CreateResourceLimit64From32(system, &out_handle);
2036
2037 SetReg32(system, 0, Convert<uint32_t>(ret));
2038 SetReg32(system, 1, Convert<uint32_t>(out_handle));
2130} 2039}
2131 2040
2132static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, 2041static void SvcWrap_SetResourceLimitLimitValue64From32(Core::System& system) {
2133 s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { 2042 Result ret{};
2134 const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32)); 2043
2135 return WaitForAddress(system, address, arb_type, value, timeout); 2044 Handle resource_limit_handle{};
2045 LimitableResource which{};
2046 int64_t limit_value{};
2047
2048 resource_limit_handle = Convert<Handle>(GetReg32(system, 0));
2049 which = Convert<LimitableResource>(GetReg32(system, 1));
2050 std::array<uint32_t, 2> limit_value_gather{};
2051 limit_value_gather[0] = GetReg32(system, 2);
2052 limit_value_gather[1] = GetReg32(system, 3);
2053 limit_value = Convert<int64_t>(limit_value_gather);
2054
2055 ret = SetResourceLimitLimitValue64From32(system, resource_limit_handle, which, limit_value);
2056
2057 SetReg32(system, 0, Convert<uint32_t>(ret));
2136} 2058}
2137 2059
2138// Signals to an address (via Address Arbiter) 2060static void SvcWrap_MapInsecureMemory64From32(Core::System& system) {
2139static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, 2061 Result ret{};
2140 s32 value, s32 count) {
2141 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
2142 address, signal_type, value, count);
2143 2062
2144 // Validate input. 2063 uint32_t address{};
2145 if (IsKernelAddress(address)) { 2064 uint32_t size{};
2146 LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address); 2065
2147 return ResultInvalidCurrentMemory; 2066 address = Convert<uint32_t>(GetReg32(system, 0));
2148 } 2067 size = Convert<uint32_t>(GetReg32(system, 1));
2149 if (!Common::IsAligned(address, sizeof(s32))) {
2150 LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
2151 return ResultInvalidAddress;
2152 }
2153 if (!IsValidSignalType(signal_type)) {
2154 LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
2155 return ResultInvalidEnumValue;
2156 }
2157 2068
2158 return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, 2069 ret = MapInsecureMemory64From32(system, address, size);
2159 count); 2070
2071 SetReg32(system, 0, Convert<uint32_t>(ret));
2160} 2072}
2161 2073
2162static void SynchronizePreemptionState(Core::System& system) { 2074static void SvcWrap_UnmapInsecureMemory64From32(Core::System& system) {
2163 auto& kernel = system.Kernel(); 2075 Result ret{};
2164 2076
2165 // Lock the scheduler. 2077 uint32_t address{};
2166 KScopedSchedulerLock sl{kernel}; 2078 uint32_t size{};
2167 2079
2168 // If the current thread is pinned, unpin it. 2080 address = Convert<uint32_t>(GetReg32(system, 0));
2169 KProcess* cur_process = system.Kernel().CurrentProcess(); 2081 size = Convert<uint32_t>(GetReg32(system, 1));
2170 const auto core_id = GetCurrentCoreId(kernel);
2171 2082
2172 if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) { 2083 ret = UnmapInsecureMemory64From32(system, address, size);
2173 // Clear the current thread's interrupt flag.
2174 GetCurrentThread(kernel).ClearInterruptFlag();
2175 2084
2176 // Unpin the current thread. 2085 SetReg32(system, 0, Convert<uint32_t>(ret));
2177 cur_process->UnpinCurrentThread(core_id);
2178 }
2179} 2086}
2180 2087
2181static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, 2088static void SvcWrap_SetHeapSize64(Core::System& system) {
2182 s32 value, s32 count) { 2089 Result ret{};
2183 return SignalToAddress(system, address, signal_type, value, count); 2090
2091 uintptr_t out_address{};
2092 uint64_t size{};
2093
2094 size = Convert<uint64_t>(GetReg64(system, 1));
2095
2096 ret = SetHeapSize64(system, &out_address, size);
2097
2098 SetReg64(system, 0, Convert<uint64_t>(ret));
2099 SetReg64(system, 1, Convert<uint64_t>(out_address));
2184} 2100}
2185 2101
2186static void KernelDebug([[maybe_unused]] Core::System& system, 2102static void SvcWrap_SetMemoryPermission64(Core::System& system) {
2187 [[maybe_unused]] u32 kernel_debug_type, [[maybe_unused]] u64 param1, 2103 Result ret{};
2188 [[maybe_unused]] u64 param2, [[maybe_unused]] u64 param3) { 2104
2189 // Intentionally do nothing, as this does nothing in released kernel binaries. 2105 uint64_t address{};
2106 uint64_t size{};
2107 MemoryPermission perm{};
2108
2109 address = Convert<uint64_t>(GetReg64(system, 0));
2110 size = Convert<uint64_t>(GetReg64(system, 1));
2111 perm = Convert<MemoryPermission>(GetReg64(system, 2));
2112
2113 ret = SetMemoryPermission64(system, address, size, perm);
2114
2115 SetReg64(system, 0, Convert<uint64_t>(ret));
2190} 2116}
2191 2117
2192static void ChangeKernelTraceState([[maybe_unused]] Core::System& system, 2118static void SvcWrap_SetMemoryAttribute64(Core::System& system) {
2193 [[maybe_unused]] u32 trace_state) { 2119 Result ret{};
2194 // Intentionally do nothing, as this does nothing in released kernel binaries. 2120
2121 uint64_t address{};
2122 uint64_t size{};
2123 uint32_t mask{};
2124 uint32_t attr{};
2125
2126 address = Convert<uint64_t>(GetReg64(system, 0));
2127 size = Convert<uint64_t>(GetReg64(system, 1));
2128 mask = Convert<uint32_t>(GetReg64(system, 2));
2129 attr = Convert<uint32_t>(GetReg64(system, 3));
2130
2131 ret = SetMemoryAttribute64(system, address, size, mask, attr);
2132
2133 SetReg64(system, 0, Convert<uint64_t>(ret));
2195} 2134}
2196 2135
2197/// This returns the total CPU ticks elapsed since the CPU was powered-on 2136static void SvcWrap_MapMemory64(Core::System& system) {
2198static u64 GetSystemTick(Core::System& system) { 2137 Result ret{};
2199 LOG_TRACE(Kernel_SVC, "called");
2200 2138
2201 auto& core_timing = system.CoreTiming(); 2139 uint64_t dst_address{};
2140 uint64_t src_address{};
2141 uint64_t size{};
2202 2142
2203 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) 2143 dst_address = Convert<uint64_t>(GetReg64(system, 0));
2204 const u64 result{core_timing.GetClockTicks()}; 2144 src_address = Convert<uint64_t>(GetReg64(system, 1));
2145 size = Convert<uint64_t>(GetReg64(system, 2));
2205 2146
2206 if (!system.Kernel().IsMulticore()) { 2147 ret = MapMemory64(system, dst_address, src_address, size);
2207 core_timing.AddTicks(400U);
2208 }
2209 2148
2210 return result; 2149 SetReg64(system, 0, Convert<uint64_t>(ret));
2211} 2150}
2212 2151
2213static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high) { 2152static void SvcWrap_UnmapMemory64(Core::System& system) {
2214 const auto time = GetSystemTick(system); 2153 Result ret{};
2215 *time_low = static_cast<u32>(time); 2154
2216 *time_high = static_cast<u32>(time >> 32); 2155 uint64_t dst_address{};
2156 uint64_t src_address{};
2157 uint64_t size{};
2158
2159 dst_address = Convert<uint64_t>(GetReg64(system, 0));
2160 src_address = Convert<uint64_t>(GetReg64(system, 1));
2161 size = Convert<uint64_t>(GetReg64(system, 2));
2162
2163 ret = UnmapMemory64(system, dst_address, src_address, size);
2164
2165 SetReg64(system, 0, Convert<uint64_t>(ret));
2217} 2166}
2218 2167
2219/// Close a handle 2168static void SvcWrap_QueryMemory64(Core::System& system) {
2220static Result CloseHandle(Core::System& system, Handle handle) { 2169 Result ret{};
2221 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
2222 2170
2223 // Remove the handle. 2171 PageInfo out_page_info{};
2224 R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle), 2172 uint64_t out_memory_info{};
2225 ResultInvalidHandle); 2173 uint64_t address{};
2226 2174
2227 return ResultSuccess; 2175 out_memory_info = Convert<uint64_t>(GetReg64(system, 0));
2176 address = Convert<uint64_t>(GetReg64(system, 2));
2177
2178 ret = QueryMemory64(system, out_memory_info, &out_page_info, address);
2179
2180 SetReg64(system, 0, Convert<uint64_t>(ret));
2181 SetReg64(system, 1, Convert<uint64_t>(out_page_info));
2228} 2182}
2229 2183
2230static Result CloseHandle32(Core::System& system, Handle handle) { 2184static void SvcWrap_ExitProcess64(Core::System& system) {
2231 return CloseHandle(system, handle); 2185 ExitProcess64(system);
2232} 2186}
2233 2187
2234/// Clears the signaled state of an event or process. 2188static void SvcWrap_CreateThread64(Core::System& system) {
2235static Result ResetSignal(Core::System& system, Handle handle) { 2189 Result ret{};
2236 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
2237 2190
2238 // Get the current handle table. 2191 Handle out_handle{};
2239 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 2192 uint64_t func{};
2193 uint64_t arg{};
2194 uint64_t stack_bottom{};
2195 int32_t priority{};
2196 int32_t core_id{};
2240 2197
2241 // Try to reset as readable event. 2198 func = Convert<uint64_t>(GetReg64(system, 1));
2242 { 2199 arg = Convert<uint64_t>(GetReg64(system, 2));
2243 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle); 2200 stack_bottom = Convert<uint64_t>(GetReg64(system, 3));
2244 if (readable_event.IsNotNull()) { 2201 priority = Convert<int32_t>(GetReg64(system, 4));
2245 return readable_event->Reset(); 2202 core_id = Convert<int32_t>(GetReg64(system, 5));
2246 }
2247 }
2248 2203
2249 // Try to reset as process. 2204 ret = CreateThread64(system, &out_handle, func, arg, stack_bottom, priority, core_id);
2250 { 2205
2251 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle); 2206 SetReg64(system, 0, Convert<uint64_t>(ret));
2252 if (process.IsNotNull()) { 2207 SetReg64(system, 1, Convert<uint64_t>(out_handle));
2253 return process->Reset(); 2208}
2254 } 2209
2255 } 2210static void SvcWrap_StartThread64(Core::System& system) {
2211 Result ret{};
2212
2213 Handle thread_handle{};
2256 2214
2257 LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle); 2215 thread_handle = Convert<Handle>(GetReg64(system, 0));
2258 2216
2259 return ResultInvalidHandle; 2217 ret = StartThread64(system, thread_handle);
2218
2219 SetReg64(system, 0, Convert<uint64_t>(ret));
2260} 2220}
2261 2221
2262static Result ResetSignal32(Core::System& system, Handle handle) { 2222static void SvcWrap_ExitThread64(Core::System& system) {
2263 return ResetSignal(system, handle); 2223 ExitThread64(system);
2264} 2224}
2265 2225
2266namespace { 2226static void SvcWrap_SleepThread64(Core::System& system) {
2227 int64_t ns{};
2267 2228
2268constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) { 2229 ns = Convert<int64_t>(GetReg64(system, 0));
2269 switch (perm) { 2230
2270 case MemoryPermission::None: 2231 SleepThread64(system, ns);
2271 case MemoryPermission::Read:
2272 case MemoryPermission::ReadWrite:
2273 return true;
2274 default:
2275 return false;
2276 }
2277} 2232}
2278 2233
2279} // Anonymous namespace 2234static void SvcWrap_GetThreadPriority64(Core::System& system) {
2235 Result ret{};
2280 2236
2281/// Creates a TransferMemory object 2237 int32_t out_priority{};
2282static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size, 2238 Handle thread_handle{};
2283 MemoryPermission map_perm) { 2239
2284 auto& kernel = system.Kernel(); 2240 thread_handle = Convert<Handle>(GetReg64(system, 1));
2241
2242 ret = GetThreadPriority64(system, &out_priority, thread_handle);
2285 2243
2286 // Validate the size. 2244 SetReg64(system, 0, Convert<uint64_t>(ret));
2287 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); 2245 SetReg64(system, 1, Convert<uint64_t>(out_priority));
2288 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); 2246}
2289 R_UNLESS(size > 0, ResultInvalidSize); 2247
2290 R_UNLESS((address < address + size), ResultInvalidCurrentMemory); 2248static void SvcWrap_SetThreadPriority64(Core::System& system) {
2249 Result ret{};
2250
2251 Handle thread_handle{};
2252 int32_t priority{};
2253
2254 thread_handle = Convert<Handle>(GetReg64(system, 0));
2255 priority = Convert<int32_t>(GetReg64(system, 1));
2291 2256
2292 // Validate the permissions. 2257 ret = SetThreadPriority64(system, thread_handle, priority);
2293 R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
2294 2258
2295 // Get the current process and handle table. 2259 SetReg64(system, 0, Convert<uint64_t>(ret));
2296 auto& process = *kernel.CurrentProcess(); 2260}
2297 auto& handle_table = process.GetHandleTable(); 2261
2262static void SvcWrap_GetThreadCoreMask64(Core::System& system) {
2263 Result ret{};
2298 2264
2299 // Reserve a new transfer memory from the process resource limit. 2265 int32_t out_core_id{};
2300 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), 2266 uint64_t out_affinity_mask{};
2301 LimitableResource::TransferMemoryCountMax); 2267 Handle thread_handle{};
2302 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
2303 2268
2304 // Create the transfer memory. 2269 thread_handle = Convert<Handle>(GetReg64(system, 2));
2305 KTransferMemory* trmem = KTransferMemory::Create(kernel);
2306 R_UNLESS(trmem != nullptr, ResultOutOfResource);
2307 2270
2308 // Ensure the only reference is in the handle table when we're done. 2271 ret = GetThreadCoreMask64(system, &out_core_id, &out_affinity_mask, thread_handle);
2309 SCOPE_EXIT({ trmem->Close(); });
2310 2272
2311 // Ensure that the region is in range. 2273 SetReg64(system, 0, Convert<uint64_t>(ret));
2312 R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory); 2274 SetReg64(system, 1, Convert<uint64_t>(out_core_id));
2275 SetReg64(system, 2, Convert<uint64_t>(out_affinity_mask));
2276}
2313 2277
2314 // Initialize the transfer memory. 2278static void SvcWrap_SetThreadCoreMask64(Core::System& system) {
2315 R_TRY(trmem->Initialize(address, size, map_perm)); 2279 Result ret{};
2316 2280
2317 // Commit the reservation. 2281 Handle thread_handle{};
2318 trmem_reservation.Commit(); 2282 int32_t core_id{};
2283 uint64_t affinity_mask{};
2319 2284
2320 // Register the transfer memory. 2285 thread_handle = Convert<Handle>(GetReg64(system, 0));
2321 KTransferMemory::Register(kernel, trmem); 2286 core_id = Convert<int32_t>(GetReg64(system, 1));
2287 affinity_mask = Convert<uint64_t>(GetReg64(system, 2));
2322 2288
2323 // Add the transfer memory to the handle table. 2289 ret = SetThreadCoreMask64(system, thread_handle, core_id, affinity_mask);
2324 R_TRY(handle_table.Add(out, trmem));
2325 2290
2326 return ResultSuccess; 2291 SetReg64(system, 0, Convert<uint64_t>(ret));
2327} 2292}
2328 2293
2329static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size, 2294static void SvcWrap_GetCurrentProcessorNumber64(Core::System& system) {
2330 MemoryPermission map_perm) { 2295 int32_t ret{};
2331 return CreateTransferMemory(system, out, address, size, map_perm); 2296
2297 ret = GetCurrentProcessorNumber64(system);
2298
2299 SetReg64(system, 0, Convert<uint64_t>(ret));
2332} 2300}
2333 2301
2334static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, 2302static void SvcWrap_SignalEvent64(Core::System& system) {
2335 u64* out_affinity_mask) { 2303 Result ret{};
2336 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 2304
2305 Handle event_handle{};
2337 2306
2338 // Get the thread from its handle. 2307 event_handle = Convert<Handle>(GetReg64(system, 0));
2339 KScopedAutoObject thread =
2340 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
2341 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
2342 2308
2343 // Get the core mask. 2309 ret = SignalEvent64(system, event_handle);
2344 R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
2345 2310
2346 return ResultSuccess; 2311 SetReg64(system, 0, Convert<uint64_t>(ret));
2347} 2312}
2348 2313
2349static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, 2314static void SvcWrap_ClearEvent64(Core::System& system) {
2350 u32* out_affinity_mask_low, u32* out_affinity_mask_high) { 2315 Result ret{};
2351 u64 out_affinity_mask{}; 2316
2352 const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); 2317 Handle event_handle{};
2353 *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); 2318
2354 *out_affinity_mask_low = static_cast<u32>(out_affinity_mask); 2319 event_handle = Convert<Handle>(GetReg64(system, 0));
2355 return result; 2320
2321 ret = ClearEvent64(system, event_handle);
2322
2323 SetReg64(system, 0, Convert<uint64_t>(ret));
2356} 2324}
2357 2325
2358static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, 2326static void SvcWrap_MapSharedMemory64(Core::System& system) {
2359 u64 affinity_mask) { 2327 Result ret{};
2360 // Determine the core id/affinity mask. 2328
2361 if (core_id == IdealCoreUseProcessValue) { 2329 Handle shmem_handle{};
2362 core_id = system.Kernel().CurrentProcess()->GetIdealCoreId(); 2330 uint64_t address{};
2363 affinity_mask = (1ULL << core_id); 2331 uint64_t size{};
2364 } else { 2332 MemoryPermission map_perm{};
2365 // Validate the affinity mask.
2366 const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
2367 R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
2368 R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
2369
2370 // Validate the core id.
2371 if (IsValidVirtualCoreId(core_id)) {
2372 R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
2373 } else {
2374 R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
2375 ResultInvalidCoreId);
2376 }
2377 }
2378 2333
2379 // Get the thread from its handle. 2334 shmem_handle = Convert<Handle>(GetReg64(system, 0));
2380 KScopedAutoObject thread = 2335 address = Convert<uint64_t>(GetReg64(system, 1));
2381 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle); 2336 size = Convert<uint64_t>(GetReg64(system, 2));
2382 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle); 2337 map_perm = Convert<MemoryPermission>(GetReg64(system, 3));
2383 2338
2384 // Set the core mask. 2339 ret = MapSharedMemory64(system, shmem_handle, address, size, map_perm);
2385 R_TRY(thread->SetCoreMask(core_id, affinity_mask));
2386 2340
2387 return ResultSuccess; 2341 SetReg64(system, 0, Convert<uint64_t>(ret));
2388} 2342}
2389 2343
2390static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, 2344static void SvcWrap_UnmapSharedMemory64(Core::System& system) {
2391 u32 affinity_mask_low, u32 affinity_mask_high) { 2345 Result ret{};
2392 const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); 2346
2393 return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask); 2347 Handle shmem_handle{};
2348 uint64_t address{};
2349 uint64_t size{};
2350
2351 shmem_handle = Convert<Handle>(GetReg64(system, 0));
2352 address = Convert<uint64_t>(GetReg64(system, 1));
2353 size = Convert<uint64_t>(GetReg64(system, 2));
2354
2355 ret = UnmapSharedMemory64(system, shmem_handle, address, size);
2356
2357 SetReg64(system, 0, Convert<uint64_t>(ret));
2394} 2358}
2395 2359
2396static Result SignalEvent(Core::System& system, Handle event_handle) { 2360static void SvcWrap_CreateTransferMemory64(Core::System& system) {
2397 LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); 2361 Result ret{};
2398 2362
2399 // Get the current handle table. 2363 Handle out_handle{};
2400 const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 2364 uint64_t address{};
2365 uint64_t size{};
2366 MemoryPermission map_perm{};
2401 2367
2402 // Get the event. 2368 address = Convert<uint64_t>(GetReg64(system, 1));
2403 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle); 2369 size = Convert<uint64_t>(GetReg64(system, 2));
2404 R_UNLESS(event.IsNotNull(), ResultInvalidHandle); 2370 map_perm = Convert<MemoryPermission>(GetReg64(system, 3));
2405 2371
2406 return event->Signal(); 2372 ret = CreateTransferMemory64(system, &out_handle, address, size, map_perm);
2373
2374 SetReg64(system, 0, Convert<uint64_t>(ret));
2375 SetReg64(system, 1, Convert<uint64_t>(out_handle));
2407} 2376}
2408 2377
2409static Result SignalEvent32(Core::System& system, Handle event_handle) { 2378static void SvcWrap_CloseHandle64(Core::System& system) {
2410 return SignalEvent(system, event_handle); 2379 Result ret{};
2380
2381 Handle handle{};
2382
2383 handle = Convert<Handle>(GetReg64(system, 0));
2384
2385 ret = CloseHandle64(system, handle);
2386
2387 SetReg64(system, 0, Convert<uint64_t>(ret));
2411} 2388}
2412 2389
2413static Result ClearEvent(Core::System& system, Handle event_handle) { 2390static void SvcWrap_ResetSignal64(Core::System& system) {
2414 LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle); 2391 Result ret{};
2415 2392
2416 // Get the current handle table. 2393 Handle handle{};
2417 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
2418 2394
2419 // Try to clear the writable event. 2395 handle = Convert<Handle>(GetReg64(system, 0));
2420 {
2421 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
2422 if (event.IsNotNull()) {
2423 return event->Clear();
2424 }
2425 }
2426 2396
2427 // Try to clear the readable event. 2397 ret = ResetSignal64(system, handle);
2428 { 2398
2429 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle); 2399 SetReg64(system, 0, Convert<uint64_t>(ret));
2430 if (readable_event.IsNotNull()) { 2400}
2431 return readable_event->Clear();
2432 }
2433 }
2434 2401
2435 LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle); 2402static void SvcWrap_WaitSynchronization64(Core::System& system) {
2403 Result ret{};
2436 2404
2437 return ResultInvalidHandle; 2405 int32_t out_index{};
2406 uint64_t handles{};
2407 int32_t num_handles{};
2408 int64_t timeout_ns{};
2409
2410 handles = Convert<uint64_t>(GetReg64(system, 1));
2411 num_handles = Convert<int32_t>(GetReg64(system, 2));
2412 timeout_ns = Convert<int64_t>(GetReg64(system, 3));
2413
2414 ret = WaitSynchronization64(system, &out_index, handles, num_handles, timeout_ns);
2415
2416 SetReg64(system, 0, Convert<uint64_t>(ret));
2417 SetReg64(system, 1, Convert<uint64_t>(out_index));
2438} 2418}
2439 2419
2440static Result ClearEvent32(Core::System& system, Handle event_handle) { 2420static void SvcWrap_CancelSynchronization64(Core::System& system) {
2441 return ClearEvent(system, event_handle); 2421 Result ret{};
2422
2423 Handle handle{};
2424
2425 handle = Convert<Handle>(GetReg64(system, 0));
2426
2427 ret = CancelSynchronization64(system, handle);
2428
2429 SetReg64(system, 0, Convert<uint64_t>(ret));
2442} 2430}
2443 2431
2444static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) { 2432static void SvcWrap_ArbitrateLock64(Core::System& system) {
2445 LOG_DEBUG(Kernel_SVC, "called"); 2433 Result ret{};
2446 2434
2447 // Get the kernel reference and handle table. 2435 Handle thread_handle{};
2448 auto& kernel = system.Kernel(); 2436 uint64_t address{};
2449 auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); 2437 uint32_t tag{};
2450 2438
2451 // Reserve a new event from the process resource limit 2439 thread_handle = Convert<Handle>(GetReg64(system, 0));
2452 KScopedResourceReservation event_reservation(kernel.CurrentProcess(), 2440 address = Convert<uint64_t>(GetReg64(system, 1));
2453 LimitableResource::EventCountMax); 2441 tag = Convert<uint32_t>(GetReg64(system, 2));
2454 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
2455 2442
2456 // Create a new event. 2443 ret = ArbitrateLock64(system, thread_handle, address, tag);
2457 KEvent* event = KEvent::Create(kernel);
2458 R_UNLESS(event != nullptr, ResultOutOfResource);
2459 2444
2460 // Initialize the event. 2445 SetReg64(system, 0, Convert<uint64_t>(ret));
2461 event->Initialize(kernel.CurrentProcess()); 2446}
2447
2448static void SvcWrap_ArbitrateUnlock64(Core::System& system) {
2449 Result ret{};
2462 2450
2463 // Commit the thread reservation. 2451 uint64_t address{};
2464 event_reservation.Commit();
2465 2452
2466 // Ensure that we clean up the event (and its only references are handle table) on function end. 2453 address = Convert<uint64_t>(GetReg64(system, 0));
2467 SCOPE_EXIT({
2468 event->GetReadableEvent().Close();
2469 event->Close();
2470 });
2471 2454
2472 // Register the event. 2455 ret = ArbitrateUnlock64(system, address);
2473 KEvent::Register(kernel, event); 2456
2457 SetReg64(system, 0, Convert<uint64_t>(ret));
2458}
2474 2459
2475 // Add the event to the handle table. 2460static void SvcWrap_WaitProcessWideKeyAtomic64(Core::System& system) {
2476 R_TRY(handle_table.Add(out_write, event)); 2461 Result ret{};
2477 2462
2478 // Ensure that we maintaing a clean handle state on exit. 2463 uint64_t address{};
2479 auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); }); 2464 uint64_t cv_key{};
2465 uint32_t tag{};
2466 int64_t timeout_ns{};
2480 2467
2481 // Add the readable event to the handle table. 2468 address = Convert<uint64_t>(GetReg64(system, 0));
2482 R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent()))); 2469 cv_key = Convert<uint64_t>(GetReg64(system, 1));
2470 tag = Convert<uint32_t>(GetReg64(system, 2));
2471 timeout_ns = Convert<int64_t>(GetReg64(system, 3));
2483 2472
2484 // We succeeded. 2473 ret = WaitProcessWideKeyAtomic64(system, address, cv_key, tag, timeout_ns);
2485 handle_guard.Cancel(); 2474
2486 return ResultSuccess; 2475 SetReg64(system, 0, Convert<uint64_t>(ret));
2487} 2476}
2488 2477
2489static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) { 2478static void SvcWrap_SignalProcessWideKey64(Core::System& system) {
2490 return CreateEvent(system, out_write, out_read); 2479 uint64_t cv_key{};
2480 int32_t count{};
2481
2482 cv_key = Convert<uint64_t>(GetReg64(system, 0));
2483 count = Convert<int32_t>(GetReg64(system, 1));
2484
2485 SignalProcessWideKey64(system, cv_key, count);
2491} 2486}
2492 2487
2493static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { 2488static void SvcWrap_GetSystemTick64(Core::System& system) {
2494 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); 2489 int64_t ret{};
2495 2490
2496 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); 2491 ret = GetSystemTick64(system);
2497 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
2498 if (process.IsNull()) {
2499 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
2500 process_handle);
2501 return ResultInvalidHandle;
2502 }
2503 2492
2504 const auto info_type = static_cast<ProcessInfoType>(type); 2493 SetReg64(system, 0, Convert<uint64_t>(ret));
2505 if (info_type != ProcessInfoType::ProcessState) { 2494}
2506 LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); 2495
2507 return ResultInvalidEnumValue; 2496static void SvcWrap_ConnectToNamedPort64(Core::System& system) {
2508 } 2497 Result ret{};
2509 2498
2510 *out = static_cast<u64>(process->GetState()); 2499 Handle out_handle{};
2511 return ResultSuccess; 2500 uint64_t name{};
2501
2502 name = Convert<uint64_t>(GetReg64(system, 1));
2503
2504 ret = ConnectToNamedPort64(system, &out_handle, name);
2505
2506 SetReg64(system, 0, Convert<uint64_t>(ret));
2507 SetReg64(system, 1, Convert<uint64_t>(out_handle));
2512} 2508}
2513 2509
2514static Result CreateResourceLimit(Core::System& system, Handle* out_handle) { 2510static void SvcWrap_SendSyncRequest64(Core::System& system) {
2515 LOG_DEBUG(Kernel_SVC, "called"); 2511 Result ret{};
2516 2512
2517 // Create a new resource limit. 2513 Handle session_handle{};
2518 auto& kernel = system.Kernel();
2519 KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
2520 R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
2521 2514
2522 // Ensure we don't leak a reference to the limit. 2515 session_handle = Convert<Handle>(GetReg64(system, 0));
2523 SCOPE_EXIT({ resource_limit->Close(); });
2524 2516
2525 // Initialize the resource limit. 2517 ret = SendSyncRequest64(system, session_handle);
2526 resource_limit->Initialize(&system.CoreTiming());
2527 2518
2528 // Register the limit. 2519 SetReg64(system, 0, Convert<uint64_t>(ret));
2529 KResourceLimit::Register(kernel, resource_limit); 2520}
2521
2522static void SvcWrap_SendSyncRequestWithUserBuffer64(Core::System& system) {
2523 Result ret{};
2524
2525 uint64_t message_buffer{};
2526 uint64_t message_buffer_size{};
2527 Handle session_handle{};
2528
2529 message_buffer = Convert<uint64_t>(GetReg64(system, 0));
2530 message_buffer_size = Convert<uint64_t>(GetReg64(system, 1));
2531 session_handle = Convert<Handle>(GetReg64(system, 2));
2530 2532
2531 // Add the limit to the handle table. 2533 ret = SendSyncRequestWithUserBuffer64(system, message_buffer, message_buffer_size, session_handle);
2532 R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit));
2533 2534
2534 return ResultSuccess; 2535 SetReg64(system, 0, Convert<uint64_t>(ret));
2535} 2536}
2536 2537
2537static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value, 2538static void SvcWrap_SendAsyncRequestWithUserBuffer64(Core::System& system) {
2538 Handle resource_limit_handle, LimitableResource which) { 2539 Result ret{};
2539 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
2540 which);
2541 2540
2542 // Validate the resource. 2541 Handle out_event_handle{};
2543 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); 2542 uint64_t message_buffer{};
2543 uint64_t message_buffer_size{};
2544 Handle session_handle{};
2544 2545
2545 // Get the resource limit. 2546 message_buffer = Convert<uint64_t>(GetReg64(system, 1));
2546 auto& kernel = system.Kernel(); 2547 message_buffer_size = Convert<uint64_t>(GetReg64(system, 2));
2547 KScopedAutoObject resource_limit = 2548 session_handle = Convert<Handle>(GetReg64(system, 3));
2548 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2549 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2550 2549
2551 // Get the limit value. 2550 ret = SendAsyncRequestWithUserBuffer64(system, &out_event_handle, message_buffer, message_buffer_size, session_handle);
2552 *out_limit_value = resource_limit->GetLimitValue(which);
2553 2551
2554 return ResultSuccess; 2552 SetReg64(system, 0, Convert<uint64_t>(ret));
2553 SetReg64(system, 1, Convert<uint64_t>(out_event_handle));
2555} 2554}
2556 2555
2557static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value, 2556static void SvcWrap_GetProcessId64(Core::System& system) {
2558 Handle resource_limit_handle, LimitableResource which) { 2557 Result ret{};
2559 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
2560 which);
2561 2558
2562 // Validate the resource. 2559 uint64_t out_process_id{};
2563 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); 2560 Handle process_handle{};
2564 2561
2565 // Get the resource limit. 2562 process_handle = Convert<Handle>(GetReg64(system, 1));
2566 auto& kernel = system.Kernel();
2567 KScopedAutoObject resource_limit =
2568 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2569 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2570 2563
2571 // Get the current value. 2564 ret = GetProcessId64(system, &out_process_id, process_handle);
2572 *out_current_value = resource_limit->GetCurrentValue(which);
2573 2565
2574 return ResultSuccess; 2566 SetReg64(system, 0, Convert<uint64_t>(ret));
2567 SetReg64(system, 1, Convert<uint64_t>(out_process_id));
2575} 2568}
2576 2569
2577static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, 2570static void SvcWrap_GetThreadId64(Core::System& system) {
2578 LimitableResource which, u64 limit_value) { 2571 Result ret{};
2579 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
2580 resource_limit_handle, which, limit_value);
2581 2572
2582 // Validate the resource. 2573 uint64_t out_thread_id{};
2583 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue); 2574 Handle thread_handle{};
2584 2575
2585 // Get the resource limit. 2576 thread_handle = Convert<Handle>(GetReg64(system, 1));
2586 auto& kernel = system.Kernel();
2587 KScopedAutoObject resource_limit =
2588 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
2589 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
2590 2577
2591 // Set the limit value. 2578 ret = GetThreadId64(system, &out_thread_id, thread_handle);
2592 R_TRY(resource_limit->SetLimitValue(which, limit_value));
2593 2579
2594 return ResultSuccess; 2580 SetReg64(system, 0, Convert<uint64_t>(ret));
2581 SetReg64(system, 1, Convert<uint64_t>(out_thread_id));
2595} 2582}
2596 2583
2597static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids, 2584static void SvcWrap_Break64(Core::System& system) {
2598 u32 out_process_ids_size) { 2585 BreakReason break_reason{};
2599 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}", 2586 uint64_t arg{};
2600 out_process_ids, out_process_ids_size); 2587 uint64_t size{};
2601 2588
2602 // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail. 2589 break_reason = Convert<BreakReason>(GetReg64(system, 0));
2603 if ((out_process_ids_size & 0xF0000000) != 0) { 2590 arg = Convert<uint64_t>(GetReg64(system, 1));
2604 LOG_ERROR(Kernel_SVC, 2591 size = Convert<uint64_t>(GetReg64(system, 2));
2605 "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
2606 out_process_ids_size);
2607 return ResultOutOfRange;
2608 }
2609 2592
2610 const auto& kernel = system.Kernel(); 2593 Break64(system, break_reason, arg, size);
2611 const auto total_copy_size = out_process_ids_size * sizeof(u64); 2594}
2612 2595
2613 if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace( 2596static void SvcWrap_OutputDebugString64(Core::System& system) {
2614 out_process_ids, total_copy_size)) { 2597 Result ret{};
2615 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
2616 out_process_ids, out_process_ids + total_copy_size);
2617 return ResultInvalidCurrentMemory;
2618 }
2619 2598
2620 auto& memory = system.Memory(); 2599 uint64_t debug_str{};
2621 const auto& process_list = kernel.GetProcessList(); 2600 uint64_t len{};
2622 const auto num_processes = process_list.size();
2623 const auto copy_amount = std::min(std::size_t{out_process_ids_size}, num_processes);
2624 2601
2625 for (std::size_t i = 0; i < copy_amount; ++i) { 2602 debug_str = Convert<uint64_t>(GetReg64(system, 0));
2626 memory.Write64(out_process_ids, process_list[i]->GetProcessID()); 2603 len = Convert<uint64_t>(GetReg64(system, 1));
2627 out_process_ids += sizeof(u64); 2604
2628 } 2605 ret = OutputDebugString64(system, debug_str, len);
2629 2606
2630 *out_num_processes = static_cast<u32>(num_processes); 2607 SetReg64(system, 0, Convert<uint64_t>(ret));
2631 return ResultSuccess;
2632} 2608}
2633 2609
2634static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids, 2610static void SvcWrap_ReturnFromException64(Core::System& system) {
2635 u32 out_thread_ids_size, Handle debug_handle) { 2611 Result result{};
2636 // TODO: Handle this case when debug events are supported.
2637 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
2638 2612
2639 LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}", 2613 result = Convert<Result>(GetReg64(system, 0));
2640 out_thread_ids, out_thread_ids_size);
2641 2614
2642 // If the size is negative or larger than INT32_MAX / sizeof(u64) 2615 ReturnFromException64(system, result);
2643 if ((out_thread_ids_size & 0xF0000000) != 0) { 2616}
2644 LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
2645 out_thread_ids_size);
2646 return ResultOutOfRange;
2647 }
2648 2617
2649 auto* const current_process = system.Kernel().CurrentProcess(); 2618static void SvcWrap_GetInfo64(Core::System& system) {
2650 const auto total_copy_size = out_thread_ids_size * sizeof(u64); 2619 Result ret{};
2651 2620
2652 if (out_thread_ids_size > 0 && 2621 uint64_t out{};
2653 !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) { 2622 InfoType info_type{};
2654 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", 2623 Handle handle{};
2655 out_thread_ids, out_thread_ids + total_copy_size); 2624 uint64_t info_subtype{};
2656 return ResultInvalidCurrentMemory;
2657 }
2658 2625
2659 auto& memory = system.Memory(); 2626 info_type = Convert<InfoType>(GetReg64(system, 1));
2660 const auto& thread_list = current_process->GetThreadList(); 2627 handle = Convert<Handle>(GetReg64(system, 2));
2661 const auto num_threads = thread_list.size(); 2628 info_subtype = Convert<uint64_t>(GetReg64(system, 3));
2662 const auto copy_amount = std::min(std::size_t{out_thread_ids_size}, num_threads);
2663 2629
2664 auto list_iter = thread_list.cbegin(); 2630 ret = GetInfo64(system, &out, info_type, handle, info_subtype);
2665 for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) { 2631
2666 memory.Write64(out_thread_ids, (*list_iter)->GetThreadID()); 2632 SetReg64(system, 0, Convert<uint64_t>(ret));
2667 out_thread_ids += sizeof(u64); 2633 SetReg64(system, 1, Convert<uint64_t>(out));
2668 } 2634}
2635
2636static void SvcWrap_FlushEntireDataCache64(Core::System& system) {
2637 FlushEntireDataCache64(system);
2638}
2639
2640static void SvcWrap_FlushDataCache64(Core::System& system) {
2641 Result ret{};
2642
2643 uint64_t address{};
2644 uint64_t size{};
2645
2646 address = Convert<uint64_t>(GetReg64(system, 0));
2647 size = Convert<uint64_t>(GetReg64(system, 1));
2648
2649 ret = FlushDataCache64(system, address, size);
2650
2651 SetReg64(system, 0, Convert<uint64_t>(ret));
2652}
2653
2654static void SvcWrap_MapPhysicalMemory64(Core::System& system) {
2655 Result ret{};
2656
2657 uint64_t address{};
2658 uint64_t size{};
2659
2660 address = Convert<uint64_t>(GetReg64(system, 0));
2661 size = Convert<uint64_t>(GetReg64(system, 1));
2662
2663 ret = MapPhysicalMemory64(system, address, size);
2664
2665 SetReg64(system, 0, Convert<uint64_t>(ret));
2666}
2667
2668static void SvcWrap_UnmapPhysicalMemory64(Core::System& system) {
2669 Result ret{};
2670
2671 uint64_t address{};
2672 uint64_t size{};
2673
2674 address = Convert<uint64_t>(GetReg64(system, 0));
2675 size = Convert<uint64_t>(GetReg64(system, 1));
2676
2677 ret = UnmapPhysicalMemory64(system, address, size);
2678
2679 SetReg64(system, 0, Convert<uint64_t>(ret));
2680}
2681
2682static void SvcWrap_GetDebugFutureThreadInfo64(Core::System& system) {
2683 Result ret{};
2684
2685 lp64::LastThreadContext out_context{};
2686 uint64_t out_thread_id{};
2687 Handle debug_handle{};
2688 int64_t ns{};
2689
2690 debug_handle = Convert<Handle>(GetReg64(system, 2));
2691 ns = Convert<int64_t>(GetReg64(system, 3));
2692
2693 ret = GetDebugFutureThreadInfo64(system, &out_context, &out_thread_id, debug_handle, ns);
2694
2695 SetReg64(system, 0, Convert<uint64_t>(ret));
2696 auto out_context_scatter = Convert<std::array<uint64_t, 4>>(out_context);
2697 SetReg64(system, 1, out_context_scatter[0]);
2698 SetReg64(system, 2, out_context_scatter[1]);
2699 SetReg64(system, 3, out_context_scatter[2]);
2700 SetReg64(system, 4, out_context_scatter[3]);
2701 SetReg64(system, 5, Convert<uint64_t>(out_thread_id));
2702}
2703
2704static void SvcWrap_GetLastThreadInfo64(Core::System& system) {
2705 Result ret{};
2706
2707 lp64::LastThreadContext out_context{};
2708 uintptr_t out_tls_address{};
2709 uint32_t out_flags{};
2710
2711 ret = GetLastThreadInfo64(system, &out_context, &out_tls_address, &out_flags);
2712
2713 SetReg64(system, 0, Convert<uint64_t>(ret));
2714 auto out_context_scatter = Convert<std::array<uint64_t, 4>>(out_context);
2715 SetReg64(system, 1, out_context_scatter[0]);
2716 SetReg64(system, 2, out_context_scatter[1]);
2717 SetReg64(system, 3, out_context_scatter[2]);
2718 SetReg64(system, 4, out_context_scatter[3]);
2719 SetReg64(system, 5, Convert<uint64_t>(out_tls_address));
2720 SetReg64(system, 6, Convert<uint64_t>(out_flags));
2721}
2722
2723static void SvcWrap_GetResourceLimitLimitValue64(Core::System& system) {
2724 Result ret{};
2725
2726 int64_t out_limit_value{};
2727 Handle resource_limit_handle{};
2728 LimitableResource which{};
2729
2730 resource_limit_handle = Convert<Handle>(GetReg64(system, 1));
2731 which = Convert<LimitableResource>(GetReg64(system, 2));
2732
2733 ret = GetResourceLimitLimitValue64(system, &out_limit_value, resource_limit_handle, which);
2734
2735 SetReg64(system, 0, Convert<uint64_t>(ret));
2736 SetReg64(system, 1, Convert<uint64_t>(out_limit_value));
2737}
2738
2739static void SvcWrap_GetResourceLimitCurrentValue64(Core::System& system) {
2740 Result ret{};
2741
2742 int64_t out_current_value{};
2743 Handle resource_limit_handle{};
2744 LimitableResource which{};
2745
2746 resource_limit_handle = Convert<Handle>(GetReg64(system, 1));
2747 which = Convert<LimitableResource>(GetReg64(system, 2));
2748
2749 ret = GetResourceLimitCurrentValue64(system, &out_current_value, resource_limit_handle, which);
2750
2751 SetReg64(system, 0, Convert<uint64_t>(ret));
2752 SetReg64(system, 1, Convert<uint64_t>(out_current_value));
2753}
2754
2755static void SvcWrap_SetThreadActivity64(Core::System& system) {
2756 Result ret{};
2757
2758 Handle thread_handle{};
2759 ThreadActivity thread_activity{};
2760
2761 thread_handle = Convert<Handle>(GetReg64(system, 0));
2762 thread_activity = Convert<ThreadActivity>(GetReg64(system, 1));
2763
2764 ret = SetThreadActivity64(system, thread_handle, thread_activity);
2765
2766 SetReg64(system, 0, Convert<uint64_t>(ret));
2767}
2768
2769static void SvcWrap_GetThreadContext364(Core::System& system) {
2770 Result ret{};
2771
2772 uint64_t out_context{};
2773 Handle thread_handle{};
2774
2775 out_context = Convert<uint64_t>(GetReg64(system, 0));
2776 thread_handle = Convert<Handle>(GetReg64(system, 1));
2777
2778 ret = GetThreadContext364(system, out_context, thread_handle);
2779
2780 SetReg64(system, 0, Convert<uint64_t>(ret));
2781}
2782
2783static void SvcWrap_WaitForAddress64(Core::System& system) {
2784 Result ret{};
2785
2786 uint64_t address{};
2787 ArbitrationType arb_type{};
2788 int32_t value{};
2789 int64_t timeout_ns{};
2790
2791 address = Convert<uint64_t>(GetReg64(system, 0));
2792 arb_type = Convert<ArbitrationType>(GetReg64(system, 1));
2793 value = Convert<int32_t>(GetReg64(system, 2));
2794 timeout_ns = Convert<int64_t>(GetReg64(system, 3));
2795
2796 ret = WaitForAddress64(system, address, arb_type, value, timeout_ns);
2797
2798 SetReg64(system, 0, Convert<uint64_t>(ret));
2799}
2800
2801static void SvcWrap_SignalToAddress64(Core::System& system) {
2802 Result ret{};
2803
2804 uint64_t address{};
2805 SignalType signal_type{};
2806 int32_t value{};
2807 int32_t count{};
2808
2809 address = Convert<uint64_t>(GetReg64(system, 0));
2810 signal_type = Convert<SignalType>(GetReg64(system, 1));
2811 value = Convert<int32_t>(GetReg64(system, 2));
2812 count = Convert<int32_t>(GetReg64(system, 3));
2813
2814 ret = SignalToAddress64(system, address, signal_type, value, count);
2815
2816 SetReg64(system, 0, Convert<uint64_t>(ret));
2817}
2818
2819static void SvcWrap_SynchronizePreemptionState64(Core::System& system) {
2820 SynchronizePreemptionState64(system);
2821}
2822
2823static void SvcWrap_GetResourceLimitPeakValue64(Core::System& system) {
2824 Result ret{};
2825
2826 int64_t out_peak_value{};
2827 Handle resource_limit_handle{};
2828 LimitableResource which{};
2829
2830 resource_limit_handle = Convert<Handle>(GetReg64(system, 1));
2831 which = Convert<LimitableResource>(GetReg64(system, 2));
2832
2833 ret = GetResourceLimitPeakValue64(system, &out_peak_value, resource_limit_handle, which);
2834
2835 SetReg64(system, 0, Convert<uint64_t>(ret));
2836 SetReg64(system, 1, Convert<uint64_t>(out_peak_value));
2837}
2838
2839static void SvcWrap_CreateIoPool64(Core::System& system) {
2840 Result ret{};
2841
2842 Handle out_handle{};
2843 IoPoolType which{};
2844
2845 which = Convert<IoPoolType>(GetReg64(system, 1));
2846
2847 ret = CreateIoPool64(system, &out_handle, which);
2848
2849 SetReg64(system, 0, Convert<uint64_t>(ret));
2850 SetReg64(system, 1, Convert<uint64_t>(out_handle));
2851}
2852
2853static void SvcWrap_CreateIoRegion64(Core::System& system) {
2854 Result ret{};
2855
2856 Handle out_handle{};
2857 Handle io_pool{};
2858 uint64_t physical_address{};
2859 uint64_t size{};
2860 MemoryMapping mapping{};
2861 MemoryPermission perm{};
2862
2863 io_pool = Convert<Handle>(GetReg64(system, 1));
2864 physical_address = Convert<uint64_t>(GetReg64(system, 2));
2865 size = Convert<uint64_t>(GetReg64(system, 3));
2866 mapping = Convert<MemoryMapping>(GetReg64(system, 4));
2867 perm = Convert<MemoryPermission>(GetReg64(system, 5));
2868
2869 ret = CreateIoRegion64(system, &out_handle, io_pool, physical_address, size, mapping, perm);
2870
2871 SetReg64(system, 0, Convert<uint64_t>(ret));
2872 SetReg64(system, 1, Convert<uint64_t>(out_handle));
2873}
2874
2875static void SvcWrap_KernelDebug64(Core::System& system) {
2876 KernelDebugType kern_debug_type{};
2877 uint64_t arg0{};
2878 uint64_t arg1{};
2879 uint64_t arg2{};
2880
2881 kern_debug_type = Convert<KernelDebugType>(GetReg64(system, 0));
2882 arg0 = Convert<uint64_t>(GetReg64(system, 1));
2883 arg1 = Convert<uint64_t>(GetReg64(system, 2));
2884 arg2 = Convert<uint64_t>(GetReg64(system, 3));
2885
2886 KernelDebug64(system, kern_debug_type, arg0, arg1, arg2);
2887}
2888
2889static void SvcWrap_ChangeKernelTraceState64(Core::System& system) {
2890 KernelTraceState kern_trace_state{};
2891
2892 kern_trace_state = Convert<KernelTraceState>(GetReg64(system, 0));
2893
2894 ChangeKernelTraceState64(system, kern_trace_state);
2895}
2669 2896
2670 *out_num_threads = static_cast<u32>(num_threads); 2897static void SvcWrap_CreateSession64(Core::System& system) {
2671 return ResultSuccess; 2898 Result ret{};
2672} 2899
2673 2900 Handle out_server_session_handle{};
2674static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, 2901 Handle out_client_session_handle{};
2675 u64 size) { 2902 bool is_light{};
2676 // Validate address/size. 2903 uint64_t name{};
2677 R_UNLESS(size > 0, ResultInvalidSize); 2904
2678 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); 2905 is_light = Convert<bool>(GetReg64(system, 2));
2679 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); 2906 name = Convert<uint64_t>(GetReg64(system, 3));
2680 2907
2681 // Get the process from its handle. 2908 ret = CreateSession64(system, &out_server_session_handle, &out_client_session_handle, is_light, name);
2682 KScopedAutoObject process = 2909
2683 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); 2910 SetReg64(system, 0, Convert<uint64_t>(ret));
2684 R_UNLESS(process.IsNotNull(), ResultInvalidHandle); 2911 SetReg64(system, 1, Convert<uint64_t>(out_server_session_handle));
2685 2912 SetReg64(system, 2, Convert<uint64_t>(out_client_session_handle));
2686 // Verify the region is within range. 2913}
2687 auto& page_table = process->PageTable(); 2914
2688 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); 2915static void SvcWrap_AcceptSession64(Core::System& system) {
2689 2916 Result ret{};
2690 // Perform the operation. 2917
2691 R_RETURN(system.Memory().FlushDataCache(*process, address, size)); 2918 Handle out_handle{};
2692} 2919 Handle port{};
2693 2920
2694namespace { 2921 port = Convert<Handle>(GetReg64(system, 1));
2695struct FunctionDef { 2922
2696 using Func = void(Core::System&); 2923 ret = AcceptSession64(system, &out_handle, port);
2697 2924
2698 u32 id; 2925 SetReg64(system, 0, Convert<uint64_t>(ret));
2699 Func* func; 2926 SetReg64(system, 1, Convert<uint64_t>(out_handle));
2700 const char* name; 2927}
2701}; 2928
2702} // namespace 2929static void SvcWrap_ReplyAndReceive64(Core::System& system) {
2703 2930 Result ret{};
2704static const FunctionDef SVC_Table_32[] = { 2931
2705 {0x00, nullptr, "Unknown0"}, 2932 int32_t out_index{};
2706 {0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"}, 2933 uint64_t handles{};
2707 {0x02, nullptr, "SetMemoryPermission32"}, 2934 int32_t num_handles{};
2708 {0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"}, 2935 Handle reply_target{};
2709 {0x04, SvcWrap32<MapMemory32>, "MapMemory32"}, 2936 int64_t timeout_ns{};
2710 {0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"}, 2937
2711 {0x06, SvcWrap32<QueryMemory32>, "QueryMemory32"}, 2938 handles = Convert<uint64_t>(GetReg64(system, 1));
2712 {0x07, SvcWrap32<ExitProcess32>, "ExitProcess32"}, 2939 num_handles = Convert<int32_t>(GetReg64(system, 2));
2713 {0x08, SvcWrap32<CreateThread32>, "CreateThread32"}, 2940 reply_target = Convert<Handle>(GetReg64(system, 3));
2714 {0x09, SvcWrap32<StartThread32>, "StartThread32"}, 2941 timeout_ns = Convert<int64_t>(GetReg64(system, 4));
2715 {0x0a, SvcWrap32<ExitThread32>, "ExitThread32"}, 2942
2716 {0x0b, SvcWrap32<SleepThread32>, "SleepThread32"}, 2943 ret = ReplyAndReceive64(system, &out_index, handles, num_handles, reply_target, timeout_ns);
2717 {0x0c, SvcWrap32<GetThreadPriority32>, "GetThreadPriority32"}, 2944
2718 {0x0d, SvcWrap32<SetThreadPriority32>, "SetThreadPriority32"}, 2945 SetReg64(system, 0, Convert<uint64_t>(ret));
2719 {0x0e, SvcWrap32<GetThreadCoreMask32>, "GetThreadCoreMask32"}, 2946 SetReg64(system, 1, Convert<uint64_t>(out_index));
2720 {0x0f, SvcWrap32<SetThreadCoreMask32>, "SetThreadCoreMask32"}, 2947}
2721 {0x10, SvcWrap32<GetCurrentProcessorNumber32>, "GetCurrentProcessorNumber32"}, 2948
2722 {0x11, SvcWrap32<SignalEvent32>, "SignalEvent32"}, 2949static void SvcWrap_ReplyAndReceiveWithUserBuffer64(Core::System& system) {
2723 {0x12, SvcWrap32<ClearEvent32>, "ClearEvent32"}, 2950 Result ret{};
2724 {0x13, SvcWrap32<MapSharedMemory32>, "MapSharedMemory32"}, 2951
2725 {0x14, SvcWrap32<UnmapSharedMemory32>, "UnmapSharedMemory32"}, 2952 int32_t out_index{};
2726 {0x15, SvcWrap32<CreateTransferMemory32>, "CreateTransferMemory32"}, 2953 uint64_t message_buffer{};
2727 {0x16, SvcWrap32<CloseHandle32>, "CloseHandle32"}, 2954 uint64_t message_buffer_size{};
2728 {0x17, SvcWrap32<ResetSignal32>, "ResetSignal32"}, 2955 uint64_t handles{};
2729 {0x18, SvcWrap32<WaitSynchronization32>, "WaitSynchronization32"}, 2956 int32_t num_handles{};
2730 {0x19, SvcWrap32<CancelSynchronization32>, "CancelSynchronization32"}, 2957 Handle reply_target{};
2731 {0x1a, SvcWrap32<ArbitrateLock32>, "ArbitrateLock32"}, 2958 int64_t timeout_ns{};
2732 {0x1b, SvcWrap32<ArbitrateUnlock32>, "ArbitrateUnlock32"}, 2959
2733 {0x1c, SvcWrap32<WaitProcessWideKeyAtomic32>, "WaitProcessWideKeyAtomic32"}, 2960 message_buffer = Convert<uint64_t>(GetReg64(system, 1));
2734 {0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"}, 2961 message_buffer_size = Convert<uint64_t>(GetReg64(system, 2));
2735 {0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"}, 2962 handles = Convert<uint64_t>(GetReg64(system, 3));
2736 {0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"}, 2963 num_handles = Convert<int32_t>(GetReg64(system, 4));
2737 {0x20, nullptr, "SendSyncRequestLight32"}, 2964 reply_target = Convert<Handle>(GetReg64(system, 5));
2738 {0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"}, 2965 timeout_ns = Convert<int64_t>(GetReg64(system, 6));
2739 {0x22, nullptr, "SendSyncRequestWithUserBuffer32"}, 2966
2740 {0x23, nullptr, "SendAsyncRequestWithUserBuffer32"}, 2967 ret = ReplyAndReceiveWithUserBuffer64(system, &out_index, message_buffer, message_buffer_size, handles, num_handles, reply_target, timeout_ns);
2741 {0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"}, 2968
2742 {0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"}, 2969 SetReg64(system, 0, Convert<uint64_t>(ret));
2743 {0x26, SvcWrap32<Break32>, "Break32"}, 2970 SetReg64(system, 1, Convert<uint64_t>(out_index));
2744 {0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"}, 2971}
2745 {0x28, nullptr, "ReturnFromException32"}, 2972
2746 {0x29, SvcWrap32<GetInfo32>, "GetInfo32"}, 2973static void SvcWrap_CreateEvent64(Core::System& system) {
2747 {0x2a, nullptr, "FlushEntireDataCache32"}, 2974 Result ret{};
2748 {0x2b, nullptr, "FlushDataCache32"}, 2975
2749 {0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"}, 2976 Handle out_write_handle{};
2750 {0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"}, 2977 Handle out_read_handle{};
2751 {0x2e, nullptr, "GetDebugFutureThreadInfo32"}, 2978
2752 {0x2f, nullptr, "GetLastThreadInfo32"}, 2979 ret = CreateEvent64(system, &out_write_handle, &out_read_handle);
2753 {0x30, nullptr, "GetResourceLimitLimitValue32"}, 2980
2754 {0x31, nullptr, "GetResourceLimitCurrentValue32"}, 2981 SetReg64(system, 0, Convert<uint64_t>(ret));
2755 {0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"}, 2982 SetReg64(system, 1, Convert<uint64_t>(out_write_handle));
2756 {0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"}, 2983 SetReg64(system, 2, Convert<uint64_t>(out_read_handle));
2757 {0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"}, 2984}
2758 {0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"}, 2985
2759 {0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"}, 2986static void SvcWrap_MapIoRegion64(Core::System& system) {
2760 {0x37, nullptr, "GetResourceLimitPeakValue32"}, 2987 Result ret{};
2761 {0x38, nullptr, "Unknown38"}, 2988
2762 {0x39, nullptr, "CreateIoPool32"}, 2989 Handle io_region{};
2763 {0x3a, nullptr, "CreateIoRegion32"}, 2990 uint64_t address{};
2764 {0x3b, nullptr, "Unknown3b"}, 2991 uint64_t size{};
2765 {0x3c, nullptr, "KernelDebug32"}, 2992 MemoryPermission perm{};
2766 {0x3d, nullptr, "ChangeKernelTraceState32"}, 2993
2767 {0x3e, nullptr, "Unknown3e"}, 2994 io_region = Convert<Handle>(GetReg64(system, 0));
2768 {0x3f, nullptr, "Unknown3f"}, 2995 address = Convert<uint64_t>(GetReg64(system, 1));
2769 {0x40, nullptr, "CreateSession32"}, 2996 size = Convert<uint64_t>(GetReg64(system, 2));
2770 {0x41, nullptr, "AcceptSession32"}, 2997 perm = Convert<MemoryPermission>(GetReg64(system, 3));
2771 {0x42, nullptr, "ReplyAndReceiveLight32"}, 2998
2772 {0x43, nullptr, "ReplyAndReceive32"}, 2999 ret = MapIoRegion64(system, io_region, address, size, perm);
2773 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"}, 3000
2774 {0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"}, 3001 SetReg64(system, 0, Convert<uint64_t>(ret));
2775 {0x46, nullptr, "MapIoRegion32"}, 3002}
2776 {0x47, nullptr, "UnmapIoRegion32"}, 3003
2777 {0x48, nullptr, "MapPhysicalMemoryUnsafe32"}, 3004static void SvcWrap_UnmapIoRegion64(Core::System& system) {
2778 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"}, 3005 Result ret{};
2779 {0x4a, nullptr, "SetUnsafeLimit32"}, 3006
2780 {0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"}, 3007 Handle io_region{};
2781 {0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"}, 3008 uint64_t address{};
2782 {0x4d, nullptr, "SleepSystem32"}, 3009 uint64_t size{};
2783 {0x4e, nullptr, "ReadWriteRegister32"}, 3010
2784 {0x4f, nullptr, "SetProcessActivity32"}, 3011 io_region = Convert<Handle>(GetReg64(system, 0));
2785 {0x50, nullptr, "CreateSharedMemory32"}, 3012 address = Convert<uint64_t>(GetReg64(system, 1));
2786 {0x51, nullptr, "MapTransferMemory32"}, 3013 size = Convert<uint64_t>(GetReg64(system, 2));
2787 {0x52, nullptr, "UnmapTransferMemory32"}, 3014
2788 {0x53, nullptr, "CreateInterruptEvent32"}, 3015 ret = UnmapIoRegion64(system, io_region, address, size);
2789 {0x54, nullptr, "QueryPhysicalAddress32"}, 3016
2790 {0x55, nullptr, "QueryIoMapping32"}, 3017 SetReg64(system, 0, Convert<uint64_t>(ret));
2791 {0x56, nullptr, "CreateDeviceAddressSpace32"}, 3018}
2792 {0x57, nullptr, "AttachDeviceAddressSpace32"}, 3019
2793 {0x58, nullptr, "DetachDeviceAddressSpace32"}, 3020static void SvcWrap_MapPhysicalMemoryUnsafe64(Core::System& system) {
2794 {0x59, nullptr, "MapDeviceAddressSpaceByForce32"}, 3021 Result ret{};
2795 {0x5a, nullptr, "MapDeviceAddressSpaceAligned32"}, 3022
2796 {0x5b, nullptr, "MapDeviceAddressSpace32"}, 3023 uint64_t address{};
2797 {0x5c, nullptr, "UnmapDeviceAddressSpace32"}, 3024 uint64_t size{};
2798 {0x5d, nullptr, "InvalidateProcessDataCache32"}, 3025
2799 {0x5e, nullptr, "StoreProcessDataCache32"}, 3026 address = Convert<uint64_t>(GetReg64(system, 0));
2800 {0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"}, 3027 size = Convert<uint64_t>(GetReg64(system, 1));
2801 {0x60, nullptr, "StoreProcessDataCache32"}, 3028
2802 {0x61, nullptr, "BreakDebugProcess32"}, 3029 ret = MapPhysicalMemoryUnsafe64(system, address, size);
2803 {0x62, nullptr, "TerminateDebugProcess32"}, 3030
2804 {0x63, nullptr, "GetDebugEvent32"}, 3031 SetReg64(system, 0, Convert<uint64_t>(ret));
2805 {0x64, nullptr, "ContinueDebugEvent32"}, 3032}
2806 {0x65, nullptr, "GetProcessList32"}, 3033
2807 {0x66, nullptr, "GetThreadList"}, 3034static void SvcWrap_UnmapPhysicalMemoryUnsafe64(Core::System& system) {
2808 {0x67, nullptr, "GetDebugThreadContext32"}, 3035 Result ret{};
2809 {0x68, nullptr, "SetDebugThreadContext32"}, 3036
2810 {0x69, nullptr, "QueryDebugProcessMemory32"}, 3037 uint64_t address{};
2811 {0x6A, nullptr, "ReadDebugProcessMemory32"}, 3038 uint64_t size{};
2812 {0x6B, nullptr, "WriteDebugProcessMemory32"}, 3039
2813 {0x6C, nullptr, "SetHardwareBreakPoint32"}, 3040 address = Convert<uint64_t>(GetReg64(system, 0));
2814 {0x6D, nullptr, "GetDebugThreadParam32"}, 3041 size = Convert<uint64_t>(GetReg64(system, 1));
2815 {0x6E, nullptr, "Unknown6E"}, 3042
2816 {0x6f, nullptr, "GetSystemInfo32"}, 3043 ret = UnmapPhysicalMemoryUnsafe64(system, address, size);
2817 {0x70, nullptr, "CreatePort32"}, 3044
2818 {0x71, nullptr, "ManageNamedPort32"}, 3045 SetReg64(system, 0, Convert<uint64_t>(ret));
2819 {0x72, nullptr, "ConnectToPort32"}, 3046}
2820 {0x73, nullptr, "SetProcessMemoryPermission32"}, 3047
2821 {0x74, nullptr, "MapProcessMemory32"}, 3048static void SvcWrap_SetUnsafeLimit64(Core::System& system) {
2822 {0x75, nullptr, "UnmapProcessMemory32"}, 3049 Result ret{};
2823 {0x76, nullptr, "QueryProcessMemory32"}, 3050
2824 {0x77, nullptr, "MapProcessCodeMemory32"}, 3051 uint64_t limit{};
2825 {0x78, nullptr, "UnmapProcessCodeMemory32"}, 3052
2826 {0x79, nullptr, "CreateProcess32"}, 3053 limit = Convert<uint64_t>(GetReg64(system, 0));
2827 {0x7A, nullptr, "StartProcess32"}, 3054
2828 {0x7B, nullptr, "TerminateProcess32"}, 3055 ret = SetUnsafeLimit64(system, limit);
2829 {0x7C, nullptr, "GetProcessInfo32"}, 3056
2830 {0x7D, nullptr, "CreateResourceLimit32"}, 3057 SetReg64(system, 0, Convert<uint64_t>(ret));
2831 {0x7E, nullptr, "SetResourceLimitLimitValue32"}, 3058}
2832 {0x7F, nullptr, "CallSecureMonitor32"}, 3059
2833 {0x80, nullptr, "Unknown"}, 3060static void SvcWrap_CreateCodeMemory64(Core::System& system) {
2834 {0x81, nullptr, "Unknown"}, 3061 Result ret{};
2835 {0x82, nullptr, "Unknown"}, 3062
2836 {0x83, nullptr, "Unknown"}, 3063 Handle out_handle{};
2837 {0x84, nullptr, "Unknown"}, 3064 uint64_t address{};
2838 {0x85, nullptr, "Unknown"}, 3065 uint64_t size{};
2839 {0x86, nullptr, "Unknown"}, 3066
2840 {0x87, nullptr, "Unknown"}, 3067 address = Convert<uint64_t>(GetReg64(system, 1));
2841 {0x88, nullptr, "Unknown"}, 3068 size = Convert<uint64_t>(GetReg64(system, 2));
2842 {0x89, nullptr, "Unknown"}, 3069
2843 {0x8A, nullptr, "Unknown"}, 3070 ret = CreateCodeMemory64(system, &out_handle, address, size);
2844 {0x8B, nullptr, "Unknown"}, 3071
2845 {0x8C, nullptr, "Unknown"}, 3072 SetReg64(system, 0, Convert<uint64_t>(ret));
2846 {0x8D, nullptr, "Unknown"}, 3073 SetReg64(system, 1, Convert<uint64_t>(out_handle));
2847 {0x8E, nullptr, "Unknown"}, 3074}
2848 {0x8F, nullptr, "Unknown"}, 3075
2849 {0x90, nullptr, "Unknown"}, 3076static void SvcWrap_ControlCodeMemory64(Core::System& system) {
2850 {0x91, nullptr, "Unknown"}, 3077 Result ret{};
2851 {0x92, nullptr, "Unknown"}, 3078
2852 {0x93, nullptr, "Unknown"}, 3079 Handle code_memory_handle{};
2853 {0x94, nullptr, "Unknown"}, 3080 CodeMemoryOperation operation{};
2854 {0x95, nullptr, "Unknown"}, 3081 uint64_t address{};
2855 {0x96, nullptr, "Unknown"}, 3082 uint64_t size{};
2856 {0x97, nullptr, "Unknown"}, 3083 MemoryPermission perm{};
2857 {0x98, nullptr, "Unknown"}, 3084
2858 {0x99, nullptr, "Unknown"}, 3085 code_memory_handle = Convert<Handle>(GetReg64(system, 0));
2859 {0x9A, nullptr, "Unknown"}, 3086 operation = Convert<CodeMemoryOperation>(GetReg64(system, 1));
2860 {0x9B, nullptr, "Unknown"}, 3087 address = Convert<uint64_t>(GetReg64(system, 2));
2861 {0x9C, nullptr, "Unknown"}, 3088 size = Convert<uint64_t>(GetReg64(system, 3));
2862 {0x9D, nullptr, "Unknown"}, 3089 perm = Convert<MemoryPermission>(GetReg64(system, 4));
2863 {0x9E, nullptr, "Unknown"}, 3090
2864 {0x9F, nullptr, "Unknown"}, 3091 ret = ControlCodeMemory64(system, code_memory_handle, operation, address, size, perm);
2865 {0xA0, nullptr, "Unknown"}, 3092
2866 {0xA1, nullptr, "Unknown"}, 3093 SetReg64(system, 0, Convert<uint64_t>(ret));
2867 {0xA2, nullptr, "Unknown"}, 3094}
2868 {0xA3, nullptr, "Unknown"}, 3095
2869 {0xA4, nullptr, "Unknown"}, 3096static void SvcWrap_SleepSystem64(Core::System& system) {
2870 {0xA5, nullptr, "Unknown"}, 3097 SleepSystem64(system);
2871 {0xA6, nullptr, "Unknown"}, 3098}
2872 {0xA7, nullptr, "Unknown"}, 3099
2873 {0xA8, nullptr, "Unknown"}, 3100static void SvcWrap_ReadWriteRegister64(Core::System& system) {
2874 {0xA9, nullptr, "Unknown"}, 3101 Result ret{};
2875 {0xAA, nullptr, "Unknown"}, 3102
2876 {0xAB, nullptr, "Unknown"}, 3103 uint32_t out_value{};
2877 {0xAC, nullptr, "Unknown"}, 3104 uint64_t address{};
2878 {0xAD, nullptr, "Unknown"}, 3105 uint32_t mask{};
2879 {0xAE, nullptr, "Unknown"}, 3106 uint32_t value{};
2880 {0xAF, nullptr, "Unknown"}, 3107
2881 {0xB0, nullptr, "Unknown"}, 3108 address = Convert<uint64_t>(GetReg64(system, 1));
2882 {0xB1, nullptr, "Unknown"}, 3109 mask = Convert<uint32_t>(GetReg64(system, 2));
2883 {0xB2, nullptr, "Unknown"}, 3110 value = Convert<uint32_t>(GetReg64(system, 3));
2884 {0xB3, nullptr, "Unknown"}, 3111
2885 {0xB4, nullptr, "Unknown"}, 3112 ret = ReadWriteRegister64(system, &out_value, address, mask, value);
2886 {0xB5, nullptr, "Unknown"}, 3113
2887 {0xB6, nullptr, "Unknown"}, 3114 SetReg64(system, 0, Convert<uint64_t>(ret));
2888 {0xB7, nullptr, "Unknown"}, 3115 SetReg64(system, 1, Convert<uint64_t>(out_value));
2889 {0xB8, nullptr, "Unknown"}, 3116}
2890 {0xB9, nullptr, "Unknown"}, 3117
2891 {0xBA, nullptr, "Unknown"}, 3118static void SvcWrap_SetProcessActivity64(Core::System& system) {
2892 {0xBB, nullptr, "Unknown"}, 3119 Result ret{};
2893 {0xBC, nullptr, "Unknown"}, 3120
2894 {0xBD, nullptr, "Unknown"}, 3121 Handle process_handle{};
2895 {0xBE, nullptr, "Unknown"}, 3122 ProcessActivity process_activity{};
2896 {0xBF, nullptr, "Unknown"}, 3123
2897}; 3124 process_handle = Convert<Handle>(GetReg64(system, 0));
2898 3125 process_activity = Convert<ProcessActivity>(GetReg64(system, 1));
2899static const FunctionDef SVC_Table_64[] = { 3126
2900 {0x00, nullptr, "Unknown0"}, 3127 ret = SetProcessActivity64(system, process_handle, process_activity);
2901 {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, 3128
2902 {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"}, 3129 SetReg64(system, 0, Convert<uint64_t>(ret));
2903 {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"}, 3130}
2904 {0x04, SvcWrap64<MapMemory>, "MapMemory"}, 3131
2905 {0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"}, 3132static void SvcWrap_CreateSharedMemory64(Core::System& system) {
2906 {0x06, SvcWrap64<QueryMemory>, "QueryMemory"}, 3133 Result ret{};
2907 {0x07, SvcWrap64<ExitProcess>, "ExitProcess"}, 3134
2908 {0x08, SvcWrap64<CreateThread>, "CreateThread"}, 3135 Handle out_handle{};
2909 {0x09, SvcWrap64<StartThread>, "StartThread"}, 3136 uint64_t size{};
2910 {0x0A, SvcWrap64<ExitThread>, "ExitThread"}, 3137 MemoryPermission owner_perm{};
2911 {0x0B, SvcWrap64<SleepThread>, "SleepThread"}, 3138 MemoryPermission remote_perm{};
2912 {0x0C, SvcWrap64<GetThreadPriority>, "GetThreadPriority"}, 3139
2913 {0x0D, SvcWrap64<SetThreadPriority>, "SetThreadPriority"}, 3140 size = Convert<uint64_t>(GetReg64(system, 1));
2914 {0x0E, SvcWrap64<GetThreadCoreMask>, "GetThreadCoreMask"}, 3141 owner_perm = Convert<MemoryPermission>(GetReg64(system, 2));
2915 {0x0F, SvcWrap64<SetThreadCoreMask>, "SetThreadCoreMask"}, 3142 remote_perm = Convert<MemoryPermission>(GetReg64(system, 3));
2916 {0x10, SvcWrap64<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"}, 3143
2917 {0x11, SvcWrap64<SignalEvent>, "SignalEvent"}, 3144 ret = CreateSharedMemory64(system, &out_handle, size, owner_perm, remote_perm);
2918 {0x12, SvcWrap64<ClearEvent>, "ClearEvent"}, 3145
2919 {0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"}, 3146 SetReg64(system, 0, Convert<uint64_t>(ret));
2920 {0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"}, 3147 SetReg64(system, 1, Convert<uint64_t>(out_handle));
2921 {0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"}, 3148}
2922 {0x16, SvcWrap64<CloseHandle>, "CloseHandle"}, 3149
2923 {0x17, SvcWrap64<ResetSignal>, "ResetSignal"}, 3150static void SvcWrap_MapTransferMemory64(Core::System& system) {
2924 {0x18, SvcWrap64<WaitSynchronization>, "WaitSynchronization"}, 3151 Result ret{};
2925 {0x19, SvcWrap64<CancelSynchronization>, "CancelSynchronization"}, 3152
2926 {0x1A, SvcWrap64<ArbitrateLock>, "ArbitrateLock"}, 3153 Handle trmem_handle{};
2927 {0x1B, SvcWrap64<ArbitrateUnlock>, "ArbitrateUnlock"}, 3154 uint64_t address{};
2928 {0x1C, SvcWrap64<WaitProcessWideKeyAtomic>, "WaitProcessWideKeyAtomic"}, 3155 uint64_t size{};
2929 {0x1D, SvcWrap64<SignalProcessWideKey>, "SignalProcessWideKey"}, 3156 MemoryPermission owner_perm{};
2930 {0x1E, SvcWrap64<GetSystemTick>, "GetSystemTick"}, 3157
2931 {0x1F, SvcWrap64<ConnectToNamedPort>, "ConnectToNamedPort"}, 3158 trmem_handle = Convert<Handle>(GetReg64(system, 0));
2932 {0x20, nullptr, "SendSyncRequestLight"}, 3159 address = Convert<uint64_t>(GetReg64(system, 1));
2933 {0x21, SvcWrap64<SendSyncRequest>, "SendSyncRequest"}, 3160 size = Convert<uint64_t>(GetReg64(system, 2));
2934 {0x22, nullptr, "SendSyncRequestWithUserBuffer"}, 3161 owner_perm = Convert<MemoryPermission>(GetReg64(system, 3));
2935 {0x23, nullptr, "SendAsyncRequestWithUserBuffer"}, 3162
2936 {0x24, SvcWrap64<GetProcessId>, "GetProcessId"}, 3163 ret = MapTransferMemory64(system, trmem_handle, address, size, owner_perm);
2937 {0x25, SvcWrap64<GetThreadId>, "GetThreadId"}, 3164
2938 {0x26, SvcWrap64<Break>, "Break"}, 3165 SetReg64(system, 0, Convert<uint64_t>(ret));
2939 {0x27, SvcWrap64<OutputDebugString>, "OutputDebugString"}, 3166}
2940 {0x28, nullptr, "ReturnFromException"}, 3167
2941 {0x29, SvcWrap64<GetInfo>, "GetInfo"}, 3168static void SvcWrap_UnmapTransferMemory64(Core::System& system) {
2942 {0x2A, nullptr, "FlushEntireDataCache"}, 3169 Result ret{};
2943 {0x2B, nullptr, "FlushDataCache"}, 3170
2944 {0x2C, SvcWrap64<MapPhysicalMemory>, "MapPhysicalMemory"}, 3171 Handle trmem_handle{};
2945 {0x2D, SvcWrap64<UnmapPhysicalMemory>, "UnmapPhysicalMemory"}, 3172 uint64_t address{};
2946 {0x2E, nullptr, "GetFutureThreadInfo"}, 3173 uint64_t size{};
2947 {0x2F, nullptr, "GetLastThreadInfo"}, 3174
2948 {0x30, SvcWrap64<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"}, 3175 trmem_handle = Convert<Handle>(GetReg64(system, 0));
2949 {0x31, SvcWrap64<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"}, 3176 address = Convert<uint64_t>(GetReg64(system, 1));
2950 {0x32, SvcWrap64<SetThreadActivity>, "SetThreadActivity"}, 3177 size = Convert<uint64_t>(GetReg64(system, 2));
2951 {0x33, SvcWrap64<GetThreadContext>, "GetThreadContext"}, 3178
2952 {0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"}, 3179 ret = UnmapTransferMemory64(system, trmem_handle, address, size);
2953 {0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"}, 3180
2954 {0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"}, 3181 SetReg64(system, 0, Convert<uint64_t>(ret));
2955 {0x37, nullptr, "GetResourceLimitPeakValue"}, 3182}
2956 {0x38, nullptr, "Unknown38"}, 3183
2957 {0x39, nullptr, "CreateIoPool"}, 3184static void SvcWrap_CreateInterruptEvent64(Core::System& system) {
2958 {0x3A, nullptr, "CreateIoRegion"}, 3185 Result ret{};
2959 {0x3B, nullptr, "Unknown3B"}, 3186
2960 {0x3C, SvcWrap64<KernelDebug>, "KernelDebug"}, 3187 Handle out_read_handle{};
2961 {0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"}, 3188 int32_t interrupt_id{};
2962 {0x3E, nullptr, "Unknown3e"}, 3189 InterruptType interrupt_type{};
2963 {0x3F, nullptr, "Unknown3f"}, 3190
2964 {0x40, SvcWrap64<CreateSession>, "CreateSession"}, 3191 interrupt_id = Convert<int32_t>(GetReg64(system, 1));
2965 {0x41, nullptr, "AcceptSession"}, 3192 interrupt_type = Convert<InterruptType>(GetReg64(system, 2));
2966 {0x42, nullptr, "ReplyAndReceiveLight"}, 3193
2967 {0x43, SvcWrap64<ReplyAndReceive>, "ReplyAndReceive"}, 3194 ret = CreateInterruptEvent64(system, &out_read_handle, interrupt_id, interrupt_type);
2968 {0x44, nullptr, "ReplyAndReceiveWithUserBuffer"}, 3195
2969 {0x45, SvcWrap64<CreateEvent>, "CreateEvent"}, 3196 SetReg64(system, 0, Convert<uint64_t>(ret));
2970 {0x46, nullptr, "MapIoRegion"}, 3197 SetReg64(system, 1, Convert<uint64_t>(out_read_handle));
2971 {0x47, nullptr, "UnmapIoRegion"}, 3198}
2972 {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, 3199
2973 {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, 3200static void SvcWrap_QueryPhysicalAddress64(Core::System& system) {
2974 {0x4A, nullptr, "SetUnsafeLimit"}, 3201 Result ret{};
2975 {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"}, 3202
2976 {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"}, 3203 lp64::PhysicalMemoryInfo out_info{};
2977 {0x4D, nullptr, "SleepSystem"}, 3204 uint64_t address{};
2978 {0x4E, nullptr, "ReadWriteRegister"}, 3205
2979 {0x4F, nullptr, "SetProcessActivity"}, 3206 address = Convert<uint64_t>(GetReg64(system, 1));
2980 {0x50, nullptr, "CreateSharedMemory"}, 3207
2981 {0x51, nullptr, "MapTransferMemory"}, 3208 ret = QueryPhysicalAddress64(system, &out_info, address);
2982 {0x52, nullptr, "UnmapTransferMemory"}, 3209
2983 {0x53, nullptr, "CreateInterruptEvent"}, 3210 SetReg64(system, 0, Convert<uint64_t>(ret));
2984 {0x54, nullptr, "QueryPhysicalAddress"}, 3211 auto out_info_scatter = Convert<std::array<uint64_t, 3>>(out_info);
2985 {0x55, nullptr, "QueryIoMapping"}, 3212 SetReg64(system, 1, out_info_scatter[0]);
2986 {0x56, nullptr, "CreateDeviceAddressSpace"}, 3213 SetReg64(system, 2, out_info_scatter[1]);
2987 {0x57, nullptr, "AttachDeviceAddressSpace"}, 3214 SetReg64(system, 3, out_info_scatter[2]);
2988 {0x58, nullptr, "DetachDeviceAddressSpace"}, 3215}
2989 {0x59, nullptr, "MapDeviceAddressSpaceByForce"}, 3216
2990 {0x5A, nullptr, "MapDeviceAddressSpaceAligned"}, 3217static void SvcWrap_QueryIoMapping64(Core::System& system) {
2991 {0x5B, nullptr, "MapDeviceAddressSpace"}, 3218 Result ret{};
2992 {0x5C, nullptr, "UnmapDeviceAddressSpace"}, 3219
2993 {0x5D, nullptr, "InvalidateProcessDataCache"}, 3220 uintptr_t out_address{};
2994 {0x5E, nullptr, "StoreProcessDataCache"}, 3221 uintptr_t out_size{};
2995 {0x5F, nullptr, "FlushProcessDataCache"}, 3222 uint64_t physical_address{};
2996 {0x60, nullptr, "DebugActiveProcess"}, 3223 uint64_t size{};
2997 {0x61, nullptr, "BreakDebugProcess"}, 3224
2998 {0x62, nullptr, "TerminateDebugProcess"}, 3225 physical_address = Convert<uint64_t>(GetReg64(system, 2));
2999 {0x63, nullptr, "GetDebugEvent"}, 3226 size = Convert<uint64_t>(GetReg64(system, 3));
3000 {0x64, nullptr, "ContinueDebugEvent"}, 3227
3001 {0x65, SvcWrap64<GetProcessList>, "GetProcessList"}, 3228 ret = QueryIoMapping64(system, &out_address, &out_size, physical_address, size);
3002 {0x66, SvcWrap64<GetThreadList>, "GetThreadList"}, 3229
3003 {0x67, nullptr, "GetDebugThreadContext"}, 3230 SetReg64(system, 0, Convert<uint64_t>(ret));
3004 {0x68, nullptr, "SetDebugThreadContext"}, 3231 SetReg64(system, 1, Convert<uint64_t>(out_address));
3005 {0x69, nullptr, "QueryDebugProcessMemory"}, 3232 SetReg64(system, 2, Convert<uint64_t>(out_size));
3006 {0x6A, nullptr, "ReadDebugProcessMemory"}, 3233}
3007 {0x6B, nullptr, "WriteDebugProcessMemory"}, 3234
3008 {0x6C, nullptr, "SetHardwareBreakPoint"}, 3235static void SvcWrap_CreateDeviceAddressSpace64(Core::System& system) {
3009 {0x6D, nullptr, "GetDebugThreadParam"}, 3236 Result ret{};
3010 {0x6E, nullptr, "Unknown6E"}, 3237
3011 {0x6F, nullptr, "GetSystemInfo"}, 3238 Handle out_handle{};
3012 {0x70, nullptr, "CreatePort"}, 3239 uint64_t das_address{};
3013 {0x71, nullptr, "ManageNamedPort"}, 3240 uint64_t das_size{};
3014 {0x72, nullptr, "ConnectToPort"}, 3241
3015 {0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"}, 3242 das_address = Convert<uint64_t>(GetReg64(system, 1));
3016 {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"}, 3243 das_size = Convert<uint64_t>(GetReg64(system, 2));
3017 {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"}, 3244
3018 {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, 3245 ret = CreateDeviceAddressSpace64(system, &out_handle, das_address, das_size);
3019 {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, 3246
3020 {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, 3247 SetReg64(system, 0, Convert<uint64_t>(ret));
3021 {0x79, nullptr, "CreateProcess"}, 3248 SetReg64(system, 1, Convert<uint64_t>(out_handle));
3022 {0x7A, nullptr, "StartProcess"}, 3249}
3023 {0x7B, nullptr, "TerminateProcess"}, 3250
3024 {0x7C, SvcWrap64<GetProcessInfo>, "GetProcessInfo"}, 3251static void SvcWrap_AttachDeviceAddressSpace64(Core::System& system) {
3025 {0x7D, SvcWrap64<CreateResourceLimit>, "CreateResourceLimit"}, 3252 Result ret{};
3026 {0x7E, SvcWrap64<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"}, 3253
3027 {0x7F, nullptr, "CallSecureMonitor"}, 3254 DeviceName device_name{};
3028 {0x80, nullptr, "Unknown"}, 3255 Handle das_handle{};
3029 {0x81, nullptr, "Unknown"}, 3256
3030 {0x82, nullptr, "Unknown"}, 3257 device_name = Convert<DeviceName>(GetReg64(system, 0));
3031 {0x83, nullptr, "Unknown"}, 3258 das_handle = Convert<Handle>(GetReg64(system, 1));
3032 {0x84, nullptr, "Unknown"}, 3259
3033 {0x85, nullptr, "Unknown"}, 3260 ret = AttachDeviceAddressSpace64(system, device_name, das_handle);
3034 {0x86, nullptr, "Unknown"}, 3261
3035 {0x87, nullptr, "Unknown"}, 3262 SetReg64(system, 0, Convert<uint64_t>(ret));
3036 {0x88, nullptr, "Unknown"}, 3263}
3037 {0x89, nullptr, "Unknown"}, 3264
3038 {0x8A, nullptr, "Unknown"}, 3265static void SvcWrap_DetachDeviceAddressSpace64(Core::System& system) {
3039 {0x8B, nullptr, "Unknown"}, 3266 Result ret{};
3040 {0x8C, nullptr, "Unknown"}, 3267
3041 {0x8D, nullptr, "Unknown"}, 3268 DeviceName device_name{};
3042 {0x8E, nullptr, "Unknown"}, 3269 Handle das_handle{};
3043 {0x8F, nullptr, "Unknown"}, 3270
3044 {0x90, nullptr, "Unknown"}, 3271 device_name = Convert<DeviceName>(GetReg64(system, 0));
3045 {0x91, nullptr, "Unknown"}, 3272 das_handle = Convert<Handle>(GetReg64(system, 1));
3046 {0x92, nullptr, "Unknown"}, 3273
3047 {0x93, nullptr, "Unknown"}, 3274 ret = DetachDeviceAddressSpace64(system, device_name, das_handle);
3048 {0x94, nullptr, "Unknown"}, 3275
3049 {0x95, nullptr, "Unknown"}, 3276 SetReg64(system, 0, Convert<uint64_t>(ret));
3050 {0x96, nullptr, "Unknown"}, 3277}
3051 {0x97, nullptr, "Unknown"}, 3278
3052 {0x98, nullptr, "Unknown"}, 3279static void SvcWrap_MapDeviceAddressSpaceByForce64(Core::System& system) {
3053 {0x99, nullptr, "Unknown"}, 3280 Result ret{};
3054 {0x9A, nullptr, "Unknown"}, 3281
3055 {0x9B, nullptr, "Unknown"}, 3282 Handle das_handle{};
3056 {0x9C, nullptr, "Unknown"}, 3283 Handle process_handle{};
3057 {0x9D, nullptr, "Unknown"}, 3284 uint64_t process_address{};
3058 {0x9E, nullptr, "Unknown"}, 3285 uint64_t size{};
3059 {0x9F, nullptr, "Unknown"}, 3286 uint64_t device_address{};
3060 {0xA0, nullptr, "Unknown"}, 3287 uint32_t option{};
3061 {0xA1, nullptr, "Unknown"}, 3288
3062 {0xA2, nullptr, "Unknown"}, 3289 das_handle = Convert<Handle>(GetReg64(system, 0));
3063 {0xA3, nullptr, "Unknown"}, 3290 process_handle = Convert<Handle>(GetReg64(system, 1));
3064 {0xA4, nullptr, "Unknown"}, 3291 process_address = Convert<uint64_t>(GetReg64(system, 2));
3065 {0xA5, nullptr, "Unknown"}, 3292 size = Convert<uint64_t>(GetReg64(system, 3));
3066 {0xA6, nullptr, "Unknown"}, 3293 device_address = Convert<uint64_t>(GetReg64(system, 4));
3067 {0xA7, nullptr, "Unknown"}, 3294 option = Convert<uint32_t>(GetReg64(system, 5));
3068 {0xA8, nullptr, "Unknown"}, 3295
3069 {0xA9, nullptr, "Unknown"}, 3296 ret = MapDeviceAddressSpaceByForce64(system, das_handle, process_handle, process_address, size, device_address, option);
3070 {0xAA, nullptr, "Unknown"}, 3297
3071 {0xAB, nullptr, "Unknown"}, 3298 SetReg64(system, 0, Convert<uint64_t>(ret));
3072 {0xAC, nullptr, "Unknown"}, 3299}
3073 {0xAD, nullptr, "Unknown"}, 3300
3074 {0xAE, nullptr, "Unknown"}, 3301static void SvcWrap_MapDeviceAddressSpaceAligned64(Core::System& system) {
3075 {0xAF, nullptr, "Unknown"}, 3302 Result ret{};
3076 {0xB0, nullptr, "Unknown"}, 3303
3077 {0xB1, nullptr, "Unknown"}, 3304 Handle das_handle{};
3078 {0xB2, nullptr, "Unknown"}, 3305 Handle process_handle{};
3079 {0xB3, nullptr, "Unknown"}, 3306 uint64_t process_address{};
3080 {0xB4, nullptr, "Unknown"}, 3307 uint64_t size{};
3081 {0xB5, nullptr, "Unknown"}, 3308 uint64_t device_address{};
3082 {0xB6, nullptr, "Unknown"}, 3309 uint32_t option{};
3083 {0xB7, nullptr, "Unknown"}, 3310
3084 {0xB8, nullptr, "Unknown"}, 3311 das_handle = Convert<Handle>(GetReg64(system, 0));
3085 {0xB9, nullptr, "Unknown"}, 3312 process_handle = Convert<Handle>(GetReg64(system, 1));
3086 {0xBA, nullptr, "Unknown"}, 3313 process_address = Convert<uint64_t>(GetReg64(system, 2));
3087 {0xBB, nullptr, "Unknown"}, 3314 size = Convert<uint64_t>(GetReg64(system, 3));
3088 {0xBC, nullptr, "Unknown"}, 3315 device_address = Convert<uint64_t>(GetReg64(system, 4));
3089 {0xBD, nullptr, "Unknown"}, 3316 option = Convert<uint32_t>(GetReg64(system, 5));
3090 {0xBE, nullptr, "Unknown"}, 3317
3091 {0xBF, nullptr, "Unknown"}, 3318 ret = MapDeviceAddressSpaceAligned64(system, das_handle, process_handle, process_address, size, device_address, option);
3092}; 3319
3093 3320 SetReg64(system, 0, Convert<uint64_t>(ret));
3094static const FunctionDef* GetSVCInfo32(u32 func_num) { 3321}
3095 if (func_num >= std::size(SVC_Table_32)) { 3322
3096 LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num); 3323static void SvcWrap_UnmapDeviceAddressSpace64(Core::System& system) {
3097 return nullptr; 3324 Result ret{};
3325
3326 Handle das_handle{};
3327 Handle process_handle{};
3328 uint64_t process_address{};
3329 uint64_t size{};
3330 uint64_t device_address{};
3331
3332 das_handle = Convert<Handle>(GetReg64(system, 0));
3333 process_handle = Convert<Handle>(GetReg64(system, 1));
3334 process_address = Convert<uint64_t>(GetReg64(system, 2));
3335 size = Convert<uint64_t>(GetReg64(system, 3));
3336 device_address = Convert<uint64_t>(GetReg64(system, 4));
3337
3338 ret = UnmapDeviceAddressSpace64(system, das_handle, process_handle, process_address, size, device_address);
3339
3340 SetReg64(system, 0, Convert<uint64_t>(ret));
3341}
3342
3343static void SvcWrap_InvalidateProcessDataCache64(Core::System& system) {
3344 Result ret{};
3345
3346 Handle process_handle{};
3347 uint64_t address{};
3348 uint64_t size{};
3349
3350 process_handle = Convert<Handle>(GetReg64(system, 0));
3351 address = Convert<uint64_t>(GetReg64(system, 1));
3352 size = Convert<uint64_t>(GetReg64(system, 2));
3353
3354 ret = InvalidateProcessDataCache64(system, process_handle, address, size);
3355
3356 SetReg64(system, 0, Convert<uint64_t>(ret));
3357}
3358
3359static void SvcWrap_StoreProcessDataCache64(Core::System& system) {
3360 Result ret{};
3361
3362 Handle process_handle{};
3363 uint64_t address{};
3364 uint64_t size{};
3365
3366 process_handle = Convert<Handle>(GetReg64(system, 0));
3367 address = Convert<uint64_t>(GetReg64(system, 1));
3368 size = Convert<uint64_t>(GetReg64(system, 2));
3369
3370 ret = StoreProcessDataCache64(system, process_handle, address, size);
3371
3372 SetReg64(system, 0, Convert<uint64_t>(ret));
3373}
3374
3375static void SvcWrap_FlushProcessDataCache64(Core::System& system) {
3376 Result ret{};
3377
3378 Handle process_handle{};
3379 uint64_t address{};
3380 uint64_t size{};
3381
3382 process_handle = Convert<Handle>(GetReg64(system, 0));
3383 address = Convert<uint64_t>(GetReg64(system, 1));
3384 size = Convert<uint64_t>(GetReg64(system, 2));
3385
3386 ret = FlushProcessDataCache64(system, process_handle, address, size);
3387
3388 SetReg64(system, 0, Convert<uint64_t>(ret));
3389}
3390
3391static void SvcWrap_DebugActiveProcess64(Core::System& system) {
3392 Result ret{};
3393
3394 Handle out_handle{};
3395 uint64_t process_id{};
3396
3397 process_id = Convert<uint64_t>(GetReg64(system, 1));
3398
3399 ret = DebugActiveProcess64(system, &out_handle, process_id);
3400
3401 SetReg64(system, 0, Convert<uint64_t>(ret));
3402 SetReg64(system, 1, Convert<uint64_t>(out_handle));
3403}
3404
3405static void SvcWrap_BreakDebugProcess64(Core::System& system) {
3406 Result ret{};
3407
3408 Handle debug_handle{};
3409
3410 debug_handle = Convert<Handle>(GetReg64(system, 0));
3411
3412 ret = BreakDebugProcess64(system, debug_handle);
3413
3414 SetReg64(system, 0, Convert<uint64_t>(ret));
3415}
3416
3417static void SvcWrap_TerminateDebugProcess64(Core::System& system) {
3418 Result ret{};
3419
3420 Handle debug_handle{};
3421
3422 debug_handle = Convert<Handle>(GetReg64(system, 0));
3423
3424 ret = TerminateDebugProcess64(system, debug_handle);
3425
3426 SetReg64(system, 0, Convert<uint64_t>(ret));
3427}
3428
3429static void SvcWrap_GetDebugEvent64(Core::System& system) {
3430 Result ret{};
3431
3432 uint64_t out_info{};
3433 Handle debug_handle{};
3434
3435 out_info = Convert<uint64_t>(GetReg64(system, 0));
3436 debug_handle = Convert<Handle>(GetReg64(system, 1));
3437
3438 ret = GetDebugEvent64(system, out_info, debug_handle);
3439
3440 SetReg64(system, 0, Convert<uint64_t>(ret));
3441}
3442
3443static void SvcWrap_ContinueDebugEvent64(Core::System& system) {
3444 Result ret{};
3445
3446 Handle debug_handle{};
3447 uint32_t flags{};
3448 uint64_t thread_ids{};
3449 int32_t num_thread_ids{};
3450
3451 debug_handle = Convert<Handle>(GetReg64(system, 0));
3452 flags = Convert<uint32_t>(GetReg64(system, 1));
3453 thread_ids = Convert<uint64_t>(GetReg64(system, 2));
3454 num_thread_ids = Convert<int32_t>(GetReg64(system, 3));
3455
3456 ret = ContinueDebugEvent64(system, debug_handle, flags, thread_ids, num_thread_ids);
3457
3458 SetReg64(system, 0, Convert<uint64_t>(ret));
3459}
3460
3461static void SvcWrap_GetProcessList64(Core::System& system) {
3462 Result ret{};
3463
3464 int32_t out_num_processes{};
3465 uint64_t out_process_ids{};
3466 int32_t max_out_count{};
3467
3468 out_process_ids = Convert<uint64_t>(GetReg64(system, 1));
3469 max_out_count = Convert<int32_t>(GetReg64(system, 2));
3470
3471 ret = GetProcessList64(system, &out_num_processes, out_process_ids, max_out_count);
3472
3473 SetReg64(system, 0, Convert<uint64_t>(ret));
3474 SetReg64(system, 1, Convert<uint64_t>(out_num_processes));
3475}
3476
3477static void SvcWrap_GetThreadList64(Core::System& system) {
3478 Result ret{};
3479
3480 int32_t out_num_threads{};
3481 uint64_t out_thread_ids{};
3482 int32_t max_out_count{};
3483 Handle debug_handle{};
3484
3485 out_thread_ids = Convert<uint64_t>(GetReg64(system, 1));
3486 max_out_count = Convert<int32_t>(GetReg64(system, 2));
3487 debug_handle = Convert<Handle>(GetReg64(system, 3));
3488
3489 ret = GetThreadList64(system, &out_num_threads, out_thread_ids, max_out_count, debug_handle);
3490
3491 SetReg64(system, 0, Convert<uint64_t>(ret));
3492 SetReg64(system, 1, Convert<uint64_t>(out_num_threads));
3493}
3494
3495static void SvcWrap_GetDebugThreadContext64(Core::System& system) {
3496 Result ret{};
3497
3498 uint64_t out_context{};
3499 Handle debug_handle{};
3500 uint64_t thread_id{};
3501 uint32_t context_flags{};
3502
3503 out_context = Convert<uint64_t>(GetReg64(system, 0));
3504 debug_handle = Convert<Handle>(GetReg64(system, 1));
3505 thread_id = Convert<uint64_t>(GetReg64(system, 2));
3506 context_flags = Convert<uint32_t>(GetReg64(system, 3));
3507
3508 ret = GetDebugThreadContext64(system, out_context, debug_handle, thread_id, context_flags);
3509
3510 SetReg64(system, 0, Convert<uint64_t>(ret));
3511}
3512
3513static void SvcWrap_SetDebugThreadContext64(Core::System& system) {
3514 Result ret{};
3515
3516 Handle debug_handle{};
3517 uint64_t thread_id{};
3518 uint64_t context{};
3519 uint32_t context_flags{};
3520
3521 debug_handle = Convert<Handle>(GetReg64(system, 0));
3522 thread_id = Convert<uint64_t>(GetReg64(system, 1));
3523 context = Convert<uint64_t>(GetReg64(system, 2));
3524 context_flags = Convert<uint32_t>(GetReg64(system, 3));
3525
3526 ret = SetDebugThreadContext64(system, debug_handle, thread_id, context, context_flags);
3527
3528 SetReg64(system, 0, Convert<uint64_t>(ret));
3529}
3530
3531static void SvcWrap_QueryDebugProcessMemory64(Core::System& system) {
3532 Result ret{};
3533
3534 PageInfo out_page_info{};
3535 uint64_t out_memory_info{};
3536 Handle process_handle{};
3537 uint64_t address{};
3538
3539 out_memory_info = Convert<uint64_t>(GetReg64(system, 0));
3540 process_handle = Convert<Handle>(GetReg64(system, 2));
3541 address = Convert<uint64_t>(GetReg64(system, 3));
3542
3543 ret = QueryDebugProcessMemory64(system, out_memory_info, &out_page_info, process_handle, address);
3544
3545 SetReg64(system, 0, Convert<uint64_t>(ret));
3546 SetReg64(system, 1, Convert<uint64_t>(out_page_info));
3547}
3548
3549static void SvcWrap_ReadDebugProcessMemory64(Core::System& system) {
3550 Result ret{};
3551
3552 uint64_t buffer{};
3553 Handle debug_handle{};
3554 uint64_t address{};
3555 uint64_t size{};
3556
3557 buffer = Convert<uint64_t>(GetReg64(system, 0));
3558 debug_handle = Convert<Handle>(GetReg64(system, 1));
3559 address = Convert<uint64_t>(GetReg64(system, 2));
3560 size = Convert<uint64_t>(GetReg64(system, 3));
3561
3562 ret = ReadDebugProcessMemory64(system, buffer, debug_handle, address, size);
3563
3564 SetReg64(system, 0, Convert<uint64_t>(ret));
3565}
3566
3567static void SvcWrap_WriteDebugProcessMemory64(Core::System& system) {
3568 Result ret{};
3569
3570 Handle debug_handle{};
3571 uint64_t buffer{};
3572 uint64_t address{};
3573 uint64_t size{};
3574
3575 debug_handle = Convert<Handle>(GetReg64(system, 0));
3576 buffer = Convert<uint64_t>(GetReg64(system, 1));
3577 address = Convert<uint64_t>(GetReg64(system, 2));
3578 size = Convert<uint64_t>(GetReg64(system, 3));
3579
3580 ret = WriteDebugProcessMemory64(system, debug_handle, buffer, address, size);
3581
3582 SetReg64(system, 0, Convert<uint64_t>(ret));
3583}
3584
3585static void SvcWrap_SetHardwareBreakPoint64(Core::System& system) {
3586 Result ret{};
3587
3588 HardwareBreakPointRegisterName name{};
3589 uint64_t flags{};
3590 uint64_t value{};
3591
3592 name = Convert<HardwareBreakPointRegisterName>(GetReg64(system, 0));
3593 flags = Convert<uint64_t>(GetReg64(system, 1));
3594 value = Convert<uint64_t>(GetReg64(system, 2));
3595
3596 ret = SetHardwareBreakPoint64(system, name, flags, value);
3597
3598 SetReg64(system, 0, Convert<uint64_t>(ret));
3599}
3600
3601static void SvcWrap_GetDebugThreadParam64(Core::System& system) {
3602 Result ret{};
3603
3604 uint64_t out_64{};
3605 uint32_t out_32{};
3606 Handle debug_handle{};
3607 uint64_t thread_id{};
3608 DebugThreadParam param{};
3609
3610 debug_handle = Convert<Handle>(GetReg64(system, 2));
3611 thread_id = Convert<uint64_t>(GetReg64(system, 3));
3612 param = Convert<DebugThreadParam>(GetReg64(system, 4));
3613
3614 ret = GetDebugThreadParam64(system, &out_64, &out_32, debug_handle, thread_id, param);
3615
3616 SetReg64(system, 0, Convert<uint64_t>(ret));
3617 SetReg64(system, 1, Convert<uint64_t>(out_64));
3618 SetReg64(system, 2, Convert<uint64_t>(out_32));
3619}
3620
3621static void SvcWrap_GetSystemInfo64(Core::System& system) {
3622 Result ret{};
3623
3624 uint64_t out{};
3625 SystemInfoType info_type{};
3626 Handle handle{};
3627 uint64_t info_subtype{};
3628
3629 info_type = Convert<SystemInfoType>(GetReg64(system, 1));
3630 handle = Convert<Handle>(GetReg64(system, 2));
3631 info_subtype = Convert<uint64_t>(GetReg64(system, 3));
3632
3633 ret = GetSystemInfo64(system, &out, info_type, handle, info_subtype);
3634
3635 SetReg64(system, 0, Convert<uint64_t>(ret));
3636 SetReg64(system, 1, Convert<uint64_t>(out));
3637}
3638
3639static void SvcWrap_CreatePort64(Core::System& system) {
3640 Result ret{};
3641
3642 Handle out_server_handle{};
3643 Handle out_client_handle{};
3644 int32_t max_sessions{};
3645 bool is_light{};
3646 uint64_t name{};
3647
3648 max_sessions = Convert<int32_t>(GetReg64(system, 2));
3649 is_light = Convert<bool>(GetReg64(system, 3));
3650 name = Convert<uint64_t>(GetReg64(system, 4));
3651
3652 ret = CreatePort64(system, &out_server_handle, &out_client_handle, max_sessions, is_light, name);
3653
3654 SetReg64(system, 0, Convert<uint64_t>(ret));
3655 SetReg64(system, 1, Convert<uint64_t>(out_server_handle));
3656 SetReg64(system, 2, Convert<uint64_t>(out_client_handle));
3657}
3658
3659static void SvcWrap_ManageNamedPort64(Core::System& system) {
3660 Result ret{};
3661
3662 Handle out_server_handle{};
3663 uint64_t name{};
3664 int32_t max_sessions{};
3665
3666 name = Convert<uint64_t>(GetReg64(system, 1));
3667 max_sessions = Convert<int32_t>(GetReg64(system, 2));
3668
3669 ret = ManageNamedPort64(system, &out_server_handle, name, max_sessions);
3670
3671 SetReg64(system, 0, Convert<uint64_t>(ret));
3672 SetReg64(system, 1, Convert<uint64_t>(out_server_handle));
3673}
3674
3675static void SvcWrap_ConnectToPort64(Core::System& system) {
3676 Result ret{};
3677
3678 Handle out_handle{};
3679 Handle port{};
3680
3681 port = Convert<Handle>(GetReg64(system, 1));
3682
3683 ret = ConnectToPort64(system, &out_handle, port);
3684
3685 SetReg64(system, 0, Convert<uint64_t>(ret));
3686 SetReg64(system, 1, Convert<uint64_t>(out_handle));
3687}
3688
3689static void SvcWrap_SetProcessMemoryPermission64(Core::System& system) {
3690 Result ret{};
3691
3692 Handle process_handle{};
3693 uint64_t address{};
3694 uint64_t size{};
3695 MemoryPermission perm{};
3696
3697 process_handle = Convert<Handle>(GetReg64(system, 0));
3698 address = Convert<uint64_t>(GetReg64(system, 1));
3699 size = Convert<uint64_t>(GetReg64(system, 2));
3700 perm = Convert<MemoryPermission>(GetReg64(system, 3));
3701
3702 ret = SetProcessMemoryPermission64(system, process_handle, address, size, perm);
3703
3704 SetReg64(system, 0, Convert<uint64_t>(ret));
3705}
3706
3707static void SvcWrap_MapProcessMemory64(Core::System& system) {
3708 Result ret{};
3709
3710 uint64_t dst_address{};
3711 Handle process_handle{};
3712 uint64_t src_address{};
3713 uint64_t size{};
3714
3715 dst_address = Convert<uint64_t>(GetReg64(system, 0));
3716 process_handle = Convert<Handle>(GetReg64(system, 1));
3717 src_address = Convert<uint64_t>(GetReg64(system, 2));
3718 size = Convert<uint64_t>(GetReg64(system, 3));
3719
3720 ret = MapProcessMemory64(system, dst_address, process_handle, src_address, size);
3721
3722 SetReg64(system, 0, Convert<uint64_t>(ret));
3723}
3724
3725static void SvcWrap_UnmapProcessMemory64(Core::System& system) {
3726 Result ret{};
3727
3728 uint64_t dst_address{};
3729 Handle process_handle{};
3730 uint64_t src_address{};
3731 uint64_t size{};
3732
3733 dst_address = Convert<uint64_t>(GetReg64(system, 0));
3734 process_handle = Convert<Handle>(GetReg64(system, 1));
3735 src_address = Convert<uint64_t>(GetReg64(system, 2));
3736 size = Convert<uint64_t>(GetReg64(system, 3));
3737
3738 ret = UnmapProcessMemory64(system, dst_address, process_handle, src_address, size);
3739
3740 SetReg64(system, 0, Convert<uint64_t>(ret));
3741}
3742
3743static void SvcWrap_QueryProcessMemory64(Core::System& system) {
3744 Result ret{};
3745
3746 PageInfo out_page_info{};
3747 uint64_t out_memory_info{};
3748 Handle process_handle{};
3749 uint64_t address{};
3750
3751 out_memory_info = Convert<uint64_t>(GetReg64(system, 0));
3752 process_handle = Convert<Handle>(GetReg64(system, 2));
3753 address = Convert<uint64_t>(GetReg64(system, 3));
3754
3755 ret = QueryProcessMemory64(system, out_memory_info, &out_page_info, process_handle, address);
3756
3757 SetReg64(system, 0, Convert<uint64_t>(ret));
3758 SetReg64(system, 1, Convert<uint64_t>(out_page_info));
3759}
3760
3761static void SvcWrap_MapProcessCodeMemory64(Core::System& system) {
3762 Result ret{};
3763
3764 Handle process_handle{};
3765 uint64_t dst_address{};
3766 uint64_t src_address{};
3767 uint64_t size{};
3768
3769 process_handle = Convert<Handle>(GetReg64(system, 0));
3770 dst_address = Convert<uint64_t>(GetReg64(system, 1));
3771 src_address = Convert<uint64_t>(GetReg64(system, 2));
3772 size = Convert<uint64_t>(GetReg64(system, 3));
3773
3774 ret = MapProcessCodeMemory64(system, process_handle, dst_address, src_address, size);
3775
3776 SetReg64(system, 0, Convert<uint64_t>(ret));
3777}
3778
3779static void SvcWrap_UnmapProcessCodeMemory64(Core::System& system) {
3780 Result ret{};
3781
3782 Handle process_handle{};
3783 uint64_t dst_address{};
3784 uint64_t src_address{};
3785 uint64_t size{};
3786
3787 process_handle = Convert<Handle>(GetReg64(system, 0));
3788 dst_address = Convert<uint64_t>(GetReg64(system, 1));
3789 src_address = Convert<uint64_t>(GetReg64(system, 2));
3790 size = Convert<uint64_t>(GetReg64(system, 3));
3791
3792 ret = UnmapProcessCodeMemory64(system, process_handle, dst_address, src_address, size);
3793
3794 SetReg64(system, 0, Convert<uint64_t>(ret));
3795}
3796
3797static void SvcWrap_CreateProcess64(Core::System& system) {
3798 Result ret{};
3799
3800 Handle out_handle{};
3801 uint64_t parameters{};
3802 uint64_t caps{};
3803 int32_t num_caps{};
3804
3805 parameters = Convert<uint64_t>(GetReg64(system, 1));
3806 caps = Convert<uint64_t>(GetReg64(system, 2));
3807 num_caps = Convert<int32_t>(GetReg64(system, 3));
3808
3809 ret = CreateProcess64(system, &out_handle, parameters, caps, num_caps);
3810
3811 SetReg64(system, 0, Convert<uint64_t>(ret));
3812 SetReg64(system, 1, Convert<uint64_t>(out_handle));
3813}
3814
3815static void SvcWrap_StartProcess64(Core::System& system) {
3816 Result ret{};
3817
3818 Handle process_handle{};
3819 int32_t priority{};
3820 int32_t core_id{};
3821 uint64_t main_thread_stack_size{};
3822
3823 process_handle = Convert<Handle>(GetReg64(system, 0));
3824 priority = Convert<int32_t>(GetReg64(system, 1));
3825 core_id = Convert<int32_t>(GetReg64(system, 2));
3826 main_thread_stack_size = Convert<uint64_t>(GetReg64(system, 3));
3827
3828 ret = StartProcess64(system, process_handle, priority, core_id, main_thread_stack_size);
3829
3830 SetReg64(system, 0, Convert<uint64_t>(ret));
3831}
3832
3833static void SvcWrap_TerminateProcess64(Core::System& system) {
3834 Result ret{};
3835
3836 Handle process_handle{};
3837
3838 process_handle = Convert<Handle>(GetReg64(system, 0));
3839
3840 ret = TerminateProcess64(system, process_handle);
3841
3842 SetReg64(system, 0, Convert<uint64_t>(ret));
3843}
3844
3845static void SvcWrap_GetProcessInfo64(Core::System& system) {
3846 Result ret{};
3847
3848 int64_t out_info{};
3849 Handle process_handle{};
3850 ProcessInfoType info_type{};
3851
3852 process_handle = Convert<Handle>(GetReg64(system, 1));
3853 info_type = Convert<ProcessInfoType>(GetReg64(system, 2));
3854
3855 ret = GetProcessInfo64(system, &out_info, process_handle, info_type);
3856
3857 SetReg64(system, 0, Convert<uint64_t>(ret));
3858 SetReg64(system, 1, Convert<uint64_t>(out_info));
3859}
3860
3861static void SvcWrap_CreateResourceLimit64(Core::System& system) {
3862 Result ret{};
3863
3864 Handle out_handle{};
3865
3866 ret = CreateResourceLimit64(system, &out_handle);
3867
3868 SetReg64(system, 0, Convert<uint64_t>(ret));
3869 SetReg64(system, 1, Convert<uint64_t>(out_handle));
3870}
3871
3872static void SvcWrap_SetResourceLimitLimitValue64(Core::System& system) {
3873 Result ret{};
3874
3875 Handle resource_limit_handle{};
3876 LimitableResource which{};
3877 int64_t limit_value{};
3878
3879 resource_limit_handle = Convert<Handle>(GetReg64(system, 0));
3880 which = Convert<LimitableResource>(GetReg64(system, 1));
3881 limit_value = Convert<int64_t>(GetReg64(system, 2));
3882
3883 ret = SetResourceLimitLimitValue64(system, resource_limit_handle, which, limit_value);
3884
3885 SetReg64(system, 0, Convert<uint64_t>(ret));
3886}
3887
3888static void SvcWrap_MapInsecureMemory64(Core::System& system) {
3889 Result ret{};
3890
3891 uint64_t address{};
3892 uint64_t size{};
3893
3894 address = Convert<uint64_t>(GetReg64(system, 0));
3895 size = Convert<uint64_t>(GetReg64(system, 1));
3896
3897 ret = MapInsecureMemory64(system, address, size);
3898
3899 SetReg64(system, 0, Convert<uint64_t>(ret));
3900}
3901
3902static void SvcWrap_UnmapInsecureMemory64(Core::System& system) {
3903 Result ret{};
3904
3905 uint64_t address{};
3906 uint64_t size{};
3907
3908 address = Convert<uint64_t>(GetReg64(system, 0));
3909 size = Convert<uint64_t>(GetReg64(system, 1));
3910
3911 ret = UnmapInsecureMemory64(system, address, size);
3912
3913 SetReg64(system, 0, Convert<uint64_t>(ret));
3914}
3915
3916static void Call32(Core::System& system, u32 imm) {
3917 switch (static_cast<SvcId>(imm)) {
3918 case SvcId::SetHeapSize:
3919 return SvcWrap_SetHeapSize64From32(system);
3920 case SvcId::SetMemoryPermission:
3921 return SvcWrap_SetMemoryPermission64From32(system);
3922 case SvcId::SetMemoryAttribute:
3923 return SvcWrap_SetMemoryAttribute64From32(system);
3924 case SvcId::MapMemory:
3925 return SvcWrap_MapMemory64From32(system);
3926 case SvcId::UnmapMemory:
3927 return SvcWrap_UnmapMemory64From32(system);
3928 case SvcId::QueryMemory:
3929 return SvcWrap_QueryMemory64From32(system);
3930 case SvcId::ExitProcess:
3931 return SvcWrap_ExitProcess64From32(system);
3932 case SvcId::CreateThread:
3933 return SvcWrap_CreateThread64From32(system);
3934 case SvcId::StartThread:
3935 return SvcWrap_StartThread64From32(system);
3936 case SvcId::ExitThread:
3937 return SvcWrap_ExitThread64From32(system);
3938 case SvcId::SleepThread:
3939 return SvcWrap_SleepThread64From32(system);
3940 case SvcId::GetThreadPriority:
3941 return SvcWrap_GetThreadPriority64From32(system);
3942 case SvcId::SetThreadPriority:
3943 return SvcWrap_SetThreadPriority64From32(system);
3944 case SvcId::GetThreadCoreMask:
3945 return SvcWrap_GetThreadCoreMask64From32(system);
3946 case SvcId::SetThreadCoreMask:
3947 return SvcWrap_SetThreadCoreMask64From32(system);
3948 case SvcId::GetCurrentProcessorNumber:
3949 return SvcWrap_GetCurrentProcessorNumber64From32(system);
3950 case SvcId::SignalEvent:
3951 return SvcWrap_SignalEvent64From32(system);
3952 case SvcId::ClearEvent:
3953 return SvcWrap_ClearEvent64From32(system);
3954 case SvcId::MapSharedMemory:
3955 return SvcWrap_MapSharedMemory64From32(system);
3956 case SvcId::UnmapSharedMemory:
3957 return SvcWrap_UnmapSharedMemory64From32(system);
3958 case SvcId::CreateTransferMemory:
3959 return SvcWrap_CreateTransferMemory64From32(system);
3960 case SvcId::CloseHandle:
3961 return SvcWrap_CloseHandle64From32(system);
3962 case SvcId::ResetSignal:
3963 return SvcWrap_ResetSignal64From32(system);
3964 case SvcId::WaitSynchronization:
3965 return SvcWrap_WaitSynchronization64From32(system);
3966 case SvcId::CancelSynchronization:
3967 return SvcWrap_CancelSynchronization64From32(system);
3968 case SvcId::ArbitrateLock:
3969 return SvcWrap_ArbitrateLock64From32(system);
3970 case SvcId::ArbitrateUnlock:
3971 return SvcWrap_ArbitrateUnlock64From32(system);
3972 case SvcId::WaitProcessWideKeyAtomic:
3973 return SvcWrap_WaitProcessWideKeyAtomic64From32(system);
3974 case SvcId::SignalProcessWideKey:
3975 return SvcWrap_SignalProcessWideKey64From32(system);
3976 case SvcId::GetSystemTick:
3977 return SvcWrap_GetSystemTick64From32(system);
3978 case SvcId::ConnectToNamedPort:
3979 return SvcWrap_ConnectToNamedPort64From32(system);
3980 case SvcId::SendSyncRequestLight:
3981 return SvcWrap_SendSyncRequestLight64From32(system);
3982 case SvcId::SendSyncRequest:
3983 return SvcWrap_SendSyncRequest64From32(system);
3984 case SvcId::SendSyncRequestWithUserBuffer:
3985 return SvcWrap_SendSyncRequestWithUserBuffer64From32(system);
3986 case SvcId::SendAsyncRequestWithUserBuffer:
3987 return SvcWrap_SendAsyncRequestWithUserBuffer64From32(system);
3988 case SvcId::GetProcessId:
3989 return SvcWrap_GetProcessId64From32(system);
3990 case SvcId::GetThreadId:
3991 return SvcWrap_GetThreadId64From32(system);
3992 case SvcId::Break:
3993 return SvcWrap_Break64From32(system);
3994 case SvcId::OutputDebugString:
3995 return SvcWrap_OutputDebugString64From32(system);
3996 case SvcId::ReturnFromException:
3997 return SvcWrap_ReturnFromException64From32(system);
3998 case SvcId::GetInfo:
3999 return SvcWrap_GetInfo64From32(system);
4000 case SvcId::FlushEntireDataCache:
4001 return SvcWrap_FlushEntireDataCache64From32(system);
4002 case SvcId::FlushDataCache:
4003 return SvcWrap_FlushDataCache64From32(system);
4004 case SvcId::MapPhysicalMemory:
4005 return SvcWrap_MapPhysicalMemory64From32(system);
4006 case SvcId::UnmapPhysicalMemory:
4007 return SvcWrap_UnmapPhysicalMemory64From32(system);
4008 case SvcId::GetDebugFutureThreadInfo:
4009 return SvcWrap_GetDebugFutureThreadInfo64From32(system);
4010 case SvcId::GetLastThreadInfo:
4011 return SvcWrap_GetLastThreadInfo64From32(system);
4012 case SvcId::GetResourceLimitLimitValue:
4013 return SvcWrap_GetResourceLimitLimitValue64From32(system);
4014 case SvcId::GetResourceLimitCurrentValue:
4015 return SvcWrap_GetResourceLimitCurrentValue64From32(system);
4016 case SvcId::SetThreadActivity:
4017 return SvcWrap_SetThreadActivity64From32(system);
4018 case SvcId::GetThreadContext3:
4019 return SvcWrap_GetThreadContext364From32(system);
4020 case SvcId::WaitForAddress:
4021 return SvcWrap_WaitForAddress64From32(system);
4022 case SvcId::SignalToAddress:
4023 return SvcWrap_SignalToAddress64From32(system);
4024 case SvcId::SynchronizePreemptionState:
4025 return SvcWrap_SynchronizePreemptionState64From32(system);
4026 case SvcId::GetResourceLimitPeakValue:
4027 return SvcWrap_GetResourceLimitPeakValue64From32(system);
4028 case SvcId::CreateIoPool:
4029 return SvcWrap_CreateIoPool64From32(system);
4030 case SvcId::CreateIoRegion:
4031 return SvcWrap_CreateIoRegion64From32(system);
4032 case SvcId::KernelDebug:
4033 return SvcWrap_KernelDebug64From32(system);
4034 case SvcId::ChangeKernelTraceState:
4035 return SvcWrap_ChangeKernelTraceState64From32(system);
4036 case SvcId::CreateSession:
4037 return SvcWrap_CreateSession64From32(system);
4038 case SvcId::AcceptSession:
4039 return SvcWrap_AcceptSession64From32(system);
4040 case SvcId::ReplyAndReceiveLight:
4041 return SvcWrap_ReplyAndReceiveLight64From32(system);
4042 case SvcId::ReplyAndReceive:
4043 return SvcWrap_ReplyAndReceive64From32(system);
4044 case SvcId::ReplyAndReceiveWithUserBuffer:
4045 return SvcWrap_ReplyAndReceiveWithUserBuffer64From32(system);
4046 case SvcId::CreateEvent:
4047 return SvcWrap_CreateEvent64From32(system);
4048 case SvcId::MapIoRegion:
4049 return SvcWrap_MapIoRegion64From32(system);
4050 case SvcId::UnmapIoRegion:
4051 return SvcWrap_UnmapIoRegion64From32(system);
4052 case SvcId::MapPhysicalMemoryUnsafe:
4053 return SvcWrap_MapPhysicalMemoryUnsafe64From32(system);
4054 case SvcId::UnmapPhysicalMemoryUnsafe:
4055 return SvcWrap_UnmapPhysicalMemoryUnsafe64From32(system);
4056 case SvcId::SetUnsafeLimit:
4057 return SvcWrap_SetUnsafeLimit64From32(system);
4058 case SvcId::CreateCodeMemory:
4059 return SvcWrap_CreateCodeMemory64From32(system);
4060 case SvcId::ControlCodeMemory:
4061 return SvcWrap_ControlCodeMemory64From32(system);
4062 case SvcId::SleepSystem:
4063 return SvcWrap_SleepSystem64From32(system);
4064 case SvcId::ReadWriteRegister:
4065 return SvcWrap_ReadWriteRegister64From32(system);
4066 case SvcId::SetProcessActivity:
4067 return SvcWrap_SetProcessActivity64From32(system);
4068 case SvcId::CreateSharedMemory:
4069 return SvcWrap_CreateSharedMemory64From32(system);
4070 case SvcId::MapTransferMemory:
4071 return SvcWrap_MapTransferMemory64From32(system);
4072 case SvcId::UnmapTransferMemory:
4073 return SvcWrap_UnmapTransferMemory64From32(system);
4074 case SvcId::CreateInterruptEvent:
4075 return SvcWrap_CreateInterruptEvent64From32(system);
4076 case SvcId::QueryPhysicalAddress:
4077 return SvcWrap_QueryPhysicalAddress64From32(system);
4078 case SvcId::QueryIoMapping:
4079 return SvcWrap_QueryIoMapping64From32(system);
4080 case SvcId::CreateDeviceAddressSpace:
4081 return SvcWrap_CreateDeviceAddressSpace64From32(system);
4082 case SvcId::AttachDeviceAddressSpace:
4083 return SvcWrap_AttachDeviceAddressSpace64From32(system);
4084 case SvcId::DetachDeviceAddressSpace:
4085 return SvcWrap_DetachDeviceAddressSpace64From32(system);
4086 case SvcId::MapDeviceAddressSpaceByForce:
4087 return SvcWrap_MapDeviceAddressSpaceByForce64From32(system);
4088 case SvcId::MapDeviceAddressSpaceAligned:
4089 return SvcWrap_MapDeviceAddressSpaceAligned64From32(system);
4090 case SvcId::UnmapDeviceAddressSpace:
4091 return SvcWrap_UnmapDeviceAddressSpace64From32(system);
4092 case SvcId::InvalidateProcessDataCache:
4093 return SvcWrap_InvalidateProcessDataCache64From32(system);
4094 case SvcId::StoreProcessDataCache:
4095 return SvcWrap_StoreProcessDataCache64From32(system);
4096 case SvcId::FlushProcessDataCache:
4097 return SvcWrap_FlushProcessDataCache64From32(system);
4098 case SvcId::DebugActiveProcess:
4099 return SvcWrap_DebugActiveProcess64From32(system);
4100 case SvcId::BreakDebugProcess:
4101 return SvcWrap_BreakDebugProcess64From32(system);
4102 case SvcId::TerminateDebugProcess:
4103 return SvcWrap_TerminateDebugProcess64From32(system);
4104 case SvcId::GetDebugEvent:
4105 return SvcWrap_GetDebugEvent64From32(system);
4106 case SvcId::ContinueDebugEvent:
4107 return SvcWrap_ContinueDebugEvent64From32(system);
4108 case SvcId::GetProcessList:
4109 return SvcWrap_GetProcessList64From32(system);
4110 case SvcId::GetThreadList:
4111 return SvcWrap_GetThreadList64From32(system);
4112 case SvcId::GetDebugThreadContext:
4113 return SvcWrap_GetDebugThreadContext64From32(system);
4114 case SvcId::SetDebugThreadContext:
4115 return SvcWrap_SetDebugThreadContext64From32(system);
4116 case SvcId::QueryDebugProcessMemory:
4117 return SvcWrap_QueryDebugProcessMemory64From32(system);
4118 case SvcId::ReadDebugProcessMemory:
4119 return SvcWrap_ReadDebugProcessMemory64From32(system);
4120 case SvcId::WriteDebugProcessMemory:
4121 return SvcWrap_WriteDebugProcessMemory64From32(system);
4122 case SvcId::SetHardwareBreakPoint:
4123 return SvcWrap_SetHardwareBreakPoint64From32(system);
4124 case SvcId::GetDebugThreadParam:
4125 return SvcWrap_GetDebugThreadParam64From32(system);
4126 case SvcId::GetSystemInfo:
4127 return SvcWrap_GetSystemInfo64From32(system);
4128 case SvcId::CreatePort:
4129 return SvcWrap_CreatePort64From32(system);
4130 case SvcId::ManageNamedPort:
4131 return SvcWrap_ManageNamedPort64From32(system);
4132 case SvcId::ConnectToPort:
4133 return SvcWrap_ConnectToPort64From32(system);
4134 case SvcId::SetProcessMemoryPermission:
4135 return SvcWrap_SetProcessMemoryPermission64From32(system);
4136 case SvcId::MapProcessMemory:
4137 return SvcWrap_MapProcessMemory64From32(system);
4138 case SvcId::UnmapProcessMemory:
4139 return SvcWrap_UnmapProcessMemory64From32(system);
4140 case SvcId::QueryProcessMemory:
4141 return SvcWrap_QueryProcessMemory64From32(system);
4142 case SvcId::MapProcessCodeMemory:
4143 return SvcWrap_MapProcessCodeMemory64From32(system);
4144 case SvcId::UnmapProcessCodeMemory:
4145 return SvcWrap_UnmapProcessCodeMemory64From32(system);
4146 case SvcId::CreateProcess:
4147 return SvcWrap_CreateProcess64From32(system);
4148 case SvcId::StartProcess:
4149 return SvcWrap_StartProcess64From32(system);
4150 case SvcId::TerminateProcess:
4151 return SvcWrap_TerminateProcess64From32(system);
4152 case SvcId::GetProcessInfo:
4153 return SvcWrap_GetProcessInfo64From32(system);
4154 case SvcId::CreateResourceLimit:
4155 return SvcWrap_CreateResourceLimit64From32(system);
4156 case SvcId::SetResourceLimitLimitValue:
4157 return SvcWrap_SetResourceLimitLimitValue64From32(system);
4158 case SvcId::CallSecureMonitor:
4159 return SvcWrap_CallSecureMonitor64From32(system);
4160 case SvcId::MapInsecureMemory:
4161 return SvcWrap_MapInsecureMemory64From32(system);
4162 case SvcId::UnmapInsecureMemory:
4163 return SvcWrap_UnmapInsecureMemory64From32(system);
4164 default:
4165 LOG_CRITICAL(Kernel_SVC, "Unknown SVC {:x}!", imm);
4166 break;
3098 } 4167 }
3099 return &SVC_Table_32[func_num];
3100} 4168}
3101 4169
3102static const FunctionDef* GetSVCInfo64(u32 func_num) { 4170static void Call64(Core::System& system, u32 imm) {
3103 if (func_num >= std::size(SVC_Table_64)) { 4171 switch (static_cast<SvcId>(imm)) {
3104 LOG_ERROR(Kernel_SVC, "Unknown svc=0x{:02X}", func_num); 4172 case SvcId::SetHeapSize:
3105 return nullptr; 4173 return SvcWrap_SetHeapSize64(system);
4174 case SvcId::SetMemoryPermission:
4175 return SvcWrap_SetMemoryPermission64(system);
4176 case SvcId::SetMemoryAttribute:
4177 return SvcWrap_SetMemoryAttribute64(system);
4178 case SvcId::MapMemory:
4179 return SvcWrap_MapMemory64(system);
4180 case SvcId::UnmapMemory:
4181 return SvcWrap_UnmapMemory64(system);
4182 case SvcId::QueryMemory:
4183 return SvcWrap_QueryMemory64(system);
4184 case SvcId::ExitProcess:
4185 return SvcWrap_ExitProcess64(system);
4186 case SvcId::CreateThread:
4187 return SvcWrap_CreateThread64(system);
4188 case SvcId::StartThread:
4189 return SvcWrap_StartThread64(system);
4190 case SvcId::ExitThread:
4191 return SvcWrap_ExitThread64(system);
4192 case SvcId::SleepThread:
4193 return SvcWrap_SleepThread64(system);
4194 case SvcId::GetThreadPriority:
4195 return SvcWrap_GetThreadPriority64(system);
4196 case SvcId::SetThreadPriority:
4197 return SvcWrap_SetThreadPriority64(system);
4198 case SvcId::GetThreadCoreMask:
4199 return SvcWrap_GetThreadCoreMask64(system);
4200 case SvcId::SetThreadCoreMask:
4201 return SvcWrap_SetThreadCoreMask64(system);
4202 case SvcId::GetCurrentProcessorNumber:
4203 return SvcWrap_GetCurrentProcessorNumber64(system);
4204 case SvcId::SignalEvent:
4205 return SvcWrap_SignalEvent64(system);
4206 case SvcId::ClearEvent:
4207 return SvcWrap_ClearEvent64(system);
4208 case SvcId::MapSharedMemory:
4209 return SvcWrap_MapSharedMemory64(system);
4210 case SvcId::UnmapSharedMemory:
4211 return SvcWrap_UnmapSharedMemory64(system);
4212 case SvcId::CreateTransferMemory:
4213 return SvcWrap_CreateTransferMemory64(system);
4214 case SvcId::CloseHandle:
4215 return SvcWrap_CloseHandle64(system);
4216 case SvcId::ResetSignal:
4217 return SvcWrap_ResetSignal64(system);
4218 case SvcId::WaitSynchronization:
4219 return SvcWrap_WaitSynchronization64(system);
4220 case SvcId::CancelSynchronization:
4221 return SvcWrap_CancelSynchronization64(system);
4222 case SvcId::ArbitrateLock:
4223 return SvcWrap_ArbitrateLock64(system);
4224 case SvcId::ArbitrateUnlock:
4225 return SvcWrap_ArbitrateUnlock64(system);
4226 case SvcId::WaitProcessWideKeyAtomic:
4227 return SvcWrap_WaitProcessWideKeyAtomic64(system);
4228 case SvcId::SignalProcessWideKey:
4229 return SvcWrap_SignalProcessWideKey64(system);
4230 case SvcId::GetSystemTick:
4231 return SvcWrap_GetSystemTick64(system);
4232 case SvcId::ConnectToNamedPort:
4233 return SvcWrap_ConnectToNamedPort64(system);
4234 case SvcId::SendSyncRequestLight:
4235 return SvcWrap_SendSyncRequestLight64(system);
4236 case SvcId::SendSyncRequest:
4237 return SvcWrap_SendSyncRequest64(system);
4238 case SvcId::SendSyncRequestWithUserBuffer:
4239 return SvcWrap_SendSyncRequestWithUserBuffer64(system);
4240 case SvcId::SendAsyncRequestWithUserBuffer:
4241 return SvcWrap_SendAsyncRequestWithUserBuffer64(system);
4242 case SvcId::GetProcessId:
4243 return SvcWrap_GetProcessId64(system);
4244 case SvcId::GetThreadId:
4245 return SvcWrap_GetThreadId64(system);
4246 case SvcId::Break:
4247 return SvcWrap_Break64(system);
4248 case SvcId::OutputDebugString:
4249 return SvcWrap_OutputDebugString64(system);
4250 case SvcId::ReturnFromException:
4251 return SvcWrap_ReturnFromException64(system);
4252 case SvcId::GetInfo:
4253 return SvcWrap_GetInfo64(system);
4254 case SvcId::FlushEntireDataCache:
4255 return SvcWrap_FlushEntireDataCache64(system);
4256 case SvcId::FlushDataCache:
4257 return SvcWrap_FlushDataCache64(system);
4258 case SvcId::MapPhysicalMemory:
4259 return SvcWrap_MapPhysicalMemory64(system);
4260 case SvcId::UnmapPhysicalMemory:
4261 return SvcWrap_UnmapPhysicalMemory64(system);
4262 case SvcId::GetDebugFutureThreadInfo:
4263 return SvcWrap_GetDebugFutureThreadInfo64(system);
4264 case SvcId::GetLastThreadInfo:
4265 return SvcWrap_GetLastThreadInfo64(system);
4266 case SvcId::GetResourceLimitLimitValue:
4267 return SvcWrap_GetResourceLimitLimitValue64(system);
4268 case SvcId::GetResourceLimitCurrentValue:
4269 return SvcWrap_GetResourceLimitCurrentValue64(system);
4270 case SvcId::SetThreadActivity:
4271 return SvcWrap_SetThreadActivity64(system);
4272 case SvcId::GetThreadContext3:
4273 return SvcWrap_GetThreadContext364(system);
4274 case SvcId::WaitForAddress:
4275 return SvcWrap_WaitForAddress64(system);
4276 case SvcId::SignalToAddress:
4277 return SvcWrap_SignalToAddress64(system);
4278 case SvcId::SynchronizePreemptionState:
4279 return SvcWrap_SynchronizePreemptionState64(system);
4280 case SvcId::GetResourceLimitPeakValue:
4281 return SvcWrap_GetResourceLimitPeakValue64(system);
4282 case SvcId::CreateIoPool:
4283 return SvcWrap_CreateIoPool64(system);
4284 case SvcId::CreateIoRegion:
4285 return SvcWrap_CreateIoRegion64(system);
4286 case SvcId::KernelDebug:
4287 return SvcWrap_KernelDebug64(system);
4288 case SvcId::ChangeKernelTraceState:
4289 return SvcWrap_ChangeKernelTraceState64(system);
4290 case SvcId::CreateSession:
4291 return SvcWrap_CreateSession64(system);
4292 case SvcId::AcceptSession:
4293 return SvcWrap_AcceptSession64(system);
4294 case SvcId::ReplyAndReceiveLight:
4295 return SvcWrap_ReplyAndReceiveLight64(system);
4296 case SvcId::ReplyAndReceive:
4297 return SvcWrap_ReplyAndReceive64(system);
4298 case SvcId::ReplyAndReceiveWithUserBuffer:
4299 return SvcWrap_ReplyAndReceiveWithUserBuffer64(system);
4300 case SvcId::CreateEvent:
4301 return SvcWrap_CreateEvent64(system);
4302 case SvcId::MapIoRegion:
4303 return SvcWrap_MapIoRegion64(system);
4304 case SvcId::UnmapIoRegion:
4305 return SvcWrap_UnmapIoRegion64(system);
4306 case SvcId::MapPhysicalMemoryUnsafe:
4307 return SvcWrap_MapPhysicalMemoryUnsafe64(system);
4308 case SvcId::UnmapPhysicalMemoryUnsafe:
4309 return SvcWrap_UnmapPhysicalMemoryUnsafe64(system);
4310 case SvcId::SetUnsafeLimit:
4311 return SvcWrap_SetUnsafeLimit64(system);
4312 case SvcId::CreateCodeMemory:
4313 return SvcWrap_CreateCodeMemory64(system);
4314 case SvcId::ControlCodeMemory:
4315 return SvcWrap_ControlCodeMemory64(system);
4316 case SvcId::SleepSystem:
4317 return SvcWrap_SleepSystem64(system);
4318 case SvcId::ReadWriteRegister:
4319 return SvcWrap_ReadWriteRegister64(system);
4320 case SvcId::SetProcessActivity:
4321 return SvcWrap_SetProcessActivity64(system);
4322 case SvcId::CreateSharedMemory:
4323 return SvcWrap_CreateSharedMemory64(system);
4324 case SvcId::MapTransferMemory:
4325 return SvcWrap_MapTransferMemory64(system);
4326 case SvcId::UnmapTransferMemory:
4327 return SvcWrap_UnmapTransferMemory64(system);
4328 case SvcId::CreateInterruptEvent:
4329 return SvcWrap_CreateInterruptEvent64(system);
4330 case SvcId::QueryPhysicalAddress:
4331 return SvcWrap_QueryPhysicalAddress64(system);
4332 case SvcId::QueryIoMapping:
4333 return SvcWrap_QueryIoMapping64(system);
4334 case SvcId::CreateDeviceAddressSpace:
4335 return SvcWrap_CreateDeviceAddressSpace64(system);
4336 case SvcId::AttachDeviceAddressSpace:
4337 return SvcWrap_AttachDeviceAddressSpace64(system);
4338 case SvcId::DetachDeviceAddressSpace:
4339 return SvcWrap_DetachDeviceAddressSpace64(system);
4340 case SvcId::MapDeviceAddressSpaceByForce:
4341 return SvcWrap_MapDeviceAddressSpaceByForce64(system);
4342 case SvcId::MapDeviceAddressSpaceAligned:
4343 return SvcWrap_MapDeviceAddressSpaceAligned64(system);
4344 case SvcId::UnmapDeviceAddressSpace:
4345 return SvcWrap_UnmapDeviceAddressSpace64(system);
4346 case SvcId::InvalidateProcessDataCache:
4347 return SvcWrap_InvalidateProcessDataCache64(system);
4348 case SvcId::StoreProcessDataCache:
4349 return SvcWrap_StoreProcessDataCache64(system);
4350 case SvcId::FlushProcessDataCache:
4351 return SvcWrap_FlushProcessDataCache64(system);
4352 case SvcId::DebugActiveProcess:
4353 return SvcWrap_DebugActiveProcess64(system);
4354 case SvcId::BreakDebugProcess:
4355 return SvcWrap_BreakDebugProcess64(system);
4356 case SvcId::TerminateDebugProcess:
4357 return SvcWrap_TerminateDebugProcess64(system);
4358 case SvcId::GetDebugEvent:
4359 return SvcWrap_GetDebugEvent64(system);
4360 case SvcId::ContinueDebugEvent:
4361 return SvcWrap_ContinueDebugEvent64(system);
4362 case SvcId::GetProcessList:
4363 return SvcWrap_GetProcessList64(system);
4364 case SvcId::GetThreadList:
4365 return SvcWrap_GetThreadList64(system);
4366 case SvcId::GetDebugThreadContext:
4367 return SvcWrap_GetDebugThreadContext64(system);
4368 case SvcId::SetDebugThreadContext:
4369 return SvcWrap_SetDebugThreadContext64(system);
4370 case SvcId::QueryDebugProcessMemory:
4371 return SvcWrap_QueryDebugProcessMemory64(system);
4372 case SvcId::ReadDebugProcessMemory:
4373 return SvcWrap_ReadDebugProcessMemory64(system);
4374 case SvcId::WriteDebugProcessMemory:
4375 return SvcWrap_WriteDebugProcessMemory64(system);
4376 case SvcId::SetHardwareBreakPoint:
4377 return SvcWrap_SetHardwareBreakPoint64(system);
4378 case SvcId::GetDebugThreadParam:
4379 return SvcWrap_GetDebugThreadParam64(system);
4380 case SvcId::GetSystemInfo:
4381 return SvcWrap_GetSystemInfo64(system);
4382 case SvcId::CreatePort:
4383 return SvcWrap_CreatePort64(system);
4384 case SvcId::ManageNamedPort:
4385 return SvcWrap_ManageNamedPort64(system);
4386 case SvcId::ConnectToPort:
4387 return SvcWrap_ConnectToPort64(system);
4388 case SvcId::SetProcessMemoryPermission:
4389 return SvcWrap_SetProcessMemoryPermission64(system);
4390 case SvcId::MapProcessMemory:
4391 return SvcWrap_MapProcessMemory64(system);
4392 case SvcId::UnmapProcessMemory:
4393 return SvcWrap_UnmapProcessMemory64(system);
4394 case SvcId::QueryProcessMemory:
4395 return SvcWrap_QueryProcessMemory64(system);
4396 case SvcId::MapProcessCodeMemory:
4397 return SvcWrap_MapProcessCodeMemory64(system);
4398 case SvcId::UnmapProcessCodeMemory:
4399 return SvcWrap_UnmapProcessCodeMemory64(system);
4400 case SvcId::CreateProcess:
4401 return SvcWrap_CreateProcess64(system);
4402 case SvcId::StartProcess:
4403 return SvcWrap_StartProcess64(system);
4404 case SvcId::TerminateProcess:
4405 return SvcWrap_TerminateProcess64(system);
4406 case SvcId::GetProcessInfo:
4407 return SvcWrap_GetProcessInfo64(system);
4408 case SvcId::CreateResourceLimit:
4409 return SvcWrap_CreateResourceLimit64(system);
4410 case SvcId::SetResourceLimitLimitValue:
4411 return SvcWrap_SetResourceLimitLimitValue64(system);
4412 case SvcId::CallSecureMonitor:
4413 return SvcWrap_CallSecureMonitor64(system);
4414 case SvcId::MapInsecureMemory:
4415 return SvcWrap_MapInsecureMemory64(system);
4416 case SvcId::UnmapInsecureMemory:
4417 return SvcWrap_UnmapInsecureMemory64(system);
4418 default:
4419 LOG_CRITICAL(Kernel_SVC, "Unknown SVC {:x}!", imm);
4420 break;
3106 } 4421 }
3107 return &SVC_Table_64[func_num];
3108} 4422}
4423// clang-format on
3109 4424
3110void Call(Core::System& system, u32 immediate) { 4425void Call(Core::System& system, u32 imm) {
3111 auto& kernel = system.Kernel(); 4426 auto& kernel = system.Kernel();
3112 kernel.EnterSVCProfile(); 4427 kernel.EnterSVCProfile();
3113 4428
3114 auto* thread = GetCurrentThreadPointer(kernel); 4429 if (system.CurrentProcess()->Is64BitProcess()) {
3115 thread->SetIsCallingSvc(); 4430 Call64(system, imm);
3116
3117 const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
3118 : GetSVCInfo32(immediate);
3119 if (info) {
3120 if (info->func) {
3121 info->func(system);
3122 } else {
3123 LOG_CRITICAL(Kernel_SVC, "Unimplemented SVC function {}(..)", info->name);
3124 }
3125 } else { 4431 } else {
3126 LOG_CRITICAL(Kernel_SVC, "Unknown SVC function 0x{:X}", immediate); 4432 Call32(system, imm);
3127 } 4433 }
3128 4434
3129 kernel.ExitSVCProfile(); 4435 kernel.ExitSVCProfile();
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 13f061b83..36e619959 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -1,16 +1,536 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#pragma once 4// This file is automatically generated using svc_generator.py.
5 5
6#include "common/common_types.h" 6#pragma once
7 7
8namespace Core { 8namespace Core {
9class System; 9class System;
10} 10}
11 11
12#include "common/common_types.h"
13#include "core/hle/kernel/svc_types.h"
14#include "core/hle/result.h"
15
12namespace Kernel::Svc { 16namespace Kernel::Svc {
13 17
14void Call(Core::System& system, u32 immediate); 18// clang-format off
19Result SetHeapSize(Core::System& system, uintptr_t* out_address, uint64_t size);
20Result SetMemoryPermission(Core::System& system, uint64_t address, uint64_t size, MemoryPermission perm);
21Result SetMemoryAttribute(Core::System& system, uint64_t address, uint64_t size, uint32_t mask, uint32_t attr);
22Result MapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
23Result UnmapMemory(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
24Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address);
25void ExitProcess(Core::System& system);
26Result CreateThread(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg, uint64_t stack_bottom, int32_t priority, int32_t core_id);
27Result StartThread(Core::System& system, Handle thread_handle);
28void ExitThread(Core::System& system);
29void SleepThread(Core::System& system, int64_t ns);
30Result GetThreadPriority(Core::System& system, int32_t* out_priority, Handle thread_handle);
31Result SetThreadPriority(Core::System& system, Handle thread_handle, int32_t priority);
32Result GetThreadCoreMask(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);
33Result SetThreadCoreMask(Core::System& system, Handle thread_handle, int32_t core_id, uint64_t affinity_mask);
34int32_t GetCurrentProcessorNumber(Core::System& system);
35Result SignalEvent(Core::System& system, Handle event_handle);
36Result ClearEvent(Core::System& system, Handle event_handle);
37Result MapSharedMemory(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size, MemoryPermission map_perm);
38Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size);
39Result CreateTransferMemory(Core::System& system, Handle* out_handle, uint64_t address, uint64_t size, MemoryPermission map_perm);
40Result CloseHandle(Core::System& system, Handle handle);
41Result ResetSignal(Core::System& system, Handle handle);
42Result WaitSynchronization(Core::System& system, int32_t* out_index, uint64_t handles, int32_t num_handles, int64_t timeout_ns);
43Result CancelSynchronization(Core::System& system, Handle handle);
44Result ArbitrateLock(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag);
45Result ArbitrateUnlock(Core::System& system, uint64_t address);
46Result WaitProcessWideKeyAtomic(Core::System& system, uint64_t address, uint64_t cv_key, uint32_t tag, int64_t timeout_ns);
47void SignalProcessWideKey(Core::System& system, uint64_t cv_key, int32_t count);
48int64_t GetSystemTick(Core::System& system);
49Result ConnectToNamedPort(Core::System& system, Handle* out_handle, uint64_t name);
50Result SendSyncRequest(Core::System& system, Handle session_handle);
51Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer, uint64_t message_buffer_size, Handle session_handle);
52Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle, uint64_t message_buffer, uint64_t message_buffer_size, Handle session_handle);
53Result GetProcessId(Core::System& system, uint64_t* out_process_id, Handle process_handle);
54Result GetThreadId(Core::System& system, uint64_t* out_thread_id, Handle thread_handle);
55void Break(Core::System& system, BreakReason break_reason, uint64_t arg, uint64_t size);
56Result OutputDebugString(Core::System& system, uint64_t debug_str, uint64_t len);
57void ReturnFromException(Core::System& system, Result result);
58Result GetInfo(Core::System& system, uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);
59void FlushEntireDataCache(Core::System& system);
60Result FlushDataCache(Core::System& system, uint64_t address, uint64_t size);
61Result MapPhysicalMemory(Core::System& system, uint64_t address, uint64_t size);
62Result UnmapPhysicalMemory(Core::System& system, uint64_t address, uint64_t size);
63Result GetDebugFutureThreadInfo(Core::System& system, lp64::LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);
64Result GetLastThreadInfo(Core::System& system, lp64::LastThreadContext* out_context, uintptr_t* out_tls_address, uint32_t* out_flags);
65Result GetResourceLimitLimitValue(Core::System& system, int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);
66Result GetResourceLimitCurrentValue(Core::System& system, int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);
67Result SetThreadActivity(Core::System& system, Handle thread_handle, ThreadActivity thread_activity);
68Result GetThreadContext3(Core::System& system, uint64_t out_context, Handle thread_handle);
69Result WaitForAddress(Core::System& system, uint64_t address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);
70Result SignalToAddress(Core::System& system, uint64_t address, SignalType signal_type, int32_t value, int32_t count);
71void SynchronizePreemptionState(Core::System& system);
72Result GetResourceLimitPeakValue(Core::System& system, int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);
73Result CreateIoPool(Core::System& system, Handle* out_handle, IoPoolType which);
74Result CreateIoRegion(Core::System& system, Handle* out_handle, Handle io_pool, uint64_t physical_address, uint64_t size, MemoryMapping mapping, MemoryPermission perm);
75void KernelDebug(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);
76void ChangeKernelTraceState(Core::System& system, KernelTraceState kern_trace_state);
77Result CreateSession(Core::System& system, Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, uint64_t name);
78Result AcceptSession(Core::System& system, Handle* out_handle, Handle port);
79Result ReplyAndReceive(Core::System& system, int32_t* out_index, uint64_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
80Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index, uint64_t message_buffer, uint64_t message_buffer_size, uint64_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
81Result CreateEvent(Core::System& system, Handle* out_write_handle, Handle* out_read_handle);
82Result MapIoRegion(Core::System& system, Handle io_region, uint64_t address, uint64_t size, MemoryPermission perm);
83Result UnmapIoRegion(Core::System& system, Handle io_region, uint64_t address, uint64_t size);
84Result MapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size);
85Result UnmapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size);
86Result SetUnsafeLimit(Core::System& system, uint64_t limit);
87Result CreateCodeMemory(Core::System& system, Handle* out_handle, uint64_t address, uint64_t size);
88Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);
89void SleepSystem(Core::System& system);
90Result ReadWriteRegister(Core::System& system, uint32_t* out_value, uint64_t address, uint32_t mask, uint32_t value);
91Result SetProcessActivity(Core::System& system, Handle process_handle, ProcessActivity process_activity);
92Result CreateSharedMemory(Core::System& system, Handle* out_handle, uint64_t size, MemoryPermission owner_perm, MemoryPermission remote_perm);
93Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, MemoryPermission owner_perm);
94Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size);
95Result CreateInterruptEvent(Core::System& system, Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);
96Result QueryPhysicalAddress(Core::System& system, lp64::PhysicalMemoryInfo* out_info, uint64_t address);
97Result QueryIoMapping(Core::System& system, uintptr_t* out_address, uintptr_t* out_size, uint64_t physical_address, uint64_t size);
98Result CreateDeviceAddressSpace(Core::System& system, Handle* out_handle, uint64_t das_address, uint64_t das_size);
99Result AttachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle);
100Result DetachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle);
101Result MapDeviceAddressSpaceByForce(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address, uint32_t option);
102Result MapDeviceAddressSpaceAligned(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address, uint32_t option);
103Result UnmapDeviceAddressSpace(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address);
104Result InvalidateProcessDataCache(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
105Result StoreProcessDataCache(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
106Result FlushProcessDataCache(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
107Result DebugActiveProcess(Core::System& system, Handle* out_handle, uint64_t process_id);
108Result BreakDebugProcess(Core::System& system, Handle debug_handle);
109Result TerminateDebugProcess(Core::System& system, Handle debug_handle);
110Result GetDebugEvent(Core::System& system, uint64_t out_info, Handle debug_handle);
111Result ContinueDebugEvent(Core::System& system, Handle debug_handle, uint32_t flags, uint64_t thread_ids, int32_t num_thread_ids);
112Result GetProcessList(Core::System& system, int32_t* out_num_processes, uint64_t out_process_ids, int32_t max_out_count);
113Result GetThreadList(Core::System& system, int32_t* out_num_threads, uint64_t out_thread_ids, int32_t max_out_count, Handle debug_handle);
114Result GetDebugThreadContext(Core::System& system, uint64_t out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);
115Result SetDebugThreadContext(Core::System& system, Handle debug_handle, uint64_t thread_id, uint64_t context, uint32_t context_flags);
116Result QueryDebugProcessMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
117Result ReadDebugProcessMemory(Core::System& system, uint64_t buffer, Handle debug_handle, uint64_t address, uint64_t size);
118Result WriteDebugProcessMemory(Core::System& system, Handle debug_handle, uint64_t buffer, uint64_t address, uint64_t size);
119Result SetHardwareBreakPoint(Core::System& system, HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);
120Result GetDebugThreadParam(Core::System& system, uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);
121Result GetSystemInfo(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);
122Result CreatePort(Core::System& system, Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, uint64_t name);
123Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t name, int32_t max_sessions);
124Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port);
125Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);
126Result MapProcessMemory(Core::System& system, uint64_t dst_address, Handle process_handle, uint64_t src_address, uint64_t size);
127Result UnmapProcessMemory(Core::System& system, uint64_t dst_address, Handle process_handle, uint64_t src_address, uint64_t size);
128Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
129Result MapProcessCodeMemory(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
130Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
131Result CreateProcess(Core::System& system, Handle* out_handle, uint64_t parameters, uint64_t caps, int32_t num_caps);
132Result StartProcess(Core::System& system, Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);
133Result TerminateProcess(Core::System& system, Handle process_handle);
134Result GetProcessInfo(Core::System& system, int64_t* out_info, Handle process_handle, ProcessInfoType info_type);
135Result CreateResourceLimit(Core::System& system, Handle* out_handle);
136Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle, LimitableResource which, int64_t limit_value);
137Result MapInsecureMemory(Core::System& system, uint64_t address, uint64_t size);
138Result UnmapInsecureMemory(Core::System& system, uint64_t address, uint64_t size);
139
140Result SetHeapSize64From32(Core::System& system, uintptr_t* out_address, uint32_t size);
141Result SetMemoryPermission64From32(Core::System& system, uint32_t address, uint32_t size, MemoryPermission perm);
142Result SetMemoryAttribute64From32(Core::System& system, uint32_t address, uint32_t size, uint32_t mask, uint32_t attr);
143Result MapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address, uint32_t size);
144Result UnmapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address, uint32_t size);
145Result QueryMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info, uint32_t address);
146void ExitProcess64From32(Core::System& system);
147Result CreateThread64From32(Core::System& system, Handle* out_handle, uint32_t func, uint32_t arg, uint32_t stack_bottom, int32_t priority, int32_t core_id);
148Result StartThread64From32(Core::System& system, Handle thread_handle);
149void ExitThread64From32(Core::System& system);
150void SleepThread64From32(Core::System& system, int64_t ns);
151Result GetThreadPriority64From32(Core::System& system, int32_t* out_priority, Handle thread_handle);
152Result SetThreadPriority64From32(Core::System& system, Handle thread_handle, int32_t priority);
153Result GetThreadCoreMask64From32(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);
154Result SetThreadCoreMask64From32(Core::System& system, Handle thread_handle, int32_t core_id, uint64_t affinity_mask);
155int32_t GetCurrentProcessorNumber64From32(Core::System& system);
156Result SignalEvent64From32(Core::System& system, Handle event_handle);
157Result ClearEvent64From32(Core::System& system, Handle event_handle);
158Result MapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address, uint32_t size, MemoryPermission map_perm);
159Result UnmapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address, uint32_t size);
160Result CreateTransferMemory64From32(Core::System& system, Handle* out_handle, uint32_t address, uint32_t size, MemoryPermission map_perm);
161Result CloseHandle64From32(Core::System& system, Handle handle);
162Result ResetSignal64From32(Core::System& system, Handle handle);
163Result WaitSynchronization64From32(Core::System& system, int32_t* out_index, uint32_t handles, int32_t num_handles, int64_t timeout_ns);
164Result CancelSynchronization64From32(Core::System& system, Handle handle);
165Result ArbitrateLock64From32(Core::System& system, Handle thread_handle, uint32_t address, uint32_t tag);
166Result ArbitrateUnlock64From32(Core::System& system, uint32_t address);
167Result WaitProcessWideKeyAtomic64From32(Core::System& system, uint32_t address, uint32_t cv_key, uint32_t tag, int64_t timeout_ns);
168void SignalProcessWideKey64From32(Core::System& system, uint32_t cv_key, int32_t count);
169int64_t GetSystemTick64From32(Core::System& system);
170Result ConnectToNamedPort64From32(Core::System& system, Handle* out_handle, uint32_t name);
171Result SendSyncRequest64From32(Core::System& system, Handle session_handle);
172Result SendSyncRequestWithUserBuffer64From32(Core::System& system, uint32_t message_buffer, uint32_t message_buffer_size, Handle session_handle);
173Result SendAsyncRequestWithUserBuffer64From32(Core::System& system, Handle* out_event_handle, uint32_t message_buffer, uint32_t message_buffer_size, Handle session_handle);
174Result GetProcessId64From32(Core::System& system, uint64_t* out_process_id, Handle process_handle);
175Result GetThreadId64From32(Core::System& system, uint64_t* out_thread_id, Handle thread_handle);
176void Break64From32(Core::System& system, BreakReason break_reason, uint32_t arg, uint32_t size);
177Result OutputDebugString64From32(Core::System& system, uint32_t debug_str, uint32_t len);
178void ReturnFromException64From32(Core::System& system, Result result);
179Result GetInfo64From32(Core::System& system, uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);
180void FlushEntireDataCache64From32(Core::System& system);
181Result FlushDataCache64From32(Core::System& system, uint32_t address, uint32_t size);
182Result MapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size);
183Result UnmapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size);
184Result GetDebugFutureThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);
185Result GetLastThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context, uintptr_t* out_tls_address, uint32_t* out_flags);
186Result GetResourceLimitLimitValue64From32(Core::System& system, int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);
187Result GetResourceLimitCurrentValue64From32(Core::System& system, int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);
188Result SetThreadActivity64From32(Core::System& system, Handle thread_handle, ThreadActivity thread_activity);
189Result GetThreadContext364From32(Core::System& system, uint32_t out_context, Handle thread_handle);
190Result WaitForAddress64From32(Core::System& system, uint32_t address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);
191Result SignalToAddress64From32(Core::System& system, uint32_t address, SignalType signal_type, int32_t value, int32_t count);
192void SynchronizePreemptionState64From32(Core::System& system);
193Result GetResourceLimitPeakValue64From32(Core::System& system, int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);
194Result CreateIoPool64From32(Core::System& system, Handle* out_handle, IoPoolType which);
195Result CreateIoRegion64From32(Core::System& system, Handle* out_handle, Handle io_pool, uint64_t physical_address, uint32_t size, MemoryMapping mapping, MemoryPermission perm);
196void KernelDebug64From32(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);
197void ChangeKernelTraceState64From32(Core::System& system, KernelTraceState kern_trace_state);
198Result CreateSession64From32(Core::System& system, Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, uint32_t name);
199Result AcceptSession64From32(Core::System& system, Handle* out_handle, Handle port);
200Result ReplyAndReceive64From32(Core::System& system, int32_t* out_index, uint32_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
201Result ReplyAndReceiveWithUserBuffer64From32(Core::System& system, int32_t* out_index, uint32_t message_buffer, uint32_t message_buffer_size, uint32_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
202Result CreateEvent64From32(Core::System& system, Handle* out_write_handle, Handle* out_read_handle);
203Result MapIoRegion64From32(Core::System& system, Handle io_region, uint32_t address, uint32_t size, MemoryPermission perm);
204Result UnmapIoRegion64From32(Core::System& system, Handle io_region, uint32_t address, uint32_t size);
205Result MapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size);
206Result UnmapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size);
207Result SetUnsafeLimit64From32(Core::System& system, uint32_t limit);
208Result CreateCodeMemory64From32(Core::System& system, Handle* out_handle, uint32_t address, uint32_t size);
209Result ControlCodeMemory64From32(Core::System& system, Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);
210void SleepSystem64From32(Core::System& system);
211Result ReadWriteRegister64From32(Core::System& system, uint32_t* out_value, uint64_t address, uint32_t mask, uint32_t value);
212Result SetProcessActivity64From32(Core::System& system, Handle process_handle, ProcessActivity process_activity);
213Result CreateSharedMemory64From32(Core::System& system, Handle* out_handle, uint32_t size, MemoryPermission owner_perm, MemoryPermission remote_perm);
214Result MapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address, uint32_t size, MemoryPermission owner_perm);
215Result UnmapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address, uint32_t size);
216Result CreateInterruptEvent64From32(Core::System& system, Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);
217Result QueryPhysicalAddress64From32(Core::System& system, ilp32::PhysicalMemoryInfo* out_info, uint32_t address);
218Result QueryIoMapping64From32(Core::System& system, uintptr_t* out_address, uintptr_t* out_size, uint64_t physical_address, uint32_t size);
219Result CreateDeviceAddressSpace64From32(Core::System& system, Handle* out_handle, uint64_t das_address, uint64_t das_size);
220Result AttachDeviceAddressSpace64From32(Core::System& system, DeviceName device_name, Handle das_handle);
221Result DetachDeviceAddressSpace64From32(Core::System& system, DeviceName device_name, Handle das_handle);
222Result MapDeviceAddressSpaceByForce64From32(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint32_t size, uint64_t device_address, uint32_t option);
223Result MapDeviceAddressSpaceAligned64From32(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint32_t size, uint64_t device_address, uint32_t option);
224Result UnmapDeviceAddressSpace64From32(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint32_t size, uint64_t device_address);
225Result InvalidateProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
226Result StoreProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
227Result FlushProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
228Result DebugActiveProcess64From32(Core::System& system, Handle* out_handle, uint64_t process_id);
229Result BreakDebugProcess64From32(Core::System& system, Handle debug_handle);
230Result TerminateDebugProcess64From32(Core::System& system, Handle debug_handle);
231Result GetDebugEvent64From32(Core::System& system, uint32_t out_info, Handle debug_handle);
232Result ContinueDebugEvent64From32(Core::System& system, Handle debug_handle, uint32_t flags, uint32_t thread_ids, int32_t num_thread_ids);
233Result GetProcessList64From32(Core::System& system, int32_t* out_num_processes, uint32_t out_process_ids, int32_t max_out_count);
234Result GetThreadList64From32(Core::System& system, int32_t* out_num_threads, uint32_t out_thread_ids, int32_t max_out_count, Handle debug_handle);
235Result GetDebugThreadContext64From32(Core::System& system, uint32_t out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);
236Result SetDebugThreadContext64From32(Core::System& system, Handle debug_handle, uint64_t thread_id, uint32_t context, uint32_t context_flags);
237Result QueryDebugProcessMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint32_t address);
238Result ReadDebugProcessMemory64From32(Core::System& system, uint32_t buffer, Handle debug_handle, uint32_t address, uint32_t size);
239Result WriteDebugProcessMemory64From32(Core::System& system, Handle debug_handle, uint32_t buffer, uint32_t address, uint32_t size);
240Result SetHardwareBreakPoint64From32(Core::System& system, HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);
241Result GetDebugThreadParam64From32(Core::System& system, uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);
242Result GetSystemInfo64From32(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);
243Result CreatePort64From32(Core::System& system, Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, uint32_t name);
244Result ManageNamedPort64From32(Core::System& system, Handle* out_server_handle, uint32_t name, int32_t max_sessions);
245Result ConnectToPort64From32(Core::System& system, Handle* out_handle, Handle port);
246Result SetProcessMemoryPermission64From32(Core::System& system, Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);
247Result MapProcessMemory64From32(Core::System& system, uint32_t dst_address, Handle process_handle, uint64_t src_address, uint32_t size);
248Result UnmapProcessMemory64From32(Core::System& system, uint32_t dst_address, Handle process_handle, uint64_t src_address, uint32_t size);
249Result QueryProcessMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
250Result MapProcessCodeMemory64From32(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
251Result UnmapProcessCodeMemory64From32(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
252Result CreateProcess64From32(Core::System& system, Handle* out_handle, uint32_t parameters, uint32_t caps, int32_t num_caps);
253Result StartProcess64From32(Core::System& system, Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);
254Result TerminateProcess64From32(Core::System& system, Handle process_handle);
255Result GetProcessInfo64From32(Core::System& system, int64_t* out_info, Handle process_handle, ProcessInfoType info_type);
256Result CreateResourceLimit64From32(Core::System& system, Handle* out_handle);
257Result SetResourceLimitLimitValue64From32(Core::System& system, Handle resource_limit_handle, LimitableResource which, int64_t limit_value);
258Result MapInsecureMemory64From32(Core::System& system, uint32_t address, uint32_t size);
259Result UnmapInsecureMemory64From32(Core::System& system, uint32_t address, uint32_t size);
260
261Result SetHeapSize64(Core::System& system, uintptr_t* out_address, uint64_t size);
262Result SetMemoryPermission64(Core::System& system, uint64_t address, uint64_t size, MemoryPermission perm);
263Result SetMemoryAttribute64(Core::System& system, uint64_t address, uint64_t size, uint32_t mask, uint32_t attr);
264Result MapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
265Result UnmapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address, uint64_t size);
266Result QueryMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, uint64_t address);
267void ExitProcess64(Core::System& system);
268Result CreateThread64(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg, uint64_t stack_bottom, int32_t priority, int32_t core_id);
269Result StartThread64(Core::System& system, Handle thread_handle);
270void ExitThread64(Core::System& system);
271void SleepThread64(Core::System& system, int64_t ns);
272Result GetThreadPriority64(Core::System& system, int32_t* out_priority, Handle thread_handle);
273Result SetThreadPriority64(Core::System& system, Handle thread_handle, int32_t priority);
274Result GetThreadCoreMask64(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);
275Result SetThreadCoreMask64(Core::System& system, Handle thread_handle, int32_t core_id, uint64_t affinity_mask);
276int32_t GetCurrentProcessorNumber64(Core::System& system);
277Result SignalEvent64(Core::System& system, Handle event_handle);
278Result ClearEvent64(Core::System& system, Handle event_handle);
279Result MapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size, MemoryPermission map_perm);
280Result UnmapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size);
281Result CreateTransferMemory64(Core::System& system, Handle* out_handle, uint64_t address, uint64_t size, MemoryPermission map_perm);
282Result CloseHandle64(Core::System& system, Handle handle);
283Result ResetSignal64(Core::System& system, Handle handle);
284Result WaitSynchronization64(Core::System& system, int32_t* out_index, uint64_t handles, int32_t num_handles, int64_t timeout_ns);
285Result CancelSynchronization64(Core::System& system, Handle handle);
286Result ArbitrateLock64(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag);
287Result ArbitrateUnlock64(Core::System& system, uint64_t address);
288Result WaitProcessWideKeyAtomic64(Core::System& system, uint64_t address, uint64_t cv_key, uint32_t tag, int64_t timeout_ns);
289void SignalProcessWideKey64(Core::System& system, uint64_t cv_key, int32_t count);
290int64_t GetSystemTick64(Core::System& system);
291Result ConnectToNamedPort64(Core::System& system, Handle* out_handle, uint64_t name);
292Result SendSyncRequest64(Core::System& system, Handle session_handle);
293Result SendSyncRequestWithUserBuffer64(Core::System& system, uint64_t message_buffer, uint64_t message_buffer_size, Handle session_handle);
294Result SendAsyncRequestWithUserBuffer64(Core::System& system, Handle* out_event_handle, uint64_t message_buffer, uint64_t message_buffer_size, Handle session_handle);
295Result GetProcessId64(Core::System& system, uint64_t* out_process_id, Handle process_handle);
296Result GetThreadId64(Core::System& system, uint64_t* out_thread_id, Handle thread_handle);
297void Break64(Core::System& system, BreakReason break_reason, uint64_t arg, uint64_t size);
298Result OutputDebugString64(Core::System& system, uint64_t debug_str, uint64_t len);
299void ReturnFromException64(Core::System& system, Result result);
300Result GetInfo64(Core::System& system, uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);
301void FlushEntireDataCache64(Core::System& system);
302Result FlushDataCache64(Core::System& system, uint64_t address, uint64_t size);
303Result MapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size);
304Result UnmapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size);
305Result GetDebugFutureThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);
306Result GetLastThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context, uintptr_t* out_tls_address, uint32_t* out_flags);
307Result GetResourceLimitLimitValue64(Core::System& system, int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);
308Result GetResourceLimitCurrentValue64(Core::System& system, int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);
309Result SetThreadActivity64(Core::System& system, Handle thread_handle, ThreadActivity thread_activity);
310Result GetThreadContext364(Core::System& system, uint64_t out_context, Handle thread_handle);
311Result WaitForAddress64(Core::System& system, uint64_t address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);
312Result SignalToAddress64(Core::System& system, uint64_t address, SignalType signal_type, int32_t value, int32_t count);
313void SynchronizePreemptionState64(Core::System& system);
314Result GetResourceLimitPeakValue64(Core::System& system, int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);
315Result CreateIoPool64(Core::System& system, Handle* out_handle, IoPoolType which);
316Result CreateIoRegion64(Core::System& system, Handle* out_handle, Handle io_pool, uint64_t physical_address, uint64_t size, MemoryMapping mapping, MemoryPermission perm);
317void KernelDebug64(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);
318void ChangeKernelTraceState64(Core::System& system, KernelTraceState kern_trace_state);
319Result CreateSession64(Core::System& system, Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, uint64_t name);
320Result AcceptSession64(Core::System& system, Handle* out_handle, Handle port);
321Result ReplyAndReceive64(Core::System& system, int32_t* out_index, uint64_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
322Result ReplyAndReceiveWithUserBuffer64(Core::System& system, int32_t* out_index, uint64_t message_buffer, uint64_t message_buffer_size, uint64_t handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);
323Result CreateEvent64(Core::System& system, Handle* out_write_handle, Handle* out_read_handle);
324Result MapIoRegion64(Core::System& system, Handle io_region, uint64_t address, uint64_t size, MemoryPermission perm);
325Result UnmapIoRegion64(Core::System& system, Handle io_region, uint64_t address, uint64_t size);
326Result MapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size);
327Result UnmapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size);
328Result SetUnsafeLimit64(Core::System& system, uint64_t limit);
329Result CreateCodeMemory64(Core::System& system, Handle* out_handle, uint64_t address, uint64_t size);
330Result ControlCodeMemory64(Core::System& system, Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);
331void SleepSystem64(Core::System& system);
332Result ReadWriteRegister64(Core::System& system, uint32_t* out_value, uint64_t address, uint32_t mask, uint32_t value);
333Result SetProcessActivity64(Core::System& system, Handle process_handle, ProcessActivity process_activity);
334Result CreateSharedMemory64(Core::System& system, Handle* out_handle, uint64_t size, MemoryPermission owner_perm, MemoryPermission remote_perm);
335Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, MemoryPermission owner_perm);
336Result UnmapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size);
337Result CreateInterruptEvent64(Core::System& system, Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);
338Result QueryPhysicalAddress64(Core::System& system, lp64::PhysicalMemoryInfo* out_info, uint64_t address);
339Result QueryIoMapping64(Core::System& system, uintptr_t* out_address, uintptr_t* out_size, uint64_t physical_address, uint64_t size);
340Result CreateDeviceAddressSpace64(Core::System& system, Handle* out_handle, uint64_t das_address, uint64_t das_size);
341Result AttachDeviceAddressSpace64(Core::System& system, DeviceName device_name, Handle das_handle);
342Result DetachDeviceAddressSpace64(Core::System& system, DeviceName device_name, Handle das_handle);
343Result MapDeviceAddressSpaceByForce64(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address, uint32_t option);
344Result MapDeviceAddressSpaceAligned64(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address, uint32_t option);
345Result UnmapDeviceAddressSpace64(Core::System& system, Handle das_handle, Handle process_handle, uint64_t process_address, uint64_t size, uint64_t device_address);
346Result InvalidateProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
347Result StoreProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
348Result FlushProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address, uint64_t size);
349Result DebugActiveProcess64(Core::System& system, Handle* out_handle, uint64_t process_id);
350Result BreakDebugProcess64(Core::System& system, Handle debug_handle);
351Result TerminateDebugProcess64(Core::System& system, Handle debug_handle);
352Result GetDebugEvent64(Core::System& system, uint64_t out_info, Handle debug_handle);
353Result ContinueDebugEvent64(Core::System& system, Handle debug_handle, uint32_t flags, uint64_t thread_ids, int32_t num_thread_ids);
354Result GetProcessList64(Core::System& system, int32_t* out_num_processes, uint64_t out_process_ids, int32_t max_out_count);
355Result GetThreadList64(Core::System& system, int32_t* out_num_threads, uint64_t out_thread_ids, int32_t max_out_count, Handle debug_handle);
356Result GetDebugThreadContext64(Core::System& system, uint64_t out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);
357Result SetDebugThreadContext64(Core::System& system, Handle debug_handle, uint64_t thread_id, uint64_t context, uint32_t context_flags);
358Result QueryDebugProcessMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
359Result ReadDebugProcessMemory64(Core::System& system, uint64_t buffer, Handle debug_handle, uint64_t address, uint64_t size);
360Result WriteDebugProcessMemory64(Core::System& system, Handle debug_handle, uint64_t buffer, uint64_t address, uint64_t size);
361Result SetHardwareBreakPoint64(Core::System& system, HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);
362Result GetDebugThreadParam64(Core::System& system, uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);
363Result GetSystemInfo64(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);
364Result CreatePort64(Core::System& system, Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, uint64_t name);
365Result ManageNamedPort64(Core::System& system, Handle* out_server_handle, uint64_t name, int32_t max_sessions);
366Result ConnectToPort64(Core::System& system, Handle* out_handle, Handle port);
367Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);
368Result MapProcessMemory64(Core::System& system, uint64_t dst_address, Handle process_handle, uint64_t src_address, uint64_t size);
369Result UnmapProcessMemory64(Core::System& system, uint64_t dst_address, Handle process_handle, uint64_t src_address, uint64_t size);
370Result QueryProcessMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);
371Result MapProcessCodeMemory64(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
372Result UnmapProcessCodeMemory64(Core::System& system, Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);
373Result CreateProcess64(Core::System& system, Handle* out_handle, uint64_t parameters, uint64_t caps, int32_t num_caps);
374Result StartProcess64(Core::System& system, Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);
375Result TerminateProcess64(Core::System& system, Handle process_handle);
376Result GetProcessInfo64(Core::System& system, int64_t* out_info, Handle process_handle, ProcessInfoType info_type);
377Result CreateResourceLimit64(Core::System& system, Handle* out_handle);
378Result SetResourceLimitLimitValue64(Core::System& system, Handle resource_limit_handle, LimitableResource which, int64_t limit_value);
379Result MapInsecureMemory64(Core::System& system, uint64_t address, uint64_t size);
380Result UnmapInsecureMemory64(Core::System& system, uint64_t address, uint64_t size);
381
382enum class SvcId : u32 {
383 SetHeapSize = 0x1,
384 SetMemoryPermission = 0x2,
385 SetMemoryAttribute = 0x3,
386 MapMemory = 0x4,
387 UnmapMemory = 0x5,
388 QueryMemory = 0x6,
389 ExitProcess = 0x7,
390 CreateThread = 0x8,
391 StartThread = 0x9,
392 ExitThread = 0xa,
393 SleepThread = 0xb,
394 GetThreadPriority = 0xc,
395 SetThreadPriority = 0xd,
396 GetThreadCoreMask = 0xe,
397 SetThreadCoreMask = 0xf,
398 GetCurrentProcessorNumber = 0x10,
399 SignalEvent = 0x11,
400 ClearEvent = 0x12,
401 MapSharedMemory = 0x13,
402 UnmapSharedMemory = 0x14,
403 CreateTransferMemory = 0x15,
404 CloseHandle = 0x16,
405 ResetSignal = 0x17,
406 WaitSynchronization = 0x18,
407 CancelSynchronization = 0x19,
408 ArbitrateLock = 0x1a,
409 ArbitrateUnlock = 0x1b,
410 WaitProcessWideKeyAtomic = 0x1c,
411 SignalProcessWideKey = 0x1d,
412 GetSystemTick = 0x1e,
413 ConnectToNamedPort = 0x1f,
414 SendSyncRequestLight = 0x20,
415 SendSyncRequest = 0x21,
416 SendSyncRequestWithUserBuffer = 0x22,
417 SendAsyncRequestWithUserBuffer = 0x23,
418 GetProcessId = 0x24,
419 GetThreadId = 0x25,
420 Break = 0x26,
421 OutputDebugString = 0x27,
422 ReturnFromException = 0x28,
423 GetInfo = 0x29,
424 FlushEntireDataCache = 0x2a,
425 FlushDataCache = 0x2b,
426 MapPhysicalMemory = 0x2c,
427 UnmapPhysicalMemory = 0x2d,
428 GetDebugFutureThreadInfo = 0x2e,
429 GetLastThreadInfo = 0x2f,
430 GetResourceLimitLimitValue = 0x30,
431 GetResourceLimitCurrentValue = 0x31,
432 SetThreadActivity = 0x32,
433 GetThreadContext3 = 0x33,
434 WaitForAddress = 0x34,
435 SignalToAddress = 0x35,
436 SynchronizePreemptionState = 0x36,
437 GetResourceLimitPeakValue = 0x37,
438 CreateIoPool = 0x39,
439 CreateIoRegion = 0x3a,
440 KernelDebug = 0x3c,
441 ChangeKernelTraceState = 0x3d,
442 CreateSession = 0x40,
443 AcceptSession = 0x41,
444 ReplyAndReceiveLight = 0x42,
445 ReplyAndReceive = 0x43,
446 ReplyAndReceiveWithUserBuffer = 0x44,
447 CreateEvent = 0x45,
448 MapIoRegion = 0x46,
449 UnmapIoRegion = 0x47,
450 MapPhysicalMemoryUnsafe = 0x48,
451 UnmapPhysicalMemoryUnsafe = 0x49,
452 SetUnsafeLimit = 0x4a,
453 CreateCodeMemory = 0x4b,
454 ControlCodeMemory = 0x4c,
455 SleepSystem = 0x4d,
456 ReadWriteRegister = 0x4e,
457 SetProcessActivity = 0x4f,
458 CreateSharedMemory = 0x50,
459 MapTransferMemory = 0x51,
460 UnmapTransferMemory = 0x52,
461 CreateInterruptEvent = 0x53,
462 QueryPhysicalAddress = 0x54,
463 QueryIoMapping = 0x55,
464 CreateDeviceAddressSpace = 0x56,
465 AttachDeviceAddressSpace = 0x57,
466 DetachDeviceAddressSpace = 0x58,
467 MapDeviceAddressSpaceByForce = 0x59,
468 MapDeviceAddressSpaceAligned = 0x5a,
469 UnmapDeviceAddressSpace = 0x5c,
470 InvalidateProcessDataCache = 0x5d,
471 StoreProcessDataCache = 0x5e,
472 FlushProcessDataCache = 0x5f,
473 DebugActiveProcess = 0x60,
474 BreakDebugProcess = 0x61,
475 TerminateDebugProcess = 0x62,
476 GetDebugEvent = 0x63,
477 ContinueDebugEvent = 0x64,
478 GetProcessList = 0x65,
479 GetThreadList = 0x66,
480 GetDebugThreadContext = 0x67,
481 SetDebugThreadContext = 0x68,
482 QueryDebugProcessMemory = 0x69,
483 ReadDebugProcessMemory = 0x6a,
484 WriteDebugProcessMemory = 0x6b,
485 SetHardwareBreakPoint = 0x6c,
486 GetDebugThreadParam = 0x6d,
487 GetSystemInfo = 0x6f,
488 CreatePort = 0x70,
489 ManageNamedPort = 0x71,
490 ConnectToPort = 0x72,
491 SetProcessMemoryPermission = 0x73,
492 MapProcessMemory = 0x74,
493 UnmapProcessMemory = 0x75,
494 QueryProcessMemory = 0x76,
495 MapProcessCodeMemory = 0x77,
496 UnmapProcessCodeMemory = 0x78,
497 CreateProcess = 0x79,
498 StartProcess = 0x7a,
499 TerminateProcess = 0x7b,
500 GetProcessInfo = 0x7c,
501 CreateResourceLimit = 0x7d,
502 SetResourceLimitLimitValue = 0x7e,
503 CallSecureMonitor = 0x7f,
504 MapInsecureMemory = 0x90,
505 UnmapInsecureMemory = 0x91,
506};
507// clang-format on
508
509// Custom ABI.
510Result ReplyAndReceiveLight(Core::System& system, Handle handle, uint32_t* args);
511Result ReplyAndReceiveLight64From32(Core::System& system, Handle handle, uint32_t* args);
512Result ReplyAndReceiveLight64(Core::System& system, Handle handle, uint32_t* args);
513
514Result SendSyncRequestLight(Core::System& system, Handle session_handle, uint32_t* args);
515Result SendSyncRequestLight64From32(Core::System& system, Handle session_handle, uint32_t* args);
516Result SendSyncRequestLight64(Core::System& system, Handle session_handle, uint32_t* args);
517
518void CallSecureMonitor(Core::System& system, lp64::SecureMonitorArguments* args);
519void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArguments* args);
520void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args);
521
522// Defined in svc_light_ipc.cpp.
523void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system);
524void SvcWrap_ReplyAndReceiveLight64(Core::System& system);
525
526void SvcWrap_SendSyncRequestLight64From32(Core::System& system);
527void SvcWrap_SendSyncRequestLight64(Core::System& system);
528
529// Defined in svc_secure_monitor_call.cpp.
530void SvcWrap_CallSecureMonitor64From32(Core::System& system);
531void SvcWrap_CallSecureMonitor64(Core::System& system);
532
533// Perform a supervisor call by index.
534void Call(Core::System& system, u32 imm);
15 535
16} // namespace Kernel::Svc 536} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_activity.cpp b/src/core/hle/kernel/svc/svc_activity.cpp
new file mode 100644
index 000000000..1dcdb7a15
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_activity.cpp
@@ -0,0 +1,65 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/k_thread.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/kernel/svc_results.h"
9
10namespace Kernel::Svc {
11
12/// Sets the thread activity
13Result SetThreadActivity(Core::System& system, Handle thread_handle,
14 ThreadActivity thread_activity) {
15 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
16 thread_activity);
17
18 // Validate the activity.
19 constexpr auto IsValidThreadActivity = [](ThreadActivity activity) {
20 return activity == ThreadActivity::Runnable || activity == ThreadActivity::Paused;
21 };
22 R_UNLESS(IsValidThreadActivity(thread_activity), ResultInvalidEnumValue);
23
24 // Get the thread from its handle.
25 KScopedAutoObject thread =
26 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
27 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
28
29 // Check that the activity is being set on a non-current thread for the current process.
30 R_UNLESS(thread->GetOwnerProcess() == system.Kernel().CurrentProcess(), ResultInvalidHandle);
31 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(system.Kernel()), ResultBusy);
32
33 // Set the activity.
34 R_TRY(thread->SetActivity(thread_activity));
35
36 return ResultSuccess;
37}
38
39Result SetProcessActivity(Core::System& system, Handle process_handle,
40 ProcessActivity process_activity) {
41 UNIMPLEMENTED();
42 R_THROW(ResultNotImplemented);
43}
44
45Result SetThreadActivity64(Core::System& system, Handle thread_handle,
46 ThreadActivity thread_activity) {
47 return SetThreadActivity(system, thread_handle, thread_activity);
48}
49
50Result SetProcessActivity64(Core::System& system, Handle process_handle,
51 ProcessActivity process_activity) {
52 return SetProcessActivity(system, process_handle, process_activity);
53}
54
55Result SetThreadActivity64From32(Core::System& system, Handle thread_handle,
56 ThreadActivity thread_activity) {
57 return SetThreadActivity(system, thread_handle, thread_activity);
58}
59
60Result SetProcessActivity64From32(Core::System& system, Handle process_handle,
61 ProcessActivity process_activity) {
62 return SetProcessActivity(system, process_handle, process_activity);
63}
64
65} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_address_arbiter.cpp b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
new file mode 100644
index 000000000..e6a5d2ae5
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_address_arbiter.cpp
@@ -0,0 +1,122 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/kernel/svc_results.h"
10#include "core/hle/kernel/svc_types.h"
11
12namespace Kernel::Svc {
13namespace {
14
15constexpr bool IsValidSignalType(Svc::SignalType type) {
16 switch (type) {
17 case Svc::SignalType::Signal:
18 case Svc::SignalType::SignalAndIncrementIfEqual:
19 case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
20 return true;
21 default:
22 return false;
23 }
24}
25
26constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
27 switch (type) {
28 case Svc::ArbitrationType::WaitIfLessThan:
29 case Svc::ArbitrationType::DecrementAndWaitIfLessThan:
30 case Svc::ArbitrationType::WaitIfEqual:
31 return true;
32 default:
33 return false;
34 }
35}
36
37} // namespace
38
39// Wait for an address (via Address Arbiter)
40Result WaitForAddress(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
41 s64 timeout_ns) {
42 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
43 address, arb_type, value, timeout_ns);
44
45 // Validate input.
46 if (IsKernelAddress(address)) {
47 LOG_ERROR(Kernel_SVC, "Attempting to wait on kernel address (address={:08X})", address);
48 return ResultInvalidCurrentMemory;
49 }
50 if (!Common::IsAligned(address, sizeof(s32))) {
51 LOG_ERROR(Kernel_SVC, "Wait address must be 4 byte aligned (address={:08X})", address);
52 return ResultInvalidAddress;
53 }
54 if (!IsValidArbitrationType(arb_type)) {
55 LOG_ERROR(Kernel_SVC, "Invalid arbitration type specified (type={})", arb_type);
56 return ResultInvalidEnumValue;
57 }
58
59 // Convert timeout from nanoseconds to ticks.
60 s64 timeout{};
61 if (timeout_ns > 0) {
62 const s64 offset_tick(timeout_ns);
63 if (offset_tick > 0) {
64 timeout = offset_tick + 2;
65 if (timeout <= 0) {
66 timeout = std::numeric_limits<s64>::max();
67 }
68 } else {
69 timeout = std::numeric_limits<s64>::max();
70 }
71 } else {
72 timeout = timeout_ns;
73 }
74
75 return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
76}
77
78// Signals to an address (via Address Arbiter)
79Result SignalToAddress(Core::System& system, VAddr address, SignalType signal_type, s32 value,
80 s32 count) {
81 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
82 address, signal_type, value, count);
83
84 // Validate input.
85 if (IsKernelAddress(address)) {
86 LOG_ERROR(Kernel_SVC, "Attempting to signal to a kernel address (address={:08X})", address);
87 return ResultInvalidCurrentMemory;
88 }
89 if (!Common::IsAligned(address, sizeof(s32))) {
90 LOG_ERROR(Kernel_SVC, "Signaled address must be 4 byte aligned (address={:08X})", address);
91 return ResultInvalidAddress;
92 }
93 if (!IsValidSignalType(signal_type)) {
94 LOG_ERROR(Kernel_SVC, "Invalid signal type specified (type={})", signal_type);
95 return ResultInvalidEnumValue;
96 }
97
98 return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value,
99 count);
100}
101
102Result WaitForAddress64(Core::System& system, VAddr address, ArbitrationType arb_type, s32 value,
103 s64 timeout_ns) {
104 return WaitForAddress(system, address, arb_type, value, timeout_ns);
105}
106
107Result SignalToAddress64(Core::System& system, VAddr address, SignalType signal_type, s32 value,
108 s32 count) {
109 return SignalToAddress(system, address, signal_type, value, count);
110}
111
112Result WaitForAddress64From32(Core::System& system, u32 address, ArbitrationType arb_type,
113 s32 value, s64 timeout_ns) {
114 return WaitForAddress(system, address, arb_type, value, timeout_ns);
115}
116
117Result SignalToAddress64From32(Core::System& system, u32 address, SignalType signal_type, s32 value,
118 s32 count) {
119 return SignalToAddress(system, address, signal_type, value, count);
120}
121
122} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_address_translation.cpp b/src/core/hle/kernel/svc/svc_address_translation.cpp
new file mode 100644
index 000000000..c25e144cd
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_address_translation.cpp
@@ -0,0 +1,50 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5#include "core/hle/kernel/svc_results.h"
6
7namespace Kernel::Svc {
8
9Result QueryPhysicalAddress(Core::System& system, lp64::PhysicalMemoryInfo* out_info,
10 uint64_t address) {
11 UNIMPLEMENTED();
12 R_THROW(ResultNotImplemented);
13}
14
15Result QueryIoMapping(Core::System& system, uintptr_t* out_address, uintptr_t* out_size,
16 uint64_t physical_address, uint64_t size) {
17 UNIMPLEMENTED();
18 R_THROW(ResultNotImplemented);
19}
20
21Result QueryPhysicalAddress64(Core::System& system, lp64::PhysicalMemoryInfo* out_info,
22 uint64_t address) {
23 R_RETURN(QueryPhysicalAddress(system, out_info, address));
24}
25
26Result QueryIoMapping64(Core::System& system, uintptr_t* out_address, uintptr_t* out_size,
27 uint64_t physical_address, uint64_t size) {
28 R_RETURN(QueryIoMapping(system, out_address, out_size, physical_address, size));
29}
30
31Result QueryPhysicalAddress64From32(Core::System& system, ilp32::PhysicalMemoryInfo* out_info,
32 uint32_t address) {
33 lp64::PhysicalMemoryInfo info{};
34 R_TRY(QueryPhysicalAddress(system, std::addressof(info), address));
35
36 *out_info = {
37 .physical_address = info.physical_address,
38 .virtual_address = static_cast<u32>(info.virtual_address),
39 .size = static_cast<u32>(info.size),
40 };
41 R_SUCCEED();
42}
43
44Result QueryIoMapping64From32(Core::System& system, uintptr_t* out_address, uintptr_t* out_size,
45 uint64_t physical_address, uint32_t size) {
46 R_RETURN(QueryIoMapping(system, reinterpret_cast<uintptr_t*>(out_address),
47 reinterpret_cast<uintptr_t*>(out_size), physical_address, size));
48}
49
50} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_cache.cpp b/src/core/hle/kernel/svc/svc_cache.cpp
new file mode 100644
index 000000000..b5404760e
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_cache.cpp
@@ -0,0 +1,98 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7#include "core/hle/kernel/svc_results.h"
8#include "core/hle/kernel/svc_types.h"
9
10namespace Kernel::Svc {
11
12void FlushEntireDataCache(Core::System& system) {
13 UNIMPLEMENTED();
14}
15
16Result FlushDataCache(Core::System& system, VAddr address, size_t size) {
17 UNIMPLEMENTED();
18 R_THROW(ResultNotImplemented);
19}
20
21Result InvalidateProcessDataCache(Core::System& system, Handle process_handle, uint64_t address,
22 uint64_t size) {
23 UNIMPLEMENTED();
24 R_THROW(ResultNotImplemented);
25}
26
27Result StoreProcessDataCache(Core::System& system, Handle process_handle, uint64_t address,
28 uint64_t size) {
29 UNIMPLEMENTED();
30 R_THROW(ResultNotImplemented);
31}
32
33Result FlushProcessDataCache(Core::System& system, Handle process_handle, u64 address, u64 size) {
34 // Validate address/size.
35 R_UNLESS(size > 0, ResultInvalidSize);
36 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
37 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
38
39 // Get the process from its handle.
40 KScopedAutoObject process =
41 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
42 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
43
44 // Verify the region is within range.
45 auto& page_table = process->PageTable();
46 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
47
48 // Perform the operation.
49 R_RETURN(system.Memory().FlushDataCache(*process, address, size));
50}
51
52void FlushEntireDataCache64(Core::System& system) {
53 FlushEntireDataCache(system);
54}
55
56Result FlushDataCache64(Core::System& system, VAddr address, size_t size) {
57 R_RETURN(FlushDataCache(system, address, size));
58}
59
60Result InvalidateProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address,
61 uint64_t size) {
62 R_RETURN(InvalidateProcessDataCache(system, process_handle, address, size));
63}
64
65Result StoreProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address,
66 uint64_t size) {
67 R_RETURN(StoreProcessDataCache(system, process_handle, address, size));
68}
69
70Result FlushProcessDataCache64(Core::System& system, Handle process_handle, uint64_t address,
71 uint64_t size) {
72 R_RETURN(FlushProcessDataCache(system, process_handle, address, size));
73}
74
75void FlushEntireDataCache64From32(Core::System& system) {
76 return FlushEntireDataCache(system);
77}
78
79Result FlushDataCache64From32(Core::System& system, uint32_t address, uint32_t size) {
80 R_RETURN(FlushDataCache(system, address, size));
81}
82
83Result InvalidateProcessDataCache64From32(Core::System& system, Handle process_handle,
84 uint64_t address, uint64_t size) {
85 R_RETURN(InvalidateProcessDataCache(system, process_handle, address, size));
86}
87
88Result StoreProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address,
89 uint64_t size) {
90 R_RETURN(StoreProcessDataCache(system, process_handle, address, size));
91}
92
93Result FlushProcessDataCache64From32(Core::System& system, Handle process_handle, uint64_t address,
94 uint64_t size) {
95 R_RETURN(FlushProcessDataCache(system, process_handle, address, size));
96}
97
98} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp
new file mode 100644
index 000000000..ec256b757
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_code_memory.cpp
@@ -0,0 +1,168 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_code_memory.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11namespace {
12
13constexpr bool IsValidMapCodeMemoryPermission(MemoryPermission perm) {
14 return perm == MemoryPermission::ReadWrite;
15}
16
17constexpr bool IsValidMapToOwnerCodeMemoryPermission(MemoryPermission perm) {
18 return perm == MemoryPermission::Read || perm == MemoryPermission::ReadExecute;
19}
20
21constexpr bool IsValidUnmapCodeMemoryPermission(MemoryPermission perm) {
22 return perm == MemoryPermission::None;
23}
24
25constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(MemoryPermission perm) {
26 return perm == MemoryPermission::None;
27}
28
29} // namespace
30
31Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
32 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
33
34 // Get kernel instance.
35 auto& kernel = system.Kernel();
36
37 // Validate address / size.
38 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
39 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
40 R_UNLESS(size > 0, ResultInvalidSize);
41 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
42
43 // Create the code memory.
44
45 KCodeMemory* code_mem = KCodeMemory::Create(kernel);
46 R_UNLESS(code_mem != nullptr, ResultOutOfResource);
47
48 // Verify that the region is in range.
49 R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
50 ResultInvalidCurrentMemory);
51
52 // Initialize the code memory.
53 R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
54
55 // Register the code memory.
56 KCodeMemory::Register(kernel, code_mem);
57
58 // Add the code memory to the handle table.
59 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
60
61 code_mem->Close();
62
63 return ResultSuccess;
64}
65
66Result ControlCodeMemory(Core::System& system, Handle code_memory_handle,
67 CodeMemoryOperation operation, VAddr address, size_t size,
68 MemoryPermission perm) {
69
70 LOG_TRACE(Kernel_SVC,
71 "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
72 "permission=0x{:X}",
73 code_memory_handle, operation, address, size, perm);
74
75 // Validate the address / size.
76 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
77 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
78 R_UNLESS(size > 0, ResultInvalidSize);
79 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
80
81 // Get the code memory from its handle.
82 KScopedAutoObject code_mem =
83 system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
84 R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
85
86 // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
87 // This enables homebrew usage of these SVCs for JIT.
88
89 // Perform the operation.
90 switch (operation) {
91 case CodeMemoryOperation::Map: {
92 // Check that the region is in range.
93 R_UNLESS(
94 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
95 ResultInvalidMemoryRegion);
96
97 // Check the memory permission.
98 R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
99
100 // Map the memory.
101 R_TRY(code_mem->Map(address, size));
102 } break;
103 case CodeMemoryOperation::Unmap: {
104 // Check that the region is in range.
105 R_UNLESS(
106 system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
107 ResultInvalidMemoryRegion);
108
109 // Check the memory permission.
110 R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
111
112 // Unmap the memory.
113 R_TRY(code_mem->Unmap(address, size));
114 } break;
115 case CodeMemoryOperation::MapToOwner: {
116 // Check that the region is in range.
117 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
118 KMemoryState::GeneratedCode),
119 ResultInvalidMemoryRegion);
120
121 // Check the memory permission.
122 R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
123
124 // Map the memory to its owner.
125 R_TRY(code_mem->MapToOwner(address, size, perm));
126 } break;
127 case CodeMemoryOperation::UnmapFromOwner: {
128 // Check that the region is in range.
129 R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
130 KMemoryState::GeneratedCode),
131 ResultInvalidMemoryRegion);
132
133 // Check the memory permission.
134 R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
135
136 // Unmap the memory from its owner.
137 R_TRY(code_mem->UnmapFromOwner(address, size));
138 } break;
139 default:
140 return ResultInvalidEnumValue;
141 }
142
143 return ResultSuccess;
144}
145
146Result CreateCodeMemory64(Core::System& system, Handle* out_handle, uint64_t address,
147 uint64_t size) {
148 R_RETURN(CreateCodeMemory(system, out_handle, address, size));
149}
150
151Result ControlCodeMemory64(Core::System& system, Handle code_memory_handle,
152 CodeMemoryOperation operation, uint64_t address, uint64_t size,
153 MemoryPermission perm) {
154 R_RETURN(ControlCodeMemory(system, code_memory_handle, operation, address, size, perm));
155}
156
157Result CreateCodeMemory64From32(Core::System& system, Handle* out_handle, uint32_t address,
158 uint32_t size) {
159 R_RETURN(CreateCodeMemory(system, out_handle, address, size));
160}
161
162Result ControlCodeMemory64From32(Core::System& system, Handle code_memory_handle,
163 CodeMemoryOperation operation, uint64_t address, uint64_t size,
164 MemoryPermission perm) {
165 R_RETURN(ControlCodeMemory(system, code_memory_handle, operation, address, size, perm));
166}
167
168} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_condition_variable.cpp b/src/core/hle/kernel/svc/svc_condition_variable.cpp
new file mode 100644
index 000000000..b59a33e68
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_condition_variable.cpp
@@ -0,0 +1,77 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/kernel.h"
8#include "core/hle/kernel/svc.h"
9#include "core/hle/kernel/svc_results.h"
10
11namespace Kernel::Svc {
12
13/// Wait process wide key atomic
14Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
15 s64 timeout_ns) {
16 LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
17 cv_key, tag, timeout_ns);
18
19 // Validate input.
20 if (IsKernelAddress(address)) {
21 LOG_ERROR(Kernel_SVC, "Attempted to wait on kernel address (address={:08X})", address);
22 return ResultInvalidCurrentMemory;
23 }
24 if (!Common::IsAligned(address, sizeof(s32))) {
25 LOG_ERROR(Kernel_SVC, "Address must be 4 byte aligned (address={:08X})", address);
26 return ResultInvalidAddress;
27 }
28
29 // Convert timeout from nanoseconds to ticks.
30 s64 timeout{};
31 if (timeout_ns > 0) {
32 const s64 offset_tick(timeout_ns);
33 if (offset_tick > 0) {
34 timeout = offset_tick + 2;
35 if (timeout <= 0) {
36 timeout = std::numeric_limits<s64>::max();
37 }
38 } else {
39 timeout = std::numeric_limits<s64>::max();
40 }
41 } else {
42 timeout = timeout_ns;
43 }
44
45 // Wait on the condition variable.
46 return system.Kernel().CurrentProcess()->WaitConditionVariable(
47 address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
48}
49
50/// Signal process wide key
51void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) {
52 LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count);
53
54 // Signal the condition variable.
55 return system.Kernel().CurrentProcess()->SignalConditionVariable(
56 Common::AlignDown(cv_key, sizeof(u32)), count);
57}
58
59Result WaitProcessWideKeyAtomic64(Core::System& system, uint64_t address, uint64_t cv_key,
60 uint32_t tag, int64_t timeout_ns) {
61 R_RETURN(WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns));
62}
63
64void SignalProcessWideKey64(Core::System& system, uint64_t cv_key, int32_t count) {
65 SignalProcessWideKey(system, cv_key, count);
66}
67
68Result WaitProcessWideKeyAtomic64From32(Core::System& system, uint32_t address, uint32_t cv_key,
69 uint32_t tag, int64_t timeout_ns) {
70 R_RETURN(WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns));
71}
72
73void SignalProcessWideKey64From32(Core::System& system, uint32_t cv_key, int32_t count) {
74 SignalProcessWideKey(system, cv_key, count);
75}
76
77} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_debug.cpp b/src/core/hle/kernel/svc/svc_debug.cpp
new file mode 100644
index 000000000..a14050fa7
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_debug.cpp
@@ -0,0 +1,194 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5#include "core/hle/kernel/svc_results.h"
6
7namespace Kernel::Svc {
8
9Result DebugActiveProcess(Core::System& system, Handle* out_handle, uint64_t process_id) {
10 UNIMPLEMENTED();
11 R_THROW(ResultNotImplemented);
12}
13
14Result BreakDebugProcess(Core::System& system, Handle debug_handle) {
15 UNIMPLEMENTED();
16 R_THROW(ResultNotImplemented);
17}
18
19Result TerminateDebugProcess(Core::System& system, Handle debug_handle) {
20 UNIMPLEMENTED();
21 R_THROW(ResultNotImplemented);
22}
23
24Result GetDebugEvent(Core::System& system, uint64_t out_info, Handle debug_handle) {
25 UNIMPLEMENTED();
26 R_THROW(ResultNotImplemented);
27}
28
29Result ContinueDebugEvent(Core::System& system, Handle debug_handle, uint32_t flags,
30 uint64_t user_thread_ids, int32_t num_thread_ids) {
31 UNIMPLEMENTED();
32 R_THROW(ResultNotImplemented);
33}
34
35Result GetDebugThreadContext(Core::System& system, uint64_t out_context, Handle debug_handle,
36 uint64_t thread_id, uint32_t context_flags) {
37 UNIMPLEMENTED();
38 R_THROW(ResultNotImplemented);
39}
40
41Result SetDebugThreadContext(Core::System& system, Handle debug_handle, uint64_t thread_id,
42 uint64_t user_context, uint32_t context_flags) {
43 UNIMPLEMENTED();
44 R_THROW(ResultNotImplemented);
45}
46
47Result QueryDebugProcessMemory(Core::System& system, uint64_t out_memory_info,
48 PageInfo* out_page_info, Handle debug_handle, uintptr_t address) {
49 UNIMPLEMENTED();
50 R_THROW(ResultNotImplemented);
51}
52
53Result ReadDebugProcessMemory(Core::System& system, uintptr_t buffer, Handle debug_handle,
54 uintptr_t address, size_t size) {
55 UNIMPLEMENTED();
56 R_THROW(ResultNotImplemented);
57}
58
59Result WriteDebugProcessMemory(Core::System& system, Handle debug_handle, uintptr_t buffer,
60 uintptr_t address, size_t size) {
61 UNIMPLEMENTED();
62 R_THROW(ResultNotImplemented);
63}
64
65Result SetHardwareBreakPoint(Core::System& system, HardwareBreakPointRegisterName name,
66 uint64_t flags, uint64_t value) {
67 UNIMPLEMENTED();
68 R_THROW(ResultNotImplemented);
69}
70
71Result GetDebugThreadParam(Core::System& system, uint64_t* out_64, uint32_t* out_32,
72 Handle debug_handle, uint64_t thread_id, DebugThreadParam param) {
73 UNIMPLEMENTED();
74 R_THROW(ResultNotImplemented);
75}
76
77Result DebugActiveProcess64(Core::System& system, Handle* out_handle, uint64_t process_id) {
78 R_RETURN(DebugActiveProcess(system, out_handle, process_id));
79}
80
81Result BreakDebugProcess64(Core::System& system, Handle debug_handle) {
82 R_RETURN(BreakDebugProcess(system, debug_handle));
83}
84
85Result TerminateDebugProcess64(Core::System& system, Handle debug_handle) {
86 R_RETURN(TerminateDebugProcess(system, debug_handle));
87}
88
89Result GetDebugEvent64(Core::System& system, uint64_t out_info, Handle debug_handle) {
90 R_RETURN(GetDebugEvent(system, out_info, debug_handle));
91}
92
93Result ContinueDebugEvent64(Core::System& system, Handle debug_handle, uint32_t flags,
94 uint64_t thread_ids, int32_t num_thread_ids) {
95 R_RETURN(ContinueDebugEvent(system, debug_handle, flags, thread_ids, num_thread_ids));
96}
97
98Result GetDebugThreadContext64(Core::System& system, uint64_t out_context, Handle debug_handle,
99 uint64_t thread_id, uint32_t context_flags) {
100 R_RETURN(GetDebugThreadContext(system, out_context, debug_handle, thread_id, context_flags));
101}
102
103Result SetDebugThreadContext64(Core::System& system, Handle debug_handle, uint64_t thread_id,
104 uint64_t context, uint32_t context_flags) {
105 R_RETURN(SetDebugThreadContext(system, debug_handle, thread_id, context, context_flags));
106}
107
108Result QueryDebugProcessMemory64(Core::System& system, uint64_t out_memory_info,
109 PageInfo* out_page_info, Handle debug_handle, uint64_t address) {
110 R_RETURN(
111 QueryDebugProcessMemory(system, out_memory_info, out_page_info, debug_handle, address));
112}
113
114Result ReadDebugProcessMemory64(Core::System& system, uint64_t buffer, Handle debug_handle,
115 uint64_t address, uint64_t size) {
116 R_RETURN(ReadDebugProcessMemory(system, buffer, debug_handle, address, size));
117}
118
119Result WriteDebugProcessMemory64(Core::System& system, Handle debug_handle, uint64_t buffer,
120 uint64_t address, uint64_t size) {
121 R_RETURN(WriteDebugProcessMemory(system, debug_handle, buffer, address, size));
122}
123
124Result SetHardwareBreakPoint64(Core::System& system, HardwareBreakPointRegisterName name,
125 uint64_t flags, uint64_t value) {
126 R_RETURN(SetHardwareBreakPoint(system, name, flags, value));
127}
128
129Result GetDebugThreadParam64(Core::System& system, uint64_t* out_64, uint32_t* out_32,
130 Handle debug_handle, uint64_t thread_id, DebugThreadParam param) {
131 R_RETURN(GetDebugThreadParam(system, out_64, out_32, debug_handle, thread_id, param));
132}
133
134Result DebugActiveProcess64From32(Core::System& system, Handle* out_handle, uint64_t process_id) {
135 R_RETURN(DebugActiveProcess(system, out_handle, process_id));
136}
137
138Result BreakDebugProcess64From32(Core::System& system, Handle debug_handle) {
139 R_RETURN(BreakDebugProcess(system, debug_handle));
140}
141
142Result TerminateDebugProcess64From32(Core::System& system, Handle debug_handle) {
143 R_RETURN(TerminateDebugProcess(system, debug_handle));
144}
145
146Result GetDebugEvent64From32(Core::System& system, uint32_t out_info, Handle debug_handle) {
147 R_RETURN(GetDebugEvent(system, out_info, debug_handle));
148}
149
150Result ContinueDebugEvent64From32(Core::System& system, Handle debug_handle, uint32_t flags,
151 uint32_t thread_ids, int32_t num_thread_ids) {
152 R_RETURN(ContinueDebugEvent(system, debug_handle, flags, thread_ids, num_thread_ids));
153}
154
155Result GetDebugThreadContext64From32(Core::System& system, uint32_t out_context,
156 Handle debug_handle, uint64_t thread_id,
157 uint32_t context_flags) {
158 R_RETURN(GetDebugThreadContext(system, out_context, debug_handle, thread_id, context_flags));
159}
160
161Result SetDebugThreadContext64From32(Core::System& system, Handle debug_handle, uint64_t thread_id,
162 uint32_t context, uint32_t context_flags) {
163 R_RETURN(SetDebugThreadContext(system, debug_handle, thread_id, context, context_flags));
164}
165
166Result QueryDebugProcessMemory64From32(Core::System& system, uint32_t out_memory_info,
167 PageInfo* out_page_info, Handle debug_handle,
168 uint32_t address) {
169 R_RETURN(
170 QueryDebugProcessMemory(system, out_memory_info, out_page_info, debug_handle, address));
171}
172
173Result ReadDebugProcessMemory64From32(Core::System& system, uint32_t buffer, Handle debug_handle,
174 uint32_t address, uint32_t size) {
175 R_RETURN(ReadDebugProcessMemory(system, buffer, debug_handle, address, size));
176}
177
178Result WriteDebugProcessMemory64From32(Core::System& system, Handle debug_handle, uint32_t buffer,
179 uint32_t address, uint32_t size) {
180 R_RETURN(WriteDebugProcessMemory(system, debug_handle, buffer, address, size));
181}
182
183Result SetHardwareBreakPoint64From32(Core::System& system, HardwareBreakPointRegisterName name,
184 uint64_t flags, uint64_t value) {
185 R_RETURN(SetHardwareBreakPoint(system, name, flags, value));
186}
187
188Result GetDebugThreadParam64From32(Core::System& system, uint64_t* out_64, uint32_t* out_32,
189 Handle debug_handle, uint64_t thread_id,
190 DebugThreadParam param) {
191 R_RETURN(GetDebugThreadParam(system, out_64, out_32, debug_handle, thread_id, param));
192}
193
194} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_debug_string.cpp b/src/core/hle/kernel/svc/svc_debug_string.cpp
new file mode 100644
index 000000000..d4bf062d1
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_debug_string.cpp
@@ -0,0 +1,29 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/svc.h"
6#include "core/memory.h"
7
8namespace Kernel::Svc {
9
10/// Used to output a message on a debug hardware unit - does nothing on a retail unit
11Result OutputDebugString(Core::System& system, VAddr address, u64 len) {
12 R_SUCCEED_IF(len == 0);
13
14 std::string str(len, '\0');
15 system.Memory().ReadBlock(address, str.data(), str.size());
16 LOG_DEBUG(Debug_Emulated, "{}", str);
17
18 R_SUCCEED();
19}
20
21Result OutputDebugString64(Core::System& system, uint64_t debug_str, uint64_t len) {
22 R_RETURN(OutputDebugString(system, debug_str, len));
23}
24
25Result OutputDebugString64From32(Core::System& system, uint32_t debug_str, uint32_t len) {
26 R_RETURN(OutputDebugString(system, debug_str, len));
27}
28
29} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp
new file mode 100644
index 000000000..cdc453c35
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp
@@ -0,0 +1,253 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/alignment.h"
5#include "common/scope_exit.h"
6#include "core/core.h"
7#include "core/hle/kernel/k_device_address_space.h"
8#include "core/hle/kernel/k_process.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12
13constexpr inline u64 DeviceAddressSpaceAlignMask = (1ULL << 22) - 1;
14
15constexpr bool IsProcessAndDeviceAligned(uint64_t process_address, uint64_t device_address) {
16 return (process_address & DeviceAddressSpaceAlignMask) ==
17 (device_address & DeviceAddressSpaceAlignMask);
18}
19
20Result CreateDeviceAddressSpace(Core::System& system, Handle* out, uint64_t das_address,
21 uint64_t das_size) {
22 // Validate input.
23 R_UNLESS(Common::IsAligned(das_address, PageSize), ResultInvalidMemoryRegion);
24 R_UNLESS(Common::IsAligned(das_size, PageSize), ResultInvalidMemoryRegion);
25 R_UNLESS(das_size > 0, ResultInvalidMemoryRegion);
26 R_UNLESS((das_address < das_address + das_size), ResultInvalidMemoryRegion);
27
28 // Create the device address space.
29 KDeviceAddressSpace* das = KDeviceAddressSpace::Create(system.Kernel());
30 R_UNLESS(das != nullptr, ResultOutOfResource);
31 SCOPE_EXIT({ das->Close(); });
32
33 // Initialize the device address space.
34 R_TRY(das->Initialize(das_address, das_size));
35
36 // Register the device address space.
37 KDeviceAddressSpace::Register(system.Kernel(), das);
38
39 // Add to the handle table.
40 R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, das));
41
42 R_SUCCEED();
43}
44
45Result AttachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle) {
46 // Get the device address space.
47 KScopedAutoObject das =
48 system.CurrentProcess()->GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
49 R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
50
51 // Attach.
52 R_RETURN(das->Attach(device_name));
53}
54
55Result DetachDeviceAddressSpace(Core::System& system, DeviceName device_name, Handle das_handle) {
56 // Get the device address space.
57 KScopedAutoObject das =
58 system.CurrentProcess()->GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
59 R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
60
61 // Detach.
62 R_RETURN(das->Detach(device_name));
63}
64
65constexpr bool IsValidDeviceMemoryPermission(MemoryPermission device_perm) {
66 switch (device_perm) {
67 case MemoryPermission::Read:
68 case MemoryPermission::Write:
69 case MemoryPermission::ReadWrite:
70 return true;
71 default:
72 return false;
73 }
74}
75
76Result MapDeviceAddressSpaceByForce(Core::System& system, Handle das_handle, Handle process_handle,
77 uint64_t process_address, size_t size, uint64_t device_address,
78 u32 option) {
79 // Decode the option.
80 const MapDeviceAddressSpaceOption option_pack{option};
81 const auto device_perm = option_pack.permission;
82 const auto reserved = option_pack.reserved;
83
84 // Validate input.
85 R_UNLESS(Common::IsAligned(process_address, PageSize), ResultInvalidAddress);
86 R_UNLESS(Common::IsAligned(device_address, PageSize), ResultInvalidAddress);
87 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
88 R_UNLESS(size > 0, ResultInvalidSize);
89 R_UNLESS((process_address < process_address + size), ResultInvalidCurrentMemory);
90 R_UNLESS((device_address < device_address + size), ResultInvalidMemoryRegion);
91 R_UNLESS((process_address == static_cast<uintptr_t>(process_address)),
92 ResultInvalidCurrentMemory);
93 R_UNLESS(IsValidDeviceMemoryPermission(device_perm), ResultInvalidNewMemoryPermission);
94 R_UNLESS(reserved == 0, ResultInvalidEnumValue);
95
96 // Get the device address space.
97 KScopedAutoObject das =
98 system.CurrentProcess()->GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
99 R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
100
101 // Get the process.
102 KScopedAutoObject process =
103 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
104 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
105
106 // Validate that the process address is within range.
107 auto& page_table = process->PageTable();
108 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
109
110 // Map.
111 R_RETURN(
112 das->MapByForce(std::addressof(page_table), process_address, size, device_address, option));
113}
114
115Result MapDeviceAddressSpaceAligned(Core::System& system, Handle das_handle, Handle process_handle,
116 uint64_t process_address, size_t size, uint64_t device_address,
117 u32 option) {
118 // Decode the option.
119 const MapDeviceAddressSpaceOption option_pack{option};
120 const auto device_perm = option_pack.permission;
121 const auto reserved = option_pack.reserved;
122
123 // Validate input.
124 R_UNLESS(Common::IsAligned(process_address, PageSize), ResultInvalidAddress);
125 R_UNLESS(Common::IsAligned(device_address, PageSize), ResultInvalidAddress);
126 R_UNLESS(IsProcessAndDeviceAligned(process_address, device_address), ResultInvalidAddress);
127 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
128 R_UNLESS(size > 0, ResultInvalidSize);
129 R_UNLESS((process_address < process_address + size), ResultInvalidCurrentMemory);
130 R_UNLESS((device_address < device_address + size), ResultInvalidMemoryRegion);
131 R_UNLESS((process_address == static_cast<uintptr_t>(process_address)),
132 ResultInvalidCurrentMemory);
133 R_UNLESS(IsValidDeviceMemoryPermission(device_perm), ResultInvalidNewMemoryPermission);
134 R_UNLESS(reserved == 0, ResultInvalidEnumValue);
135
136 // Get the device address space.
137 KScopedAutoObject das =
138 system.CurrentProcess()->GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
139 R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
140
141 // Get the process.
142 KScopedAutoObject process =
143 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
144 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
145
146 // Validate that the process address is within range.
147 auto& page_table = process->PageTable();
148 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
149
150 // Map.
151 R_RETURN(
152 das->MapAligned(std::addressof(page_table), process_address, size, device_address, option));
153}
154
155Result UnmapDeviceAddressSpace(Core::System& system, Handle das_handle, Handle process_handle,
156 uint64_t process_address, size_t size, uint64_t device_address) {
157 // Validate input.
158 R_UNLESS(Common::IsAligned(process_address, PageSize), ResultInvalidAddress);
159 R_UNLESS(Common::IsAligned(device_address, PageSize), ResultInvalidAddress);
160 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
161 R_UNLESS(size > 0, ResultInvalidSize);
162 R_UNLESS((process_address < process_address + size), ResultInvalidCurrentMemory);
163 R_UNLESS((device_address < device_address + size), ResultInvalidMemoryRegion);
164 R_UNLESS((process_address == static_cast<uintptr_t>(process_address)),
165 ResultInvalidCurrentMemory);
166
167 // Get the device address space.
168 KScopedAutoObject das =
169 system.CurrentProcess()->GetHandleTable().GetObject<KDeviceAddressSpace>(das_handle);
170 R_UNLESS(das.IsNotNull(), ResultInvalidHandle);
171
172 // Get the process.
173 KScopedAutoObject process =
174 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
175 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
176
177 // Validate that the process address is within range.
178 auto& page_table = process->PageTable();
179 R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory);
180
181 R_RETURN(das->Unmap(std::addressof(page_table), process_address, size, device_address));
182}
183
184Result CreateDeviceAddressSpace64(Core::System& system, Handle* out_handle, uint64_t das_address,
185 uint64_t das_size) {
186 R_RETURN(CreateDeviceAddressSpace(system, out_handle, das_address, das_size));
187}
188
189Result AttachDeviceAddressSpace64(Core::System& system, DeviceName device_name, Handle das_handle) {
190 R_RETURN(AttachDeviceAddressSpace(system, device_name, das_handle));
191}
192
193Result DetachDeviceAddressSpace64(Core::System& system, DeviceName device_name, Handle das_handle) {
194 R_RETURN(DetachDeviceAddressSpace(system, device_name, das_handle));
195}
196
197Result MapDeviceAddressSpaceByForce64(Core::System& system, Handle das_handle,
198 Handle process_handle, uint64_t process_address,
199 uint64_t size, uint64_t device_address, u32 option) {
200 R_RETURN(MapDeviceAddressSpaceByForce(system, das_handle, process_handle, process_address, size,
201 device_address, option));
202}
203
204Result MapDeviceAddressSpaceAligned64(Core::System& system, Handle das_handle,
205 Handle process_handle, uint64_t process_address,
206 uint64_t size, uint64_t device_address, u32 option) {
207 R_RETURN(MapDeviceAddressSpaceAligned(system, das_handle, process_handle, process_address, size,
208 device_address, option));
209}
210
211Result UnmapDeviceAddressSpace64(Core::System& system, Handle das_handle, Handle process_handle,
212 uint64_t process_address, uint64_t size, uint64_t device_address) {
213 R_RETURN(UnmapDeviceAddressSpace(system, das_handle, process_handle, process_address, size,
214 device_address));
215}
216
217Result CreateDeviceAddressSpace64From32(Core::System& system, Handle* out_handle,
218 uint64_t das_address, uint64_t das_size) {
219 R_RETURN(CreateDeviceAddressSpace(system, out_handle, das_address, das_size));
220}
221
222Result AttachDeviceAddressSpace64From32(Core::System& system, DeviceName device_name,
223 Handle das_handle) {
224 R_RETURN(AttachDeviceAddressSpace(system, device_name, das_handle));
225}
226
227Result DetachDeviceAddressSpace64From32(Core::System& system, DeviceName device_name,
228 Handle das_handle) {
229 R_RETURN(DetachDeviceAddressSpace(system, device_name, das_handle));
230}
231
232Result MapDeviceAddressSpaceByForce64From32(Core::System& system, Handle das_handle,
233 Handle process_handle, uint64_t process_address,
234 uint32_t size, uint64_t device_address, u32 option) {
235 R_RETURN(MapDeviceAddressSpaceByForce(system, das_handle, process_handle, process_address, size,
236 device_address, option));
237}
238
239Result MapDeviceAddressSpaceAligned64From32(Core::System& system, Handle das_handle,
240 Handle process_handle, uint64_t process_address,
241 uint32_t size, uint64_t device_address, u32 option) {
242 R_RETURN(MapDeviceAddressSpaceAligned(system, das_handle, process_handle, process_address, size,
243 device_address, option));
244}
245
246Result UnmapDeviceAddressSpace64From32(Core::System& system, Handle das_handle,
247 Handle process_handle, uint64_t process_address,
248 uint32_t size, uint64_t device_address) {
249 R_RETURN(UnmapDeviceAddressSpace(system, das_handle, process_handle, process_address, size,
250 device_address));
251}
252
253} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_event.cpp b/src/core/hle/kernel/svc/svc_event.cpp
new file mode 100644
index 000000000..e8fb9efbc
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_event.cpp
@@ -0,0 +1,124 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_scoped_resource_reservation.h"
9#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13
14Result SignalEvent(Core::System& system, Handle event_handle) {
15 LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
16
17 // Get the current handle table.
18 const KHandleTable& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
19
20 // Get the event.
21 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
22 R_UNLESS(event.IsNotNull(), ResultInvalidHandle);
23
24 return event->Signal();
25}
26
27Result ClearEvent(Core::System& system, Handle event_handle) {
28 LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
29
30 // Get the current handle table.
31 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
32
33 // Try to clear the writable event.
34 {
35 KScopedAutoObject event = handle_table.GetObject<KEvent>(event_handle);
36 if (event.IsNotNull()) {
37 return event->Clear();
38 }
39 }
40
41 // Try to clear the readable event.
42 {
43 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(event_handle);
44 if (readable_event.IsNotNull()) {
45 return readable_event->Clear();
46 }
47 }
48
49 LOG_ERROR(Kernel_SVC, "Event handle does not exist, event_handle=0x{:08X}", event_handle);
50
51 return ResultInvalidHandle;
52}
53
54Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
55 LOG_DEBUG(Kernel_SVC, "called");
56
57 // Get the kernel reference and handle table.
58 auto& kernel = system.Kernel();
59 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
60
61 // Reserve a new event from the process resource limit
62 KScopedResourceReservation event_reservation(kernel.CurrentProcess(),
63 LimitableResource::EventCountMax);
64 R_UNLESS(event_reservation.Succeeded(), ResultLimitReached);
65
66 // Create a new event.
67 KEvent* event = KEvent::Create(kernel);
68 R_UNLESS(event != nullptr, ResultOutOfResource);
69
70 // Initialize the event.
71 event->Initialize(kernel.CurrentProcess());
72
73 // Commit the thread reservation.
74 event_reservation.Commit();
75
76 // Ensure that we clean up the event (and its only references are handle table) on function end.
77 SCOPE_EXIT({
78 event->GetReadableEvent().Close();
79 event->Close();
80 });
81
82 // Register the event.
83 KEvent::Register(kernel, event);
84
85 // Add the event to the handle table.
86 R_TRY(handle_table.Add(out_write, event));
87
88 // Ensure that we maintaing a clean handle state on exit.
89 auto handle_guard = SCOPE_GUARD({ handle_table.Remove(*out_write); });
90
91 // Add the readable event to the handle table.
92 R_TRY(handle_table.Add(out_read, std::addressof(event->GetReadableEvent())));
93
94 // We succeeded.
95 handle_guard.Cancel();
96 return ResultSuccess;
97}
98
99Result SignalEvent64(Core::System& system, Handle event_handle) {
100 R_RETURN(SignalEvent(system, event_handle));
101}
102
103Result ClearEvent64(Core::System& system, Handle event_handle) {
104 R_RETURN(ClearEvent(system, event_handle));
105}
106
107Result CreateEvent64(Core::System& system, Handle* out_write_handle, Handle* out_read_handle) {
108 R_RETURN(CreateEvent(system, out_write_handle, out_read_handle));
109}
110
111Result SignalEvent64From32(Core::System& system, Handle event_handle) {
112 R_RETURN(SignalEvent(system, event_handle));
113}
114
115Result ClearEvent64From32(Core::System& system, Handle event_handle) {
116 R_RETURN(ClearEvent(system, event_handle));
117}
118
119Result CreateEvent64From32(Core::System& system, Handle* out_write_handle,
120 Handle* out_read_handle) {
121 R_RETURN(CreateEvent(system, out_write_handle, out_read_handle));
122}
123
124} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_exception.cpp b/src/core/hle/kernel/svc/svc_exception.cpp
new file mode 100644
index 000000000..c2782908d
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_exception.cpp
@@ -0,0 +1,137 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/debugger/debugger.h"
6#include "core/hle/kernel/k_thread.h"
7#include "core/hle/kernel/svc.h"
8#include "core/hle/kernel/svc_types.h"
9#include "core/memory.h"
10#include "core/reporter.h"
11
12namespace Kernel::Svc {
13
14/// Break program execution
15void Break(Core::System& system, BreakReason reason, u64 info1, u64 info2) {
16 BreakReason break_reason =
17 reason & static_cast<BreakReason>(~BreakReason::NotificationOnlyFlag);
18 bool notification_only = True(reason & BreakReason::NotificationOnlyFlag);
19
20 bool has_dumped_buffer{};
21 std::vector<u8> debug_buffer;
22
23 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
24 if (sz == 0 || addr == 0 || has_dumped_buffer) {
25 return;
26 }
27
28 auto& memory = system.Memory();
29
30 // This typically is an error code so we're going to assume this is the case
31 if (sz == sizeof(u32)) {
32 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", memory.Read32(addr));
33 } else {
34 // We don't know what's in here so we'll hexdump it
35 debug_buffer.resize(sz);
36 memory.ReadBlock(addr, debug_buffer.data(), sz);
37 std::string hexdump;
38 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
39 hexdump += fmt::format("{:02X} ", debug_buffer[i]);
40 if (i != 0 && i % 16 == 0) {
41 hexdump += '\n';
42 }
43 }
44 LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
45 }
46 has_dumped_buffer = true;
47 };
48 switch (break_reason) {
49 case BreakReason::Panic:
50 LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1,
51 info2);
52 handle_debug_buffer(info1, info2);
53 break;
54 case BreakReason::Assert:
55 LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
56 info1, info2);
57 handle_debug_buffer(info1, info2);
58 break;
59 case BreakReason::User:
60 LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2);
61 handle_debug_buffer(info1, info2);
62 break;
63 case BreakReason::PreLoadDll:
64 LOG_INFO(Debug_Emulated,
65 "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1,
66 info2);
67 break;
68 case BreakReason::PostLoadDll:
69 LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
70 info2);
71 break;
72 case BreakReason::PreUnloadDll:
73 LOG_INFO(Debug_Emulated,
74 "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1,
75 info2);
76 break;
77 case BreakReason::PostUnloadDll:
78 LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}",
79 info1, info2);
80 break;
81 case BreakReason::CppException:
82 LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
83 break;
84 default:
85 LOG_WARNING(
86 Debug_Emulated,
87 "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}",
88 reason, info1, info2);
89 handle_debug_buffer(info1, info2);
90 break;
91 }
92
93 system.GetReporter().SaveSvcBreakReport(
94 static_cast<u32>(reason), notification_only, info1, info2,
95 has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
96
97 if (!notification_only) {
98 LOG_CRITICAL(
99 Debug_Emulated,
100 "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
101 reason, info1, info2);
102
103 handle_debug_buffer(info1, info2);
104
105 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
106 const auto thread_processor_id = current_thread->GetActiveCore();
107 system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
108 }
109
110 if (system.DebuggerEnabled()) {
111 auto* thread = system.Kernel().GetCurrentEmuThread();
112 system.GetDebugger().NotifyThreadStopped(thread);
113 thread->RequestSuspend(Kernel::SuspendType::Debug);
114 }
115}
116
117void ReturnFromException(Core::System& system, Result result) {
118 UNIMPLEMENTED();
119}
120
121void Break64(Core::System& system, BreakReason break_reason, uint64_t arg, uint64_t size) {
122 Break(system, break_reason, arg, size);
123}
124
125void Break64From32(Core::System& system, BreakReason break_reason, uint32_t arg, uint32_t size) {
126 Break(system, break_reason, arg, size);
127}
128
129void ReturnFromException64(Core::System& system, Result result) {
130 ReturnFromException(system, result);
131}
132
133void ReturnFromException64From32(Core::System& system, Result result) {
134 ReturnFromException(system, result);
135}
136
137} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp
new file mode 100644
index 000000000..ad56e2fe6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_info.cpp
@@ -0,0 +1,297 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_resource_limit.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12/// Gets system/memory information for the current process
13Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle handle,
14 u64 info_sub_id) {
15 LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}",
16 info_id_type, info_sub_id, handle);
17
18 u32 info_id = static_cast<u32>(info_id_type);
19
20 switch (info_id_type) {
21 case InfoType::CoreMask:
22 case InfoType::PriorityMask:
23 case InfoType::AliasRegionAddress:
24 case InfoType::AliasRegionSize:
25 case InfoType::HeapRegionAddress:
26 case InfoType::HeapRegionSize:
27 case InfoType::AslrRegionAddress:
28 case InfoType::AslrRegionSize:
29 case InfoType::StackRegionAddress:
30 case InfoType::StackRegionSize:
31 case InfoType::TotalMemorySize:
32 case InfoType::UsedMemorySize:
33 case InfoType::SystemResourceSizeTotal:
34 case InfoType::SystemResourceSizeUsed:
35 case InfoType::ProgramId:
36 case InfoType::UserExceptionContextAddress:
37 case InfoType::TotalNonSystemMemorySize:
38 case InfoType::UsedNonSystemMemorySize:
39 case InfoType::IsApplication:
40 case InfoType::FreeThreadCount: {
41 if (info_sub_id != 0) {
42 LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
43 info_sub_id);
44 return ResultInvalidEnumValue;
45 }
46
47 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
48 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
49 if (process.IsNull()) {
50 LOG_ERROR(Kernel_SVC, "Process is not valid! info_id={}, info_sub_id={}, handle={:08X}",
51 info_id, info_sub_id, handle);
52 return ResultInvalidHandle;
53 }
54
55 switch (info_id_type) {
56 case InfoType::CoreMask:
57 *result = process->GetCoreMask();
58 return ResultSuccess;
59
60 case InfoType::PriorityMask:
61 *result = process->GetPriorityMask();
62 return ResultSuccess;
63
64 case InfoType::AliasRegionAddress:
65 *result = process->PageTable().GetAliasRegionStart();
66 return ResultSuccess;
67
68 case InfoType::AliasRegionSize:
69 *result = process->PageTable().GetAliasRegionSize();
70 return ResultSuccess;
71
72 case InfoType::HeapRegionAddress:
73 *result = process->PageTable().GetHeapRegionStart();
74 return ResultSuccess;
75
76 case InfoType::HeapRegionSize:
77 *result = process->PageTable().GetHeapRegionSize();
78 return ResultSuccess;
79
80 case InfoType::AslrRegionAddress:
81 *result = process->PageTable().GetAliasCodeRegionStart();
82 return ResultSuccess;
83
84 case InfoType::AslrRegionSize:
85 *result = process->PageTable().GetAliasCodeRegionSize();
86 return ResultSuccess;
87
88 case InfoType::StackRegionAddress:
89 *result = process->PageTable().GetStackRegionStart();
90 return ResultSuccess;
91
92 case InfoType::StackRegionSize:
93 *result = process->PageTable().GetStackRegionSize();
94 return ResultSuccess;
95
96 case InfoType::TotalMemorySize:
97 *result = process->GetTotalPhysicalMemoryAvailable();
98 return ResultSuccess;
99
100 case InfoType::UsedMemorySize:
101 *result = process->GetTotalPhysicalMemoryUsed();
102 return ResultSuccess;
103
104 case InfoType::SystemResourceSizeTotal:
105 *result = process->GetSystemResourceSize();
106 return ResultSuccess;
107
108 case InfoType::SystemResourceSizeUsed:
109 LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
110 *result = process->GetSystemResourceUsage();
111 return ResultSuccess;
112
113 case InfoType::ProgramId:
114 *result = process->GetProgramID();
115 return ResultSuccess;
116
117 case InfoType::UserExceptionContextAddress:
118 *result = process->GetProcessLocalRegionAddress();
119 return ResultSuccess;
120
121 case InfoType::TotalNonSystemMemorySize:
122 *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
123 return ResultSuccess;
124
125 case InfoType::UsedNonSystemMemorySize:
126 *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
127 return ResultSuccess;
128
129 case InfoType::FreeThreadCount:
130 *result = process->GetFreeThreadCount();
131 return ResultSuccess;
132
133 default:
134 break;
135 }
136
137 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
138 return ResultInvalidEnumValue;
139 }
140
141 case InfoType::DebuggerAttached:
142 *result = 0;
143 return ResultSuccess;
144
145 case InfoType::ResourceLimit: {
146 if (handle != 0) {
147 LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle);
148 return ResultInvalidHandle;
149 }
150
151 if (info_sub_id != 0) {
152 LOG_ERROR(Kernel, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id,
153 info_sub_id);
154 return ResultInvalidCombination;
155 }
156
157 KProcess* const current_process = system.Kernel().CurrentProcess();
158 KHandleTable& handle_table = current_process->GetHandleTable();
159 const auto resource_limit = current_process->GetResourceLimit();
160 if (!resource_limit) {
161 *result = Svc::InvalidHandle;
162 // Yes, the kernel considers this a successful operation.
163 return ResultSuccess;
164 }
165
166 Handle resource_handle{};
167 R_TRY(handle_table.Add(&resource_handle, resource_limit));
168
169 *result = resource_handle;
170 return ResultSuccess;
171 }
172
173 case InfoType::RandomEntropy:
174 if (handle != 0) {
175 LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
176 handle);
177 return ResultInvalidHandle;
178 }
179
180 if (info_sub_id >= KProcess::RANDOM_ENTROPY_SIZE) {
181 LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
182 KProcess::RANDOM_ENTROPY_SIZE, info_sub_id);
183 return ResultInvalidCombination;
184 }
185
186 *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id);
187 return ResultSuccess;
188
189 case InfoType::InitialProcessIdRange:
190 LOG_WARNING(Kernel_SVC,
191 "(STUBBED) Attempted to query privileged process id bounds, returned 0");
192 *result = 0;
193 return ResultSuccess;
194
195 case InfoType::ThreadTickCount: {
196 constexpr u64 num_cpus = 4;
197 if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
198 LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
199 info_sub_id);
200 return ResultInvalidCombination;
201 }
202
203 KScopedAutoObject thread =
204 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(
205 static_cast<Handle>(handle));
206 if (thread.IsNull()) {
207 LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
208 static_cast<Handle>(handle));
209 return ResultInvalidHandle;
210 }
211
212 const auto& core_timing = system.CoreTiming();
213 const auto& scheduler = *system.Kernel().CurrentScheduler();
214 const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
215 const bool same_thread = current_thread == thread.GetPointerUnsafe();
216
217 const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
218 u64 out_ticks = 0;
219 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
220 const u64 thread_ticks = current_thread->GetCpuTime();
221
222 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
223 } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
224 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
225 }
226
227 *result = out_ticks;
228 return ResultSuccess;
229 }
230 case InfoType::IdleTickCount: {
231 // Verify the input handle is invalid.
232 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
233
234 // Verify the requested core is valid.
235 const bool core_valid =
236 (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
237 (info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
238 R_UNLESS(core_valid, ResultInvalidCombination);
239
240 // Get the idle tick count.
241 *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
242 return ResultSuccess;
243 }
244 case InfoType::MesosphereCurrentProcess: {
245 // Verify the input handle is invalid.
246 R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
247
248 // Verify the sub-type is valid.
249 R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
250
251 // Get the handle table.
252 KProcess* current_process = system.Kernel().CurrentProcess();
253 KHandleTable& handle_table = current_process->GetHandleTable();
254
255 // Get a new handle for the current process.
256 Handle tmp;
257 R_TRY(handle_table.Add(&tmp, current_process));
258
259 // Set the output.
260 *result = tmp;
261
262 // We succeeded.
263 return ResultSuccess;
264 }
265 default:
266 LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
267 return ResultInvalidEnumValue;
268 }
269}
270
271Result GetSystemInfo(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle,
272 uint64_t info_subtype) {
273 UNIMPLEMENTED();
274 R_THROW(ResultNotImplemented);
275}
276
277Result GetInfo64(Core::System& system, uint64_t* out, InfoType info_type, Handle handle,
278 uint64_t info_subtype) {
279 R_RETURN(GetInfo(system, out, info_type, handle, info_subtype));
280}
281
282Result GetSystemInfo64(Core::System& system, uint64_t* out, SystemInfoType info_type, Handle handle,
283 uint64_t info_subtype) {
284 R_RETURN(GetSystemInfo(system, out, info_type, handle, info_subtype));
285}
286
287Result GetInfo64From32(Core::System& system, uint64_t* out, InfoType info_type, Handle handle,
288 uint64_t info_subtype) {
289 R_RETURN(GetInfo(system, out, info_type, handle, info_subtype));
290}
291
292Result GetSystemInfo64From32(Core::System& system, uint64_t* out, SystemInfoType info_type,
293 Handle handle, uint64_t info_subtype) {
294 R_RETURN(GetSystemInfo(system, out, info_type, handle, info_subtype));
295}
296
297} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_insecure_memory.cpp b/src/core/hle/kernel/svc/svc_insecure_memory.cpp
new file mode 100644
index 000000000..79882685d
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_insecure_memory.cpp
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5#include "core/hle/kernel/svc_results.h"
6
7namespace Kernel::Svc {
8
9Result MapInsecureMemory(Core::System& system, uintptr_t address, size_t size) {
10 UNIMPLEMENTED();
11 R_THROW(ResultNotImplemented);
12}
13
14Result UnmapInsecureMemory(Core::System& system, uintptr_t address, size_t size) {
15 UNIMPLEMENTED();
16 R_THROW(ResultNotImplemented);
17}
18
19Result MapInsecureMemory64(Core::System& system, uint64_t address, uint64_t size) {
20 R_RETURN(MapInsecureMemory(system, address, size));
21}
22
23Result UnmapInsecureMemory64(Core::System& system, uint64_t address, uint64_t size) {
24 R_RETURN(UnmapInsecureMemory(system, address, size));
25}
26
27Result MapInsecureMemory64From32(Core::System& system, uint32_t address, uint32_t size) {
28 R_RETURN(MapInsecureMemory(system, address, size));
29}
30
31Result UnmapInsecureMemory64From32(Core::System& system, uint32_t address, uint32_t size) {
32 R_RETURN(UnmapInsecureMemory(system, address, size));
33}
34
35} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_interrupt_event.cpp b/src/core/hle/kernel/svc/svc_interrupt_event.cpp
new file mode 100644
index 000000000..768b30a1f
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_interrupt_event.cpp
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5#include "core/hle/kernel/svc_results.h"
6
7namespace Kernel::Svc {
8
9Result CreateInterruptEvent(Core::System& system, Handle* out, int32_t interrupt_id,
10 InterruptType type) {
11 UNIMPLEMENTED();
12 R_THROW(ResultNotImplemented);
13}
14
15Result CreateInterruptEvent64(Core::System& system, Handle* out_read_handle, int32_t interrupt_id,
16 InterruptType interrupt_type) {
17 R_RETURN(CreateInterruptEvent(system, out_read_handle, interrupt_id, interrupt_type));
18}
19
20Result CreateInterruptEvent64From32(Core::System& system, Handle* out_read_handle,
21 int32_t interrupt_id, InterruptType interrupt_type) {
22 R_RETURN(CreateInterruptEvent(system, out_read_handle, interrupt_id, interrupt_type));
23}
24
25} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_io_pool.cpp b/src/core/hle/kernel/svc/svc_io_pool.cpp
new file mode 100644
index 000000000..33f3d69bf
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_io_pool.cpp
@@ -0,0 +1,71 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5#include "core/hle/kernel/svc_results.h"
6
7namespace Kernel::Svc {
8
9Result CreateIoPool(Core::System& system, Handle* out, IoPoolType pool_type) {
10 UNIMPLEMENTED();
11 R_THROW(ResultNotImplemented);
12}
13
14Result CreateIoRegion(Core::System& system, Handle* out, Handle io_pool_handle, uint64_t phys_addr,
15 size_t size, MemoryMapping mapping, MemoryPermission perm) {
16 UNIMPLEMENTED();
17 R_THROW(ResultNotImplemented);
18}
19
20Result MapIoRegion(Core::System& system, Handle io_region_handle, uintptr_t address, size_t size,
21 MemoryPermission map_perm) {
22 UNIMPLEMENTED();
23 R_THROW(ResultNotImplemented);
24}
25
26Result UnmapIoRegion(Core::System& system, Handle io_region_handle, uintptr_t address,
27 size_t size) {
28 UNIMPLEMENTED();
29 R_THROW(ResultNotImplemented);
30}
31
32Result CreateIoPool64(Core::System& system, Handle* out_handle, IoPoolType pool_type) {
33 R_RETURN(CreateIoPool(system, out_handle, pool_type));
34}
35
36Result CreateIoRegion64(Core::System& system, Handle* out_handle, Handle io_pool,
37 uint64_t physical_address, uint64_t size, MemoryMapping mapping,
38 MemoryPermission perm) {
39 R_RETURN(CreateIoRegion(system, out_handle, io_pool, physical_address, size, mapping, perm));
40}
41
42Result MapIoRegion64(Core::System& system, Handle io_region, uint64_t address, uint64_t size,
43 MemoryPermission perm) {
44 R_RETURN(MapIoRegion(system, io_region, address, size, perm));
45}
46
47Result UnmapIoRegion64(Core::System& system, Handle io_region, uint64_t address, uint64_t size) {
48 R_RETURN(UnmapIoRegion(system, io_region, address, size));
49}
50
51Result CreateIoPool64From32(Core::System& system, Handle* out_handle, IoPoolType pool_type) {
52 R_RETURN(CreateIoPool(system, out_handle, pool_type));
53}
54
55Result CreateIoRegion64From32(Core::System& system, Handle* out_handle, Handle io_pool,
56 uint64_t physical_address, uint32_t size, MemoryMapping mapping,
57 MemoryPermission perm) {
58 R_RETURN(CreateIoRegion(system, out_handle, io_pool, physical_address, size, mapping, perm));
59}
60
61Result MapIoRegion64From32(Core::System& system, Handle io_region, uint32_t address, uint32_t size,
62 MemoryPermission perm) {
63 R_RETURN(MapIoRegion(system, io_region, address, size, perm));
64}
65
66Result UnmapIoRegion64From32(Core::System& system, Handle io_region, uint32_t address,
67 uint32_t size) {
68 R_RETURN(UnmapIoRegion(system, io_region, address, size));
69}
70
71} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_ipc.cpp b/src/core/hle/kernel/svc/svc_ipc.cpp
new file mode 100644
index 000000000..97ce49dde
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_ipc.cpp
@@ -0,0 +1,174 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_client_session.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_server_session.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12
13/// Makes a blocking IPC call to a service.
14Result SendSyncRequest(Core::System& system, Handle handle) {
15 auto& kernel = system.Kernel();
16
17 // Get the client session from its handle.
18 KScopedAutoObject session =
19 kernel.CurrentProcess()->GetHandleTable().GetObject<KClientSession>(handle);
20 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
21
22 LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
23
24 return session->SendSyncRequest();
25}
26
27Result SendSyncRequestWithUserBuffer(Core::System& system, uint64_t message_buffer,
28 uint64_t message_buffer_size, Handle session_handle) {
29 UNIMPLEMENTED();
30 R_THROW(ResultNotImplemented);
31}
32
33Result SendAsyncRequestWithUserBuffer(Core::System& system, Handle* out_event_handle,
34 uint64_t message_buffer, uint64_t message_buffer_size,
35 Handle session_handle) {
36 UNIMPLEMENTED();
37 R_THROW(ResultNotImplemented);
38}
39
40Result ReplyAndReceive(Core::System& system, s32* out_index, uint64_t handles_addr, s32 num_handles,
41 Handle reply_target, s64 timeout_ns) {
42 auto& kernel = system.Kernel();
43 auto& handle_table = GetCurrentThread(kernel).GetOwnerProcess()->GetHandleTable();
44
45 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
46 R_UNLESS(system.Memory().IsValidVirtualAddressRange(
47 handles_addr, static_cast<u64>(sizeof(Handle) * num_handles)),
48 ResultInvalidPointer);
49
50 std::vector<Handle> handles(num_handles);
51 system.Memory().ReadBlock(handles_addr, handles.data(), sizeof(Handle) * num_handles);
52
53 // Convert handle list to object table.
54 std::vector<KSynchronizationObject*> objs(num_handles);
55 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles.data(),
56 num_handles),
57 ResultInvalidHandle);
58
59 // Ensure handles are closed when we're done.
60 SCOPE_EXIT({
61 for (auto i = 0; i < num_handles; ++i) {
62 objs[i]->Close();
63 }
64 });
65
66 // Reply to the target, if one is specified.
67 if (reply_target != InvalidHandle) {
68 KScopedAutoObject session = handle_table.GetObject<KServerSession>(reply_target);
69 R_UNLESS(session.IsNotNull(), ResultInvalidHandle);
70
71 // If we fail to reply, we want to set the output index to -1.
72 ON_RESULT_FAILURE {
73 *out_index = -1;
74 };
75
76 // Send the reply.
77 R_TRY(session->SendReply());
78 }
79
80 // Wait for a message.
81 while (true) {
82 // Wait for an object.
83 s32 index;
84 Result result = KSynchronizationObject::Wait(kernel, &index, objs.data(),
85 static_cast<s32>(objs.size()), timeout_ns);
86 if (result == ResultTimedOut) {
87 return result;
88 }
89
90 // Receive the request.
91 if (R_SUCCEEDED(result)) {
92 KServerSession* session = objs[index]->DynamicCast<KServerSession*>();
93 if (session != nullptr) {
94 result = session->ReceiveRequest();
95 if (result == ResultNotFound) {
96 continue;
97 }
98 }
99 }
100
101 *out_index = index;
102 return result;
103 }
104}
105
106Result ReplyAndReceiveWithUserBuffer(Core::System& system, int32_t* out_index,
107 uint64_t message_buffer, uint64_t message_buffer_size,
108 uint64_t handles, int32_t num_handles, Handle reply_target,
109 int64_t timeout_ns) {
110 UNIMPLEMENTED();
111 R_THROW(ResultNotImplemented);
112}
113
114Result SendSyncRequest64(Core::System& system, Handle session_handle) {
115 R_RETURN(SendSyncRequest(system, session_handle));
116}
117
118Result SendSyncRequestWithUserBuffer64(Core::System& system, uint64_t message_buffer,
119 uint64_t message_buffer_size, Handle session_handle) {
120 R_RETURN(
121 SendSyncRequestWithUserBuffer(system, message_buffer, message_buffer_size, session_handle));
122}
123
124Result SendAsyncRequestWithUserBuffer64(Core::System& system, Handle* out_event_handle,
125 uint64_t message_buffer, uint64_t message_buffer_size,
126 Handle session_handle) {
127 R_RETURN(SendAsyncRequestWithUserBuffer(system, out_event_handle, message_buffer,
128 message_buffer_size, session_handle));
129}
130
131Result ReplyAndReceive64(Core::System& system, int32_t* out_index, uint64_t handles,
132 int32_t num_handles, Handle reply_target, int64_t timeout_ns) {
133 R_RETURN(ReplyAndReceive(system, out_index, handles, num_handles, reply_target, timeout_ns));
134}
135
136Result ReplyAndReceiveWithUserBuffer64(Core::System& system, int32_t* out_index,
137 uint64_t message_buffer, uint64_t message_buffer_size,
138 uint64_t handles, int32_t num_handles, Handle reply_target,
139 int64_t timeout_ns) {
140 R_RETURN(ReplyAndReceiveWithUserBuffer(system, out_index, message_buffer, message_buffer_size,
141 handles, num_handles, reply_target, timeout_ns));
142}
143
144Result SendSyncRequest64From32(Core::System& system, Handle session_handle) {
145 R_RETURN(SendSyncRequest(system, session_handle));
146}
147
148Result SendSyncRequestWithUserBuffer64From32(Core::System& system, uint32_t message_buffer,
149 uint32_t message_buffer_size, Handle session_handle) {
150 R_RETURN(
151 SendSyncRequestWithUserBuffer(system, message_buffer, message_buffer_size, session_handle));
152}
153
154Result SendAsyncRequestWithUserBuffer64From32(Core::System& system, Handle* out_event_handle,
155 uint32_t message_buffer, uint32_t message_buffer_size,
156 Handle session_handle) {
157 R_RETURN(SendAsyncRequestWithUserBuffer(system, out_event_handle, message_buffer,
158 message_buffer_size, session_handle));
159}
160
161Result ReplyAndReceive64From32(Core::System& system, int32_t* out_index, uint32_t handles,
162 int32_t num_handles, Handle reply_target, int64_t timeout_ns) {
163 R_RETURN(ReplyAndReceive(system, out_index, handles, num_handles, reply_target, timeout_ns));
164}
165
166Result ReplyAndReceiveWithUserBuffer64From32(Core::System& system, int32_t* out_index,
167 uint32_t message_buffer, uint32_t message_buffer_size,
168 uint32_t handles, int32_t num_handles,
169 Handle reply_target, int64_t timeout_ns) {
170 R_RETURN(ReplyAndReceiveWithUserBuffer(system, out_index, message_buffer, message_buffer_size,
171 handles, num_handles, reply_target, timeout_ns));
172}
173
174} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_kernel_debug.cpp b/src/core/hle/kernel/svc/svc_kernel_debug.cpp
new file mode 100644
index 000000000..cee048279
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_kernel_debug.cpp
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5
6namespace Kernel::Svc {
7
8void KernelDebug(Core::System& system, KernelDebugType kernel_debug_type, u64 arg0, u64 arg1,
9 u64 arg2) {
10 // Intentionally do nothing, as this does nothing in released kernel binaries.
11}
12
13void ChangeKernelTraceState(Core::System& system, KernelTraceState trace_state) {
14 // Intentionally do nothing, as this does nothing in released kernel binaries.
15}
16
17void KernelDebug64(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0,
18 uint64_t arg1, uint64_t arg2) {
19 KernelDebug(system, kern_debug_type, arg0, arg1, arg2);
20}
21
22void ChangeKernelTraceState64(Core::System& system, KernelTraceState kern_trace_state) {
23 ChangeKernelTraceState(system, kern_trace_state);
24}
25
26void KernelDebug64From32(Core::System& system, KernelDebugType kern_debug_type, uint64_t arg0,
27 uint64_t arg1, uint64_t arg2) {
28 KernelDebug(system, kern_debug_type, arg0, arg1, arg2);
29}
30
31void ChangeKernelTraceState64From32(Core::System& system, KernelTraceState kern_trace_state) {
32 ChangeKernelTraceState(system, kern_trace_state);
33}
34
35} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_light_ipc.cpp b/src/core/hle/kernel/svc/svc_light_ipc.cpp
new file mode 100644
index 000000000..b76ce984c
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_light_ipc.cpp
@@ -0,0 +1,73 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/arm/arm_interface.h"
5#include "core/core.h"
6#include "core/hle/kernel/svc.h"
7#include "core/hle/kernel/svc_results.h"
8
9namespace Kernel::Svc {
10
11Result SendSyncRequestLight(Core::System& system, Handle session_handle, u32* args) {
12 UNIMPLEMENTED();
13 R_THROW(ResultNotImplemented);
14}
15
16Result ReplyAndReceiveLight(Core::System& system, Handle session_handle, u32* args) {
17 UNIMPLEMENTED();
18 R_THROW(ResultNotImplemented);
19}
20
21Result SendSyncRequestLight64(Core::System& system, Handle session_handle, u32* args) {
22 R_RETURN(SendSyncRequestLight(system, session_handle, args));
23}
24
25Result ReplyAndReceiveLight64(Core::System& system, Handle session_handle, u32* args) {
26 R_RETURN(ReplyAndReceiveLight(system, session_handle, args));
27}
28
29Result SendSyncRequestLight64From32(Core::System& system, Handle session_handle, u32* args) {
30 R_RETURN(SendSyncRequestLight(system, session_handle, args));
31}
32
33Result ReplyAndReceiveLight64From32(Core::System& system, Handle session_handle, u32* args) {
34 R_RETURN(ReplyAndReceiveLight(system, session_handle, args));
35}
36
37// Custom ABI implementation for light IPC.
38
39template <typename F>
40static void SvcWrap_LightIpc(Core::System& system, F&& cb) {
41 auto& core = system.CurrentArmInterface();
42 std::array<u32, 7> arguments{};
43
44 Handle session_handle = static_cast<Handle>(core.GetReg(0));
45 for (int i = 0; i < 7; i++) {
46 arguments[i] = static_cast<u32>(core.GetReg(i + 1));
47 }
48
49 Result ret = cb(system, session_handle, arguments.data());
50
51 core.SetReg(0, ret.raw);
52 for (int i = 0; i < 7; i++) {
53 core.SetReg(i + 1, arguments[i]);
54 }
55}
56
57void SvcWrap_SendSyncRequestLight64(Core::System& system) {
58 SvcWrap_LightIpc(system, SendSyncRequestLight64);
59}
60
61void SvcWrap_ReplyAndReceiveLight64(Core::System& system) {
62 SvcWrap_LightIpc(system, ReplyAndReceiveLight64);
63}
64
65void SvcWrap_SendSyncRequestLight64From32(Core::System& system) {
66 SvcWrap_LightIpc(system, SendSyncRequestLight64From32);
67}
68
69void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system) {
70 SvcWrap_LightIpc(system, ReplyAndReceiveLight64From32);
71}
72
73} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_lock.cpp b/src/core/hle/kernel/svc/svc_lock.cpp
new file mode 100644
index 000000000..7005ac30b
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_lock.cpp
@@ -0,0 +1,66 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_memory_layout.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// Attempts to locks a mutex
12Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
13 LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
14 thread_handle, address, tag);
15
16 // Validate the input address.
17 if (IsKernelAddress(address)) {
18 LOG_ERROR(Kernel_SVC, "Attempting to arbitrate a lock on a kernel address (address={:08X})",
19 address);
20 return ResultInvalidCurrentMemory;
21 }
22 if (!Common::IsAligned(address, sizeof(u32))) {
23 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
24 return ResultInvalidAddress;
25 }
26
27 return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
28}
29
30/// Unlock a mutex
31Result ArbitrateUnlock(Core::System& system, VAddr address) {
32 LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
33
34 // Validate the input address.
35 if (IsKernelAddress(address)) {
36 LOG_ERROR(Kernel_SVC,
37 "Attempting to arbitrate an unlock on a kernel address (address={:08X})",
38 address);
39 return ResultInvalidCurrentMemory;
40 }
41 if (!Common::IsAligned(address, sizeof(u32))) {
42 LOG_ERROR(Kernel_SVC, "Input address must be 4 byte aligned (address: {:08X})", address);
43 return ResultInvalidAddress;
44 }
45
46 return system.Kernel().CurrentProcess()->SignalToAddress(address);
47}
48
49Result ArbitrateLock64(Core::System& system, Handle thread_handle, uint64_t address, uint32_t tag) {
50 R_RETURN(ArbitrateLock(system, thread_handle, address, tag));
51}
52
53Result ArbitrateUnlock64(Core::System& system, uint64_t address) {
54 R_RETURN(ArbitrateUnlock(system, address));
55}
56
57Result ArbitrateLock64From32(Core::System& system, Handle thread_handle, uint32_t address,
58 uint32_t tag) {
59 R_RETURN(ArbitrateLock(system, thread_handle, address, tag));
60}
61
62Result ArbitrateUnlock64From32(Core::System& system, uint32_t address) {
63 R_RETURN(ArbitrateUnlock(system, address));
64}
65
66} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp
new file mode 100644
index 000000000..21f818da6
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_memory.cpp
@@ -0,0 +1,217 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9namespace {
10
11constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
12 switch (perm) {
13 case MemoryPermission::None:
14 case MemoryPermission::Read:
15 case MemoryPermission::ReadWrite:
16 return true;
17 default:
18 return false;
19 }
20}
21
22// Checks if address + size is greater than the given address
23// This can return false if the size causes an overflow of a 64-bit type
24// or if the given size is zero.
25constexpr bool IsValidAddressRange(VAddr address, u64 size) {
26 return address + size > address;
27}
28
29// Helper function that performs the common sanity checks for svcMapMemory
30// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
31// in the same order.
32Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
33 u64 size) {
34 if (!Common::Is4KBAligned(dst_addr)) {
35 LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
36 return ResultInvalidAddress;
37 }
38
39 if (!Common::Is4KBAligned(src_addr)) {
40 LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
41 return ResultInvalidSize;
42 }
43
44 if (size == 0) {
45 LOG_ERROR(Kernel_SVC, "Size is 0");
46 return ResultInvalidSize;
47 }
48
49 if (!Common::Is4KBAligned(size)) {
50 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
51 return ResultInvalidSize;
52 }
53
54 if (!IsValidAddressRange(dst_addr, size)) {
55 LOG_ERROR(Kernel_SVC,
56 "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
57 dst_addr, size);
58 return ResultInvalidCurrentMemory;
59 }
60
61 if (!IsValidAddressRange(src_addr, size)) {
62 LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
63 src_addr, size);
64 return ResultInvalidCurrentMemory;
65 }
66
67 if (!manager.IsInsideAddressSpace(src_addr, size)) {
68 LOG_ERROR(Kernel_SVC,
69 "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
70 src_addr, size);
71 return ResultInvalidCurrentMemory;
72 }
73
74 if (manager.IsOutsideStackRegion(dst_addr, size)) {
75 LOG_ERROR(Kernel_SVC,
76 "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
77 dst_addr, size);
78 return ResultInvalidMemoryRegion;
79 }
80
81 if (manager.IsInsideHeapRegion(dst_addr, size)) {
82 LOG_ERROR(Kernel_SVC,
83 "Destination does not fit within the heap region, addr=0x{:016X}, "
84 "size=0x{:016X}",
85 dst_addr, size);
86 return ResultInvalidMemoryRegion;
87 }
88
89 if (manager.IsInsideAliasRegion(dst_addr, size)) {
90 LOG_ERROR(Kernel_SVC,
91 "Destination does not fit within the map region, addr=0x{:016X}, "
92 "size=0x{:016X}",
93 dst_addr, size);
94 return ResultInvalidMemoryRegion;
95 }
96
97 return ResultSuccess;
98}
99
100} // namespace
101
102Result SetMemoryPermission(Core::System& system, VAddr address, u64 size, MemoryPermission perm) {
103 LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
104 perm);
105
106 // Validate address / size.
107 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
108 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
109 R_UNLESS(size > 0, ResultInvalidSize);
110 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
111
112 // Validate the permission.
113 R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission);
114
115 // Validate that the region is in range for the current process.
116 auto& page_table = system.Kernel().CurrentProcess()->PageTable();
117 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
118
119 // Set the memory attribute.
120 return page_table.SetMemoryPermission(address, size, perm);
121}
122
123Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask, u32 attr) {
124 LOG_DEBUG(Kernel_SVC,
125 "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
126 size, mask, attr);
127
128 // Validate address / size.
129 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
130 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
131 R_UNLESS(size > 0, ResultInvalidSize);
132 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
133
134 // Validate the attribute and mask.
135 constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
136 R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
137 R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
138
139 // Validate that the region is in range for the current process.
140 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
141 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
142
143 // Set the memory attribute.
144 return page_table.SetMemoryAttribute(address, size, mask, attr);
145}
146
147/// Maps a memory range into a different range.
148Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
149 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
150 src_addr, size);
151
152 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
153
154 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
155 result.IsError()) {
156 return result;
157 }
158
159 return page_table.MapMemory(dst_addr, src_addr, size);
160}
161
162/// Unmaps a region that was previously mapped with svcMapMemory
163Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
164 LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
165 src_addr, size);
166
167 auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
168
169 if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
170 result.IsError()) {
171 return result;
172 }
173
174 return page_table.UnmapMemory(dst_addr, src_addr, size);
175}
176
177Result SetMemoryPermission64(Core::System& system, uint64_t address, uint64_t size,
178 MemoryPermission perm) {
179 R_RETURN(SetMemoryPermission(system, address, size, perm));
180}
181
182Result SetMemoryAttribute64(Core::System& system, uint64_t address, uint64_t size, uint32_t mask,
183 uint32_t attr) {
184 R_RETURN(SetMemoryAttribute(system, address, size, mask, attr));
185}
186
187Result MapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address,
188 uint64_t size) {
189 R_RETURN(MapMemory(system, dst_address, src_address, size));
190}
191
192Result UnmapMemory64(Core::System& system, uint64_t dst_address, uint64_t src_address,
193 uint64_t size) {
194 R_RETURN(UnmapMemory(system, dst_address, src_address, size));
195}
196
197Result SetMemoryPermission64From32(Core::System& system, uint32_t address, uint32_t size,
198 MemoryPermission perm) {
199 R_RETURN(SetMemoryPermission(system, address, size, perm));
200}
201
202Result SetMemoryAttribute64From32(Core::System& system, uint32_t address, uint32_t size,
203 uint32_t mask, uint32_t attr) {
204 R_RETURN(SetMemoryAttribute(system, address, size, mask, attr));
205}
206
207Result MapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address,
208 uint32_t size) {
209 R_RETURN(MapMemory(system, dst_address, src_address, size));
210}
211
212Result UnmapMemory64From32(Core::System& system, uint32_t dst_address, uint32_t src_address,
213 uint32_t size) {
214 R_RETURN(UnmapMemory(system, dst_address, src_address, size));
215}
216
217} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp
new file mode 100644
index 000000000..8d00e1fc3
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp
@@ -0,0 +1,185 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10/// Set the process heap to a given Size. It can both extend and shrink the heap.
11Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
12 LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
13
14 // Validate size.
15 R_UNLESS(Common::IsAligned(size, HeapSizeAlignment), ResultInvalidSize);
16 R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize);
17
18 // Set the heap size.
19 R_TRY(system.Kernel().CurrentProcess()->PageTable().SetHeapSize(out_address, size));
20
21 return ResultSuccess;
22}
23
24/// Maps memory at a desired address
25Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
26 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
27
28 if (!Common::Is4KBAligned(addr)) {
29 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
30 return ResultInvalidAddress;
31 }
32
33 if (!Common::Is4KBAligned(size)) {
34 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
35 return ResultInvalidSize;
36 }
37
38 if (size == 0) {
39 LOG_ERROR(Kernel_SVC, "Size is zero");
40 return ResultInvalidSize;
41 }
42
43 if (!(addr < addr + size)) {
44 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
45 return ResultInvalidMemoryRegion;
46 }
47
48 KProcess* const current_process{system.Kernel().CurrentProcess()};
49 auto& page_table{current_process->PageTable()};
50
51 if (current_process->GetSystemResourceSize() == 0) {
52 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
53 return ResultInvalidState;
54 }
55
56 if (!page_table.IsInsideAddressSpace(addr, size)) {
57 LOG_ERROR(Kernel_SVC,
58 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
59 size);
60 return ResultInvalidMemoryRegion;
61 }
62
63 if (page_table.IsOutsideAliasRegion(addr, size)) {
64 LOG_ERROR(Kernel_SVC,
65 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
66 size);
67 return ResultInvalidMemoryRegion;
68 }
69
70 return page_table.MapPhysicalMemory(addr, size);
71}
72
73/// Unmaps memory previously mapped via MapPhysicalMemory
74Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
75 LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
76
77 if (!Common::Is4KBAligned(addr)) {
78 LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
79 return ResultInvalidAddress;
80 }
81
82 if (!Common::Is4KBAligned(size)) {
83 LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
84 return ResultInvalidSize;
85 }
86
87 if (size == 0) {
88 LOG_ERROR(Kernel_SVC, "Size is zero");
89 return ResultInvalidSize;
90 }
91
92 if (!(addr < addr + size)) {
93 LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
94 return ResultInvalidMemoryRegion;
95 }
96
97 KProcess* const current_process{system.Kernel().CurrentProcess()};
98 auto& page_table{current_process->PageTable()};
99
100 if (current_process->GetSystemResourceSize() == 0) {
101 LOG_ERROR(Kernel_SVC, "System Resource Size is zero");
102 return ResultInvalidState;
103 }
104
105 if (!page_table.IsInsideAddressSpace(addr, size)) {
106 LOG_ERROR(Kernel_SVC,
107 "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
108 size);
109 return ResultInvalidMemoryRegion;
110 }
111
112 if (page_table.IsOutsideAliasRegion(addr, size)) {
113 LOG_ERROR(Kernel_SVC,
114 "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr,
115 size);
116 return ResultInvalidMemoryRegion;
117 }
118
119 return page_table.UnmapPhysicalMemory(addr, size);
120}
121
122Result MapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size) {
123 UNIMPLEMENTED();
124 R_THROW(ResultNotImplemented);
125}
126
127Result UnmapPhysicalMemoryUnsafe(Core::System& system, uint64_t address, uint64_t size) {
128 UNIMPLEMENTED();
129 R_THROW(ResultNotImplemented);
130}
131
132Result SetUnsafeLimit(Core::System& system, uint64_t limit) {
133 UNIMPLEMENTED();
134 R_THROW(ResultNotImplemented);
135}
136
137Result SetHeapSize64(Core::System& system, uint64_t* out_address, uint64_t size) {
138 R_RETURN(SetHeapSize(system, out_address, size));
139}
140
141Result MapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size) {
142 R_RETURN(MapPhysicalMemory(system, address, size));
143}
144
145Result UnmapPhysicalMemory64(Core::System& system, uint64_t address, uint64_t size) {
146 R_RETURN(UnmapPhysicalMemory(system, address, size));
147}
148
149Result MapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size) {
150 R_RETURN(MapPhysicalMemoryUnsafe(system, address, size));
151}
152
153Result UnmapPhysicalMemoryUnsafe64(Core::System& system, uint64_t address, uint64_t size) {
154 R_RETURN(UnmapPhysicalMemoryUnsafe(system, address, size));
155}
156
157Result SetUnsafeLimit64(Core::System& system, uint64_t limit) {
158 R_RETURN(SetUnsafeLimit(system, limit));
159}
160
161Result SetHeapSize64From32(Core::System& system, uintptr_t* out_address, uint32_t size) {
162 R_RETURN(SetHeapSize(system, out_address, size));
163}
164
165Result MapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size) {
166 R_RETURN(MapPhysicalMemory(system, address, size));
167}
168
169Result UnmapPhysicalMemory64From32(Core::System& system, uint32_t address, uint32_t size) {
170 R_RETURN(UnmapPhysicalMemory(system, address, size));
171}
172
173Result MapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size) {
174 R_RETURN(MapPhysicalMemoryUnsafe(system, address, size));
175}
176
177Result UnmapPhysicalMemoryUnsafe64From32(Core::System& system, uint32_t address, uint32_t size) {
178 R_RETURN(UnmapPhysicalMemoryUnsafe(system, address, size));
179}
180
181Result SetUnsafeLimit64From32(Core::System& system, uint32_t limit) {
182 R_RETURN(SetUnsafeLimit(system, limit));
183}
184
185} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_port.cpp b/src/core/hle/kernel/svc/svc_port.cpp
new file mode 100644
index 000000000..2e5d228bb
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_port.cpp
@@ -0,0 +1,122 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_client_port.h"
7#include "core/hle/kernel/k_client_session.h"
8#include "core/hle/kernel/k_port.h"
9#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13
14/// Connect to an OS service given the port name, returns the handle to the port to out
15Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
16 auto& memory = system.Memory();
17 if (!memory.IsValidVirtualAddress(port_name_address)) {
18 LOG_ERROR(Kernel_SVC,
19 "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
20 port_name_address);
21 return ResultNotFound;
22 }
23
24 static constexpr std::size_t PortNameMaxLength = 11;
25 // Read 1 char beyond the max allowed port name to detect names that are too long.
26 const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
27 if (port_name.size() > PortNameMaxLength) {
28 LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
29 port_name.size());
30 return ResultOutOfRange;
31 }
32
33 LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
34
35 // Get the current handle table.
36 auto& kernel = system.Kernel();
37 auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
38
39 // Find the client port.
40 auto port = kernel.CreateNamedServicePort(port_name);
41 if (!port) {
42 LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
43 return ResultNotFound;
44 }
45
46 // Reserve a handle for the port.
47 // NOTE: Nintendo really does write directly to the output handle here.
48 R_TRY(handle_table.Reserve(out));
49 auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
50
51 // Create a session.
52 KClientSession* session{};
53 R_TRY(port->CreateSession(std::addressof(session)));
54
55 kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
56
57 // Register the session in the table, close the extra reference.
58 handle_table.Register(*out, session);
59 session->Close();
60
61 // We succeeded.
62 handle_guard.Cancel();
63 return ResultSuccess;
64}
65
66Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
67 int32_t max_sessions, bool is_light, uintptr_t name) {
68 UNIMPLEMENTED();
69 R_THROW(ResultNotImplemented);
70}
71
72Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port) {
73 UNIMPLEMENTED();
74 R_THROW(ResultNotImplemented);
75}
76
77Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t name,
78 int32_t max_sessions) {
79 UNIMPLEMENTED();
80 R_THROW(ResultNotImplemented);
81}
82
83Result ConnectToNamedPort64(Core::System& system, Handle* out_handle, uint64_t name) {
84 R_RETURN(ConnectToNamedPort(system, out_handle, name));
85}
86
87Result CreatePort64(Core::System& system, Handle* out_server_handle, Handle* out_client_handle,
88 int32_t max_sessions, bool is_light, uint64_t name) {
89 R_RETURN(
90 CreatePort(system, out_server_handle, out_client_handle, max_sessions, is_light, name));
91}
92
93Result ManageNamedPort64(Core::System& system, Handle* out_server_handle, uint64_t name,
94 int32_t max_sessions) {
95 R_RETURN(ManageNamedPort(system, out_server_handle, name, max_sessions));
96}
97
98Result ConnectToPort64(Core::System& system, Handle* out_handle, Handle port) {
99 R_RETURN(ConnectToPort(system, out_handle, port));
100}
101
102Result ConnectToNamedPort64From32(Core::System& system, Handle* out_handle, uint32_t name) {
103 R_RETURN(ConnectToNamedPort(system, out_handle, name));
104}
105
106Result CreatePort64From32(Core::System& system, Handle* out_server_handle,
107 Handle* out_client_handle, int32_t max_sessions, bool is_light,
108 uint32_t name) {
109 R_RETURN(
110 CreatePort(system, out_server_handle, out_client_handle, max_sessions, is_light, name));
111}
112
113Result ManageNamedPort64From32(Core::System& system, Handle* out_server_handle, uint32_t name,
114 int32_t max_sessions) {
115 R_RETURN(ManageNamedPort(system, out_server_handle, name, max_sessions));
116}
117
118Result ConnectToPort64From32(Core::System& system, Handle* out_handle, Handle port) {
119 R_RETURN(ConnectToPort(system, out_handle, port));
120}
121
122} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_power_management.cpp b/src/core/hle/kernel/svc/svc_power_management.cpp
new file mode 100644
index 000000000..f605a0317
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_power_management.cpp
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5#include "core/hle/kernel/svc_results.h"
6
7namespace Kernel::Svc {
8
9void SleepSystem(Core::System& system) {
10 UNIMPLEMENTED();
11}
12
13void SleepSystem64(Core::System& system) {
14 return SleepSystem(system);
15}
16
17void SleepSystem64From32(Core::System& system) {
18 return SleepSystem(system);
19}
20
21} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp
new file mode 100644
index 000000000..d2c20aad2
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_process.cpp
@@ -0,0 +1,194 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10/// Exits the current process
11void ExitProcess(Core::System& system) {
12 auto* current_process = system.Kernel().CurrentProcess();
13
14 LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
15 ASSERT_MSG(current_process->GetState() == KProcess::State::Running,
16 "Process has already exited");
17
18 system.Exit();
19}
20
21/// Gets the ID of the specified process or a specified thread's owning process.
22Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
23 LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
24
25 // Get the object from the handle table.
26 KScopedAutoObject obj =
27 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KAutoObject>(
28 static_cast<Handle>(handle));
29 R_UNLESS(obj.IsNotNull(), ResultInvalidHandle);
30
31 // Get the process from the object.
32 KProcess* process = nullptr;
33 if (KProcess* p = obj->DynamicCast<KProcess*>(); p != nullptr) {
34 // The object is a process, so we can use it directly.
35 process = p;
36 } else if (KThread* t = obj->DynamicCast<KThread*>(); t != nullptr) {
37 // The object is a thread, so we want to use its parent.
38 process = reinterpret_cast<KThread*>(obj.GetPointerUnsafe())->GetOwnerProcess();
39 } else {
40 // TODO(bunnei): This should also handle debug objects before returning.
41 UNIMPLEMENTED_MSG("Debug objects not implemented");
42 }
43
44 // Make sure the target process exists.
45 R_UNLESS(process != nullptr, ResultInvalidHandle);
46
47 // Get the process id.
48 *out_process_id = process->GetId();
49
50 return ResultSuccess;
51}
52
53Result GetProcessList(Core::System& system, s32* out_num_processes, VAddr out_process_ids,
54 int32_t out_process_ids_size) {
55 LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
56 out_process_ids, out_process_ids_size);
57
58 // If the supplied size is negative or greater than INT32_MAX / sizeof(u64), bail.
59 if ((out_process_ids_size & 0xF0000000) != 0) {
60 LOG_ERROR(Kernel_SVC,
61 "Supplied size outside [0, 0x0FFFFFFF] range. out_process_ids_size={}",
62 out_process_ids_size);
63 return ResultOutOfRange;
64 }
65
66 const auto& kernel = system.Kernel();
67 const auto total_copy_size = out_process_ids_size * sizeof(u64);
68
69 if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace(
70 out_process_ids, total_copy_size)) {
71 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
72 out_process_ids, out_process_ids + total_copy_size);
73 return ResultInvalidCurrentMemory;
74 }
75
76 auto& memory = system.Memory();
77 const auto& process_list = kernel.GetProcessList();
78 const auto num_processes = process_list.size();
79 const auto copy_amount =
80 std::min(static_cast<std::size_t>(out_process_ids_size), num_processes);
81
82 for (std::size_t i = 0; i < copy_amount; ++i) {
83 memory.Write64(out_process_ids, process_list[i]->GetProcessID());
84 out_process_ids += sizeof(u64);
85 }
86
87 *out_num_processes = static_cast<u32>(num_processes);
88 return ResultSuccess;
89}
90
91Result GetProcessInfo(Core::System& system, s64* out, Handle process_handle,
92 ProcessInfoType info_type) {
93 LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, info_type);
94
95 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
96 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
97 if (process.IsNull()) {
98 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
99 process_handle);
100 return ResultInvalidHandle;
101 }
102
103 if (info_type != ProcessInfoType::ProcessState) {
104 LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead",
105 info_type);
106 return ResultInvalidEnumValue;
107 }
108
109 *out = static_cast<s64>(process->GetState());
110 return ResultSuccess;
111}
112
113Result CreateProcess(Core::System& system, Handle* out_handle, uint64_t parameters, uint64_t caps,
114 int32_t num_caps) {
115 UNIMPLEMENTED();
116 R_THROW(ResultNotImplemented);
117}
118
119Result StartProcess(Core::System& system, Handle process_handle, int32_t priority, int32_t core_id,
120 uint64_t main_thread_stack_size) {
121 UNIMPLEMENTED();
122 R_THROW(ResultNotImplemented);
123}
124
125Result TerminateProcess(Core::System& system, Handle process_handle) {
126 UNIMPLEMENTED();
127 R_THROW(ResultNotImplemented);
128}
129
130void ExitProcess64(Core::System& system) {
131 ExitProcess(system);
132}
133
134Result GetProcessId64(Core::System& system, uint64_t* out_process_id, Handle process_handle) {
135 R_RETURN(GetProcessId(system, out_process_id, process_handle));
136}
137
138Result GetProcessList64(Core::System& system, int32_t* out_num_processes, uint64_t out_process_ids,
139 int32_t max_out_count) {
140 R_RETURN(GetProcessList(system, out_num_processes, out_process_ids, max_out_count));
141}
142
143Result CreateProcess64(Core::System& system, Handle* out_handle, uint64_t parameters, uint64_t caps,
144 int32_t num_caps) {
145 R_RETURN(CreateProcess(system, out_handle, parameters, caps, num_caps));
146}
147
148Result StartProcess64(Core::System& system, Handle process_handle, int32_t priority,
149 int32_t core_id, uint64_t main_thread_stack_size) {
150 R_RETURN(StartProcess(system, process_handle, priority, core_id, main_thread_stack_size));
151}
152
153Result TerminateProcess64(Core::System& system, Handle process_handle) {
154 R_RETURN(TerminateProcess(system, process_handle));
155}
156
157Result GetProcessInfo64(Core::System& system, int64_t* out_info, Handle process_handle,
158 ProcessInfoType info_type) {
159 R_RETURN(GetProcessInfo(system, out_info, process_handle, info_type));
160}
161
162void ExitProcess64From32(Core::System& system) {
163 ExitProcess(system);
164}
165
166Result GetProcessId64From32(Core::System& system, uint64_t* out_process_id, Handle process_handle) {
167 R_RETURN(GetProcessId(system, out_process_id, process_handle));
168}
169
170Result GetProcessList64From32(Core::System& system, int32_t* out_num_processes,
171 uint32_t out_process_ids, int32_t max_out_count) {
172 R_RETURN(GetProcessList(system, out_num_processes, out_process_ids, max_out_count));
173}
174
175Result CreateProcess64From32(Core::System& system, Handle* out_handle, uint32_t parameters,
176 uint32_t caps, int32_t num_caps) {
177 R_RETURN(CreateProcess(system, out_handle, parameters, caps, num_caps));
178}
179
180Result StartProcess64From32(Core::System& system, Handle process_handle, int32_t priority,
181 int32_t core_id, uint64_t main_thread_stack_size) {
182 R_RETURN(StartProcess(system, process_handle, priority, core_id, main_thread_stack_size));
183}
184
185Result TerminateProcess64From32(Core::System& system, Handle process_handle) {
186 R_RETURN(TerminateProcess(system, process_handle));
187}
188
189Result GetProcessInfo64From32(Core::System& system, int64_t* out_info, Handle process_handle,
190 ProcessInfoType info_type) {
191 R_RETURN(GetProcessInfo(system, out_info, process_handle, info_type));
192}
193
194} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp
new file mode 100644
index 000000000..dbe24e139
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_process_memory.cpp
@@ -0,0 +1,324 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9namespace {
10
11constexpr bool IsValidAddressRange(VAddr address, u64 size) {
12 return address + size > address;
13}
14
15constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
16 switch (perm) {
17 case Svc::MemoryPermission::None:
18 case Svc::MemoryPermission::Read:
19 case Svc::MemoryPermission::ReadWrite:
20 case Svc::MemoryPermission::ReadExecute:
21 return true;
22 default:
23 return false;
24 }
25}
26
27} // namespace
28
29Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
30 u64 size, Svc::MemoryPermission perm) {
31 LOG_TRACE(Kernel_SVC,
32 "called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
33 process_handle, address, size, perm);
34
35 // Validate the address/size.
36 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
37 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
38 R_UNLESS(size > 0, ResultInvalidSize);
39 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
40 R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory);
41 R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory);
42
43 // Validate the memory permission.
44 R_UNLESS(IsValidProcessMemoryPermission(perm), ResultInvalidNewMemoryPermission);
45
46 // Get the process from its handle.
47 KScopedAutoObject process =
48 system.CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle);
49 R_UNLESS(process.IsNotNull(), ResultInvalidHandle);
50
51 // Validate that the address is in range.
52 auto& page_table = process->PageTable();
53 R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
54
55 // Set the memory permission.
56 return page_table.SetProcessMemoryPermission(address, size, perm);
57}
58
59Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
60 VAddr src_address, u64 size) {
61 LOG_TRACE(Kernel_SVC,
62 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
63 dst_address, process_handle, src_address, size);
64
65 // Validate the address/size.
66 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
67 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
68 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
69 R_UNLESS(size > 0, ResultInvalidSize);
70 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
71 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
72
73 // Get the processes.
74 KProcess* dst_process = system.CurrentProcess();
75 KScopedAutoObject src_process =
76 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
77 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
78
79 // Get the page tables.
80 auto& dst_pt = dst_process->PageTable();
81 auto& src_pt = src_process->PageTable();
82
83 // Validate that the mapping is in range.
84 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
85 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
86 ResultInvalidMemoryRegion);
87
88 // Create a new page group.
89 KPageGroup pg{system.Kernel(), dst_pt.GetBlockInfoManager()};
90 R_TRY(src_pt.MakeAndOpenPageGroup(
91 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
92 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
93 KMemoryAttribute::All, KMemoryAttribute::None));
94
95 // Map the group.
96 R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode,
97 KMemoryPermission::UserReadWrite));
98
99 return ResultSuccess;
100}
101
102Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
103 VAddr src_address, u64 size) {
104 LOG_TRACE(Kernel_SVC,
105 "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
106 dst_address, process_handle, src_address, size);
107
108 // Validate the address/size.
109 R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
110 R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
111 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
112 R_UNLESS(size > 0, ResultInvalidSize);
113 R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
114 R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
115
116 // Get the processes.
117 KProcess* dst_process = system.CurrentProcess();
118 KScopedAutoObject src_process =
119 dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
120 R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
121
122 // Get the page tables.
123 auto& dst_pt = dst_process->PageTable();
124 auto& src_pt = src_process->PageTable();
125
126 // Validate that the mapping is in range.
127 R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
128 R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
129 ResultInvalidMemoryRegion);
130
131 // Unmap the memory.
132 R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
133
134 return ResultSuccess;
135}
136
137Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
138 u64 src_address, u64 size) {
139 LOG_DEBUG(Kernel_SVC,
140 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
141 "src_address=0x{:016X}, size=0x{:016X}",
142 process_handle, dst_address, src_address, size);
143
144 if (!Common::Is4KBAligned(src_address)) {
145 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
146 src_address);
147 return ResultInvalidAddress;
148 }
149
150 if (!Common::Is4KBAligned(dst_address)) {
151 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
152 dst_address);
153 return ResultInvalidAddress;
154 }
155
156 if (size == 0 || !Common::Is4KBAligned(size)) {
157 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size);
158 return ResultInvalidSize;
159 }
160
161 if (!IsValidAddressRange(dst_address, size)) {
162 LOG_ERROR(Kernel_SVC,
163 "Destination address range overflows the address space (dst_address=0x{:016X}, "
164 "size=0x{:016X}).",
165 dst_address, size);
166 return ResultInvalidCurrentMemory;
167 }
168
169 if (!IsValidAddressRange(src_address, size)) {
170 LOG_ERROR(Kernel_SVC,
171 "Source address range overflows the address space (src_address=0x{:016X}, "
172 "size=0x{:016X}).",
173 src_address, size);
174 return ResultInvalidCurrentMemory;
175 }
176
177 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
178 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
179 if (process.IsNull()) {
180 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
181 process_handle);
182 return ResultInvalidHandle;
183 }
184
185 auto& page_table = process->PageTable();
186 if (!page_table.IsInsideAddressSpace(src_address, size)) {
187 LOG_ERROR(Kernel_SVC,
188 "Source address range is not within the address space (src_address=0x{:016X}, "
189 "size=0x{:016X}).",
190 src_address, size);
191 return ResultInvalidCurrentMemory;
192 }
193
194 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
195 LOG_ERROR(Kernel_SVC,
196 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
197 "size=0x{:016X}).",
198 dst_address, size);
199 return ResultInvalidMemoryRegion;
200 }
201
202 return page_table.MapCodeMemory(dst_address, src_address, size);
203}
204
205Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
206 u64 src_address, u64 size) {
207 LOG_DEBUG(Kernel_SVC,
208 "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
209 "size=0x{:016X}",
210 process_handle, dst_address, src_address, size);
211
212 if (!Common::Is4KBAligned(dst_address)) {
213 LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).",
214 dst_address);
215 return ResultInvalidAddress;
216 }
217
218 if (!Common::Is4KBAligned(src_address)) {
219 LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).",
220 src_address);
221 return ResultInvalidAddress;
222 }
223
224 if (size == 0 || !Common::Is4KBAligned(size)) {
225 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
226 return ResultInvalidSize;
227 }
228
229 if (!IsValidAddressRange(dst_address, size)) {
230 LOG_ERROR(Kernel_SVC,
231 "Destination address range overflows the address space (dst_address=0x{:016X}, "
232 "size=0x{:016X}).",
233 dst_address, size);
234 return ResultInvalidCurrentMemory;
235 }
236
237 if (!IsValidAddressRange(src_address, size)) {
238 LOG_ERROR(Kernel_SVC,
239 "Source address range overflows the address space (src_address=0x{:016X}, "
240 "size=0x{:016X}).",
241 src_address, size);
242 return ResultInvalidCurrentMemory;
243 }
244
245 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
246 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
247 if (process.IsNull()) {
248 LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).",
249 process_handle);
250 return ResultInvalidHandle;
251 }
252
253 auto& page_table = process->PageTable();
254 if (!page_table.IsInsideAddressSpace(src_address, size)) {
255 LOG_ERROR(Kernel_SVC,
256 "Source address range is not within the address space (src_address=0x{:016X}, "
257 "size=0x{:016X}).",
258 src_address, size);
259 return ResultInvalidCurrentMemory;
260 }
261
262 if (!page_table.IsInsideASLRRegion(dst_address, size)) {
263 LOG_ERROR(Kernel_SVC,
264 "Destination address range is not within the ASLR region (dst_address=0x{:016X}, "
265 "size=0x{:016X}).",
266 dst_address, size);
267 return ResultInvalidMemoryRegion;
268 }
269
270 return page_table.UnmapCodeMemory(dst_address, src_address, size,
271 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
272}
273
274Result SetProcessMemoryPermission64(Core::System& system, Handle process_handle, uint64_t address,
275 uint64_t size, MemoryPermission perm) {
276 R_RETURN(SetProcessMemoryPermission(system, process_handle, address, size, perm));
277}
278
279Result MapProcessMemory64(Core::System& system, uint64_t dst_address, Handle process_handle,
280 uint64_t src_address, uint64_t size) {
281 R_RETURN(MapProcessMemory(system, dst_address, process_handle, src_address, size));
282}
283
284Result UnmapProcessMemory64(Core::System& system, uint64_t dst_address, Handle process_handle,
285 uint64_t src_address, uint64_t size) {
286 R_RETURN(UnmapProcessMemory(system, dst_address, process_handle, src_address, size));
287}
288
289Result MapProcessCodeMemory64(Core::System& system, Handle process_handle, uint64_t dst_address,
290 uint64_t src_address, uint64_t size) {
291 R_RETURN(MapProcessCodeMemory(system, process_handle, dst_address, src_address, size));
292}
293
294Result UnmapProcessCodeMemory64(Core::System& system, Handle process_handle, uint64_t dst_address,
295 uint64_t src_address, uint64_t size) {
296 R_RETURN(UnmapProcessCodeMemory(system, process_handle, dst_address, src_address, size));
297}
298
299Result SetProcessMemoryPermission64From32(Core::System& system, Handle process_handle,
300 uint64_t address, uint64_t size, MemoryPermission perm) {
301 R_RETURN(SetProcessMemoryPermission(system, process_handle, address, size, perm));
302}
303
304Result MapProcessMemory64From32(Core::System& system, uint32_t dst_address, Handle process_handle,
305 uint64_t src_address, uint32_t size) {
306 R_RETURN(MapProcessMemory(system, dst_address, process_handle, src_address, size));
307}
308
309Result UnmapProcessMemory64From32(Core::System& system, uint32_t dst_address, Handle process_handle,
310 uint64_t src_address, uint32_t size) {
311 R_RETURN(UnmapProcessMemory(system, dst_address, process_handle, src_address, size));
312}
313
314Result MapProcessCodeMemory64From32(Core::System& system, Handle process_handle,
315 uint64_t dst_address, uint64_t src_address, uint64_t size) {
316 R_RETURN(MapProcessCodeMemory(system, process_handle, dst_address, src_address, size));
317}
318
319Result UnmapProcessCodeMemory64From32(Core::System& system, Handle process_handle,
320 uint64_t dst_address, uint64_t src_address, uint64_t size) {
321 R_RETURN(UnmapProcessCodeMemory(system, process_handle, dst_address, src_address, size));
322}
323
324} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_processor.cpp b/src/core/hle/kernel/svc/svc_processor.cpp
new file mode 100644
index 000000000..7602ce6c0
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_processor.cpp
@@ -0,0 +1,25 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "core/core.h"
6#include "core/hle/kernel/physical_core.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// Get which CPU core is executing the current thread
12int32_t GetCurrentProcessorNumber(Core::System& system) {
13 LOG_TRACE(Kernel_SVC, "called");
14 return static_cast<int32_t>(system.CurrentPhysicalCore().CoreIndex());
15}
16
17int32_t GetCurrentProcessorNumber64(Core::System& system) {
18 return GetCurrentProcessorNumber(system);
19}
20
21int32_t GetCurrentProcessorNumber64From32(Core::System& system) {
22 return GetCurrentProcessorNumber(system);
23}
24
25} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp
new file mode 100644
index 000000000..db140a341
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_query_memory.cpp
@@ -0,0 +1,65 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/k_process.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10Result QueryMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
11 VAddr query_address) {
12 LOG_TRACE(Kernel_SVC,
13 "called, out_memory_info=0x{:016X}, "
14 "query_address=0x{:016X}",
15 out_memory_info, query_address);
16
17 // Query memory is just QueryProcessMemory on the current process.
18 return QueryProcessMemory(system, out_memory_info, out_page_info, CurrentProcess,
19 query_address);
20}
21
22Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
23 Handle process_handle, uint64_t address) {
24 LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
25 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
26 KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
27 if (process.IsNull()) {
28 LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
29 process_handle);
30 return ResultInvalidHandle;
31 }
32
33 auto& memory{system.Memory()};
34 const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()};
35
36 memory.WriteBlock(out_memory_info, &memory_info, sizeof(memory_info));
37
38 //! This is supposed to be part of the QueryInfo call.
39 *out_page_info = {};
40
41 R_SUCCEED();
42}
43
44Result QueryMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
45 uint64_t address) {
46 R_RETURN(QueryMemory(system, out_memory_info, out_page_info, address));
47}
48
49Result QueryProcessMemory64(Core::System& system, uint64_t out_memory_info, PageInfo* out_page_info,
50 Handle process_handle, uint64_t address) {
51 R_RETURN(QueryProcessMemory(system, out_memory_info, out_page_info, process_handle, address));
52}
53
54Result QueryMemory64From32(Core::System& system, uint32_t out_memory_info, PageInfo* out_page_info,
55 uint32_t address) {
56 R_RETURN(QueryMemory(system, out_memory_info, out_page_info, address));
57}
58
59Result QueryProcessMemory64From32(Core::System& system, uint32_t out_memory_info,
60 PageInfo* out_page_info, Handle process_handle,
61 uint64_t address) {
62 R_RETURN(QueryProcessMemory(system, out_memory_info, out_page_info, process_handle, address));
63}
64
65} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_register.cpp b/src/core/hle/kernel/svc/svc_register.cpp
new file mode 100644
index 000000000..b883e6618
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_register.cpp
@@ -0,0 +1,27 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5#include "core/hle/kernel/svc_results.h"
6
7namespace Kernel::Svc {
8
9Result ReadWriteRegister(Core::System& system, uint32_t* out, uint64_t address, uint32_t mask,
10 uint32_t value) {
11 *out = 0;
12
13 UNIMPLEMENTED();
14 R_THROW(ResultNotImplemented);
15}
16
17Result ReadWriteRegister64(Core::System& system, uint32_t* out_value, uint64_t address,
18 uint32_t mask, uint32_t value) {
19 R_RETURN(ReadWriteRegister(system, out_value, address, mask, value));
20}
21
22Result ReadWriteRegister64From32(Core::System& system, uint32_t* out_value, uint64_t address,
23 uint32_t mask, uint32_t value) {
24 R_RETURN(ReadWriteRegister(system, out_value, address, mask, value));
25}
26
27} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_resource_limit.cpp b/src/core/hle/kernel/svc/svc_resource_limit.cpp
new file mode 100644
index 000000000..ebc886028
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_resource_limit.cpp
@@ -0,0 +1,149 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_resource_limit.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
13 LOG_DEBUG(Kernel_SVC, "called");
14
15 // Create a new resource limit.
16 auto& kernel = system.Kernel();
17 KResourceLimit* resource_limit = KResourceLimit::Create(kernel);
18 R_UNLESS(resource_limit != nullptr, ResultOutOfResource);
19
20 // Ensure we don't leak a reference to the limit.
21 SCOPE_EXIT({ resource_limit->Close(); });
22
23 // Initialize the resource limit.
24 resource_limit->Initialize(&system.CoreTiming());
25
26 // Register the limit.
27 KResourceLimit::Register(kernel, resource_limit);
28
29 // Add the limit to the handle table.
30 R_TRY(kernel.CurrentProcess()->GetHandleTable().Add(out_handle, resource_limit));
31
32 return ResultSuccess;
33}
34
35Result GetResourceLimitLimitValue(Core::System& system, s64* out_limit_value,
36 Handle resource_limit_handle, LimitableResource which) {
37 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
38 which);
39
40 // Validate the resource.
41 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
42
43 // Get the resource limit.
44 auto& kernel = system.Kernel();
45 KScopedAutoObject resource_limit =
46 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
47 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
48
49 // Get the limit value.
50 *out_limit_value = resource_limit->GetLimitValue(which);
51
52 return ResultSuccess;
53}
54
55Result GetResourceLimitCurrentValue(Core::System& system, s64* out_current_value,
56 Handle resource_limit_handle, LimitableResource which) {
57 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
58 which);
59
60 // Validate the resource.
61 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
62
63 // Get the resource limit.
64 auto& kernel = system.Kernel();
65 KScopedAutoObject resource_limit =
66 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
67 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
68
69 // Get the current value.
70 *out_current_value = resource_limit->GetCurrentValue(which);
71
72 return ResultSuccess;
73}
74
75Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
76 LimitableResource which, s64 limit_value) {
77 LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
78 resource_limit_handle, which, limit_value);
79
80 // Validate the resource.
81 R_UNLESS(IsValidResourceType(which), ResultInvalidEnumValue);
82
83 // Get the resource limit.
84 auto& kernel = system.Kernel();
85 KScopedAutoObject resource_limit =
86 kernel.CurrentProcess()->GetHandleTable().GetObject<KResourceLimit>(resource_limit_handle);
87 R_UNLESS(resource_limit.IsNotNull(), ResultInvalidHandle);
88
89 // Set the limit value.
90 R_TRY(resource_limit->SetLimitValue(which, limit_value));
91
92 return ResultSuccess;
93}
94
95Result GetResourceLimitPeakValue(Core::System& system, int64_t* out_peak_value,
96 Handle resource_limit_handle, LimitableResource which) {
97 UNIMPLEMENTED();
98 R_THROW(ResultNotImplemented);
99}
100
101Result GetResourceLimitLimitValue64(Core::System& system, int64_t* out_limit_value,
102 Handle resource_limit_handle, LimitableResource which) {
103 R_RETURN(GetResourceLimitLimitValue(system, out_limit_value, resource_limit_handle, which));
104}
105
106Result GetResourceLimitCurrentValue64(Core::System& system, int64_t* out_current_value,
107 Handle resource_limit_handle, LimitableResource which) {
108 R_RETURN(GetResourceLimitCurrentValue(system, out_current_value, resource_limit_handle, which));
109}
110
111Result GetResourceLimitPeakValue64(Core::System& system, int64_t* out_peak_value,
112 Handle resource_limit_handle, LimitableResource which) {
113 R_RETURN(GetResourceLimitPeakValue(system, out_peak_value, resource_limit_handle, which));
114}
115
116Result CreateResourceLimit64(Core::System& system, Handle* out_handle) {
117 R_RETURN(CreateResourceLimit(system, out_handle));
118}
119
120Result SetResourceLimitLimitValue64(Core::System& system, Handle resource_limit_handle,
121 LimitableResource which, int64_t limit_value) {
122 R_RETURN(SetResourceLimitLimitValue(system, resource_limit_handle, which, limit_value));
123}
124
125Result GetResourceLimitLimitValue64From32(Core::System& system, int64_t* out_limit_value,
126 Handle resource_limit_handle, LimitableResource which) {
127 R_RETURN(GetResourceLimitLimitValue(system, out_limit_value, resource_limit_handle, which));
128}
129
130Result GetResourceLimitCurrentValue64From32(Core::System& system, int64_t* out_current_value,
131 Handle resource_limit_handle, LimitableResource which) {
132 R_RETURN(GetResourceLimitCurrentValue(system, out_current_value, resource_limit_handle, which));
133}
134
135Result GetResourceLimitPeakValue64From32(Core::System& system, int64_t* out_peak_value,
136 Handle resource_limit_handle, LimitableResource which) {
137 R_RETURN(GetResourceLimitPeakValue(system, out_peak_value, resource_limit_handle, which));
138}
139
140Result CreateResourceLimit64From32(Core::System& system, Handle* out_handle) {
141 R_RETURN(CreateResourceLimit(system, out_handle));
142}
143
144Result SetResourceLimitLimitValue64From32(Core::System& system, Handle resource_limit_handle,
145 LimitableResource which, int64_t limit_value) {
146 R_RETURN(SetResourceLimitLimitValue(system, resource_limit_handle, which, limit_value));
147}
148
149} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp
new file mode 100644
index 000000000..20f6ec643
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_secure_monitor_call.cpp
@@ -0,0 +1,53 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/hle/kernel/physical_core.h"
6#include "core/hle/kernel/svc.h"
7
8namespace Kernel::Svc {
9
10void CallSecureMonitor(Core::System& system, lp64::SecureMonitorArguments* args) {
11 UNIMPLEMENTED();
12}
13
14void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args) {
15 CallSecureMonitor(system, args);
16}
17
18void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArguments* args) {
19 // CallSecureMonitor64From32 is not supported.
20 UNIMPLEMENTED_MSG("CallSecureMonitor64From32");
21}
22
23// Custom ABI for CallSecureMonitor.
24
25void SvcWrap_CallSecureMonitor64(Core::System& system) {
26 auto& core = system.CurrentPhysicalCore().ArmInterface();
27 lp64::SecureMonitorArguments args{};
28 for (int i = 0; i < 8; i++) {
29 args.r[i] = core.GetReg(i);
30 }
31
32 CallSecureMonitor64(system, &args);
33
34 for (int i = 0; i < 8; i++) {
35 core.SetReg(i, args.r[i]);
36 }
37}
38
39void SvcWrap_CallSecureMonitor64From32(Core::System& system) {
40 auto& core = system.CurrentPhysicalCore().ArmInterface();
41 ilp32::SecureMonitorArguments args{};
42 for (int i = 0; i < 8; i++) {
43 args.r[i] = static_cast<u32>(core.GetReg(i));
44 }
45
46 CallSecureMonitor64From32(system, &args);
47
48 for (int i = 0; i < 8; i++) {
49 core.SetReg(i, args.r[i]);
50 }
51}
52
53} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_session.cpp b/src/core/hle/kernel/svc/svc_session.cpp
new file mode 100644
index 000000000..0deb61b62
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_session.cpp
@@ -0,0 +1,128 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scoped_resource_reservation.h"
8#include "core/hle/kernel/k_session.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12namespace {
13
14template <typename T>
15Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, u64 name) {
16 auto& process = *system.CurrentProcess();
17 auto& handle_table = process.GetHandleTable();
18
19 // Declare the session we're going to allocate.
20 T* session;
21
22 // Reserve a new session from the process resource limit.
23 // FIXME: LimitableResource_SessionCountMax
24 KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax);
25 if (session_reservation.Succeeded()) {
26 session = T::Create(system.Kernel());
27 } else {
28 return ResultLimitReached;
29
30 // // We couldn't reserve a session. Check that we support dynamically expanding the
31 // // resource limit.
32 // R_UNLESS(process.GetResourceLimit() ==
33 // &system.Kernel().GetSystemResourceLimit(), ResultLimitReached);
34 // R_UNLESS(KTargetSystem::IsDynamicResourceLimitsEnabled(), ResultLimitReached());
35
36 // // Try to allocate a session from unused slab memory.
37 // session = T::CreateFromUnusedSlabMemory();
38 // R_UNLESS(session != nullptr, ResultLimitReached);
39 // ON_RESULT_FAILURE { session->Close(); };
40
41 // // If we're creating a KSession, we want to add two KSessionRequests to the heap, to
42 // // prevent request exhaustion.
43 // // NOTE: Nintendo checks if session->DynamicCast<KSession *>() != nullptr, but there's
44 // // no reason to not do this statically.
45 // if constexpr (std::same_as<T, KSession>) {
46 // for (size_t i = 0; i < 2; i++) {
47 // KSessionRequest* request = KSessionRequest::CreateFromUnusedSlabMemory();
48 // R_UNLESS(request != nullptr, ResultLimitReached);
49 // request->Close();
50 // }
51 // }
52
53 // We successfully allocated a session, so add the object we allocated to the resource
54 // limit.
55 // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1);
56 }
57
58 // Check that we successfully created a session.
59 R_UNLESS(session != nullptr, ResultOutOfResource);
60
61 // Initialize the session.
62 session->Initialize(nullptr, fmt::format("{}", name));
63
64 // Commit the session reservation.
65 session_reservation.Commit();
66
67 // Ensure that we clean up the session (and its only references are handle table) on function
68 // end.
69 SCOPE_EXIT({
70 session->GetClientSession().Close();
71 session->GetServerSession().Close();
72 });
73
74 // Register the session.
75 T::Register(system.Kernel(), session);
76
77 // Add the server session to the handle table.
78 R_TRY(handle_table.Add(out_server, &session->GetServerSession()));
79
80 // Add the client session to the handle table.
81 const auto result = handle_table.Add(out_client, &session->GetClientSession());
82
83 if (!R_SUCCEEDED(result)) {
84 // Ensure that we maintaing a clean handle state on exit.
85 handle_table.Remove(*out_server);
86 }
87
88 return result;
89}
90
91} // namespace
92
93Result CreateSession(Core::System& system, Handle* out_server, Handle* out_client, bool is_light,
94 u64 name) {
95 if (is_light) {
96 // return CreateSession<KLightSession>(system, out_server, out_client, name);
97 return ResultNotImplemented;
98 } else {
99 return CreateSession<KSession>(system, out_server, out_client, name);
100 }
101}
102
103Result AcceptSession(Core::System& system, Handle* out_handle, Handle port_handle) {
104 UNIMPLEMENTED();
105 R_THROW(ResultNotImplemented);
106}
107
108Result CreateSession64(Core::System& system, Handle* out_server_session_handle,
109 Handle* out_client_session_handle, bool is_light, uint64_t name) {
110 R_RETURN(CreateSession(system, out_server_session_handle, out_client_session_handle, is_light,
111 name));
112}
113
114Result AcceptSession64(Core::System& system, Handle* out_handle, Handle port) {
115 R_RETURN(AcceptSession(system, out_handle, port));
116}
117
118Result CreateSession64From32(Core::System& system, Handle* out_server_session_handle,
119 Handle* out_client_session_handle, bool is_light, uint32_t name) {
120 R_RETURN(CreateSession(system, out_server_session_handle, out_client_session_handle, is_light,
121 name));
122}
123
124Result AcceptSession64From32(Core::System& system, Handle* out_handle, Handle port) {
125 R_RETURN(AcceptSession(system, out_handle, port));
126}
127
128} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_shared_memory.cpp b/src/core/hle/kernel/svc/svc_shared_memory.cpp
new file mode 100644
index 000000000..40d6260e3
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp
@@ -0,0 +1,133 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_shared_memory.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11namespace {
12
13constexpr bool IsValidSharedMemoryPermission(MemoryPermission perm) {
14 switch (perm) {
15 case MemoryPermission::Read:
16 case MemoryPermission::ReadWrite:
17 return true;
18 default:
19 return false;
20 }
21}
22
23[[maybe_unused]] constexpr bool IsValidRemoteSharedMemoryPermission(MemoryPermission perm) {
24 return IsValidSharedMemoryPermission(perm) || perm == MemoryPermission::DontCare;
25}
26
27} // namespace
28
29Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
30 Svc::MemoryPermission map_perm) {
31 LOG_TRACE(Kernel_SVC,
32 "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
33 shmem_handle, address, size, map_perm);
34
35 // Validate the address/size.
36 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
37 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
38 R_UNLESS(size > 0, ResultInvalidSize);
39 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
40
41 // Validate the permission.
42 R_UNLESS(IsValidSharedMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
43
44 // Get the current process.
45 auto& process = *system.Kernel().CurrentProcess();
46 auto& page_table = process.PageTable();
47
48 // Get the shared memory.
49 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
50 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
51
52 // Verify that the mapping is in range.
53 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
54
55 // Add the shared memory to the process.
56 R_TRY(process.AddSharedMemory(shmem.GetPointerUnsafe(), address, size));
57
58 // Ensure that we clean up the shared memory if we fail to map it.
59 auto guard =
60 SCOPE_GUARD({ process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size); });
61
62 // Map the shared memory.
63 R_TRY(shmem->Map(process, address, size, map_perm));
64
65 // We succeeded.
66 guard.Cancel();
67 return ResultSuccess;
68}
69
70Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size) {
71 // Validate the address/size.
72 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
73 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
74 R_UNLESS(size > 0, ResultInvalidSize);
75 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
76
77 // Get the current process.
78 auto& process = *system.Kernel().CurrentProcess();
79 auto& page_table = process.PageTable();
80
81 // Get the shared memory.
82 KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle);
83 R_UNLESS(shmem.IsNotNull(), ResultInvalidHandle);
84
85 // Verify that the mapping is in range.
86 R_UNLESS(page_table.CanContain(address, size, KMemoryState::Shared), ResultInvalidMemoryRegion);
87
88 // Unmap the shared memory.
89 R_TRY(shmem->Unmap(process, address, size));
90
91 // Remove the shared memory from the process.
92 process.RemoveSharedMemory(shmem.GetPointerUnsafe(), address, size);
93
94 return ResultSuccess;
95}
96
97Result CreateSharedMemory(Core::System& system, Handle* out_handle, uint64_t size,
98 MemoryPermission owner_perm, MemoryPermission remote_perm) {
99 UNIMPLEMENTED();
100 R_THROW(ResultNotImplemented);
101}
102
103Result MapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address, uint64_t size,
104 MemoryPermission map_perm) {
105 R_RETURN(MapSharedMemory(system, shmem_handle, address, size, map_perm));
106}
107
108Result UnmapSharedMemory64(Core::System& system, Handle shmem_handle, uint64_t address,
109 uint64_t size) {
110 R_RETURN(UnmapSharedMemory(system, shmem_handle, address, size));
111}
112
113Result CreateSharedMemory64(Core::System& system, Handle* out_handle, uint64_t size,
114 MemoryPermission owner_perm, MemoryPermission remote_perm) {
115 R_RETURN(CreateSharedMemory(system, out_handle, size, owner_perm, remote_perm));
116}
117
118Result MapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address,
119 uint32_t size, MemoryPermission map_perm) {
120 R_RETURN(MapSharedMemory(system, shmem_handle, address, size, map_perm));
121}
122
123Result UnmapSharedMemory64From32(Core::System& system, Handle shmem_handle, uint32_t address,
124 uint32_t size) {
125 R_RETURN(UnmapSharedMemory(system, shmem_handle, address, size));
126}
127
128Result CreateSharedMemory64From32(Core::System& system, Handle* out_handle, uint32_t size,
129 MemoryPermission owner_perm, MemoryPermission remote_perm) {
130 R_RETURN(CreateSharedMemory(system, out_handle, size, owner_perm, remote_perm));
131}
132
133} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_synchronization.cpp b/src/core/hle/kernel/svc/svc_synchronization.cpp
new file mode 100644
index 000000000..e516a3800
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_synchronization.cpp
@@ -0,0 +1,163 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_readable_event.h"
8#include "core/hle/kernel/svc.h"
9
10namespace Kernel::Svc {
11
12/// Close a handle
13Result CloseHandle(Core::System& system, Handle handle) {
14 LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
15
16 // Remove the handle.
17 R_UNLESS(system.Kernel().CurrentProcess()->GetHandleTable().Remove(handle),
18 ResultInvalidHandle);
19
20 return ResultSuccess;
21}
22
23/// Clears the signaled state of an event or process.
24Result ResetSignal(Core::System& system, Handle handle) {
25 LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
26
27 // Get the current handle table.
28 const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
29
30 // Try to reset as readable event.
31 {
32 KScopedAutoObject readable_event = handle_table.GetObject<KReadableEvent>(handle);
33 if (readable_event.IsNotNull()) {
34 return readable_event->Reset();
35 }
36 }
37
38 // Try to reset as process.
39 {
40 KScopedAutoObject process = handle_table.GetObject<KProcess>(handle);
41 if (process.IsNotNull()) {
42 return process->Reset();
43 }
44 }
45
46 LOG_ERROR(Kernel_SVC, "invalid handle (0x{:08X})", handle);
47
48 return ResultInvalidHandle;
49}
50
51/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
52Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, s32 num_handles,
53 s64 nano_seconds) {
54 LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
55 handles_address, num_handles, nano_seconds);
56
57 // Ensure number of handles is valid.
58 R_UNLESS(0 <= num_handles && num_handles <= ArgumentHandleCountMax, ResultOutOfRange);
59
60 auto& kernel = system.Kernel();
61 std::vector<KSynchronizationObject*> objs(num_handles);
62 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
63 Handle* handles = system.Memory().GetPointer<Handle>(handles_address);
64
65 // Copy user handles.
66 if (num_handles > 0) {
67 // Convert the handles to objects.
68 R_UNLESS(handle_table.GetMultipleObjects<KSynchronizationObject>(objs.data(), handles,
69 num_handles),
70 ResultInvalidHandle);
71 for (const auto& obj : objs) {
72 kernel.RegisterInUseObject(obj);
73 }
74 }
75
76 // Ensure handles are closed when we're done.
77 SCOPE_EXIT({
78 for (s32 i = 0; i < num_handles; ++i) {
79 kernel.UnregisterInUseObject(objs[i]);
80 objs[i]->Close();
81 }
82 });
83
84 return KSynchronizationObject::Wait(kernel, index, objs.data(), static_cast<s32>(objs.size()),
85 nano_seconds);
86}
87
88/// Resumes a thread waiting on WaitSynchronization
89Result CancelSynchronization(Core::System& system, Handle handle) {
90 LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
91
92 // Get the thread from its handle.
93 KScopedAutoObject thread =
94 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
95 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
96
97 // Cancel the thread's wait.
98 thread->WaitCancel();
99 return ResultSuccess;
100}
101
102void SynchronizePreemptionState(Core::System& system) {
103 auto& kernel = system.Kernel();
104
105 // Lock the scheduler.
106 KScopedSchedulerLock sl{kernel};
107
108 // If the current thread is pinned, unpin it.
109 KProcess* cur_process = system.Kernel().CurrentProcess();
110 const auto core_id = GetCurrentCoreId(kernel);
111
112 if (cur_process->GetPinnedThread(core_id) == GetCurrentThreadPointer(kernel)) {
113 // Clear the current thread's interrupt flag.
114 GetCurrentThread(kernel).ClearInterruptFlag();
115
116 // Unpin the current thread.
117 cur_process->UnpinCurrentThread(core_id);
118 }
119}
120
121Result CloseHandle64(Core::System& system, Handle handle) {
122 R_RETURN(CloseHandle(system, handle));
123}
124
125Result ResetSignal64(Core::System& system, Handle handle) {
126 R_RETURN(ResetSignal(system, handle));
127}
128
129Result WaitSynchronization64(Core::System& system, int32_t* out_index, uint64_t handles,
130 int32_t num_handles, int64_t timeout_ns) {
131 R_RETURN(WaitSynchronization(system, out_index, handles, num_handles, timeout_ns));
132}
133
134Result CancelSynchronization64(Core::System& system, Handle handle) {
135 R_RETURN(CancelSynchronization(system, handle));
136}
137
138void SynchronizePreemptionState64(Core::System& system) {
139 SynchronizePreemptionState(system);
140}
141
142Result CloseHandle64From32(Core::System& system, Handle handle) {
143 R_RETURN(CloseHandle(system, handle));
144}
145
146Result ResetSignal64From32(Core::System& system, Handle handle) {
147 R_RETURN(ResetSignal(system, handle));
148}
149
150Result WaitSynchronization64From32(Core::System& system, int32_t* out_index, uint32_t handles,
151 int32_t num_handles, int64_t timeout_ns) {
152 R_RETURN(WaitSynchronization(system, out_index, handles, num_handles, timeout_ns));
153}
154
155Result CancelSynchronization64From32(Core::System& system, Handle handle) {
156 R_RETURN(CancelSynchronization(system, handle));
157}
158
159void SynchronizePreemptionState64From32(Core::System& system) {
160 SynchronizePreemptionState(system);
161}
162
163} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp
new file mode 100644
index 000000000..3e325c998
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_thread.cpp
@@ -0,0 +1,437 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/core_timing.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_scoped_resource_reservation.h"
9#include "core/hle/kernel/k_thread.h"
10#include "core/hle/kernel/svc.h"
11
12namespace Kernel::Svc {
13namespace {
14
15constexpr bool IsValidVirtualCoreId(int32_t core_id) {
16 return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES));
17}
18
19} // Anonymous namespace
20
21/// Creates a new thread
22Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
23 VAddr stack_bottom, s32 priority, s32 core_id) {
24 LOG_DEBUG(Kernel_SVC,
25 "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
26 "priority=0x{:08X}, core_id=0x{:08X}",
27 entry_point, arg, stack_bottom, priority, core_id);
28
29 // Adjust core id, if it's the default magic.
30 auto& kernel = system.Kernel();
31 auto& process = *kernel.CurrentProcess();
32 if (core_id == IdealCoreUseProcessValue) {
33 core_id = process.GetIdealCoreId();
34 }
35
36 // Validate arguments.
37 if (!IsValidVirtualCoreId(core_id)) {
38 LOG_ERROR(Kernel_SVC, "Invalid Core ID specified (id={})", core_id);
39 return ResultInvalidCoreId;
40 }
41 if (((1ULL << core_id) & process.GetCoreMask()) == 0) {
42 LOG_ERROR(Kernel_SVC, "Core ID doesn't fall within allowable cores (id={})", core_id);
43 return ResultInvalidCoreId;
44 }
45
46 if (HighestThreadPriority > priority || priority > LowestThreadPriority) {
47 LOG_ERROR(Kernel_SVC, "Invalid priority specified (priority={})", priority);
48 return ResultInvalidPriority;
49 }
50 if (!process.CheckThreadPriority(priority)) {
51 LOG_ERROR(Kernel_SVC, "Invalid allowable thread priority (priority={})", priority);
52 return ResultInvalidPriority;
53 }
54
55 // Reserve a new thread from the process resource limit (waiting up to 100ms).
56 KScopedResourceReservation thread_reservation(
57 kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1,
58 system.CoreTiming().GetGlobalTimeNs().count() + 100000000);
59 if (!thread_reservation.Succeeded()) {
60 LOG_ERROR(Kernel_SVC, "Could not reserve a new thread");
61 return ResultLimitReached;
62 }
63
64 // Create the thread.
65 KThread* thread = KThread::Create(kernel);
66 if (!thread) {
67 LOG_ERROR(Kernel_SVC, "Unable to create new threads. Thread creation limit reached.");
68 return ResultOutOfResource;
69 }
70 SCOPE_EXIT({ thread->Close(); });
71
72 // Initialize the thread.
73 {
74 KScopedLightLock lk{process.GetStateLock()};
75 R_TRY(KThread::InitializeUserThread(system, thread, entry_point, arg, stack_bottom,
76 priority, core_id, &process));
77 }
78
79 // Set the thread name for debugging purposes.
80 thread->SetName(fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *out_handle));
81
82 // Commit the thread reservation.
83 thread_reservation.Commit();
84
85 // Register the new thread.
86 KThread::Register(kernel, thread);
87
88 // Add the thread to the handle table.
89 R_TRY(process.GetHandleTable().Add(out_handle, thread));
90
91 return ResultSuccess;
92}
93
94/// Starts the thread for the provided handle
95Result StartThread(Core::System& system, Handle thread_handle) {
96 LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
97
98 // Get the thread from its handle.
99 KScopedAutoObject thread =
100 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
101 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
102
103 // Try to start the thread.
104 R_TRY(thread->Run());
105
106 // If we succeeded, persist a reference to the thread.
107 thread->Open();
108 system.Kernel().RegisterInUseObject(thread.GetPointerUnsafe());
109
110 return ResultSuccess;
111}
112
113/// Called when a thread exits
114void ExitThread(Core::System& system) {
115 LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
116
117 auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
118 system.GlobalSchedulerContext().RemoveThread(current_thread);
119 current_thread->Exit();
120 system.Kernel().UnregisterInUseObject(current_thread);
121}
122
123/// Sleep the current thread
124void SleepThread(Core::System& system, s64 nanoseconds) {
125 auto& kernel = system.Kernel();
126 const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
127
128 LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
129
130 // When the input tick is positive, sleep.
131 if (nanoseconds > 0) {
132 // Convert the timeout from nanoseconds to ticks.
133 // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
134
135 // Sleep.
136 // NOTE: Nintendo does not check the result of this sleep.
137 static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
138 } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
139 KScheduler::YieldWithoutCoreMigration(kernel);
140 } else if (yield_type == Svc::YieldType::WithCoreMigration) {
141 KScheduler::YieldWithCoreMigration(kernel);
142 } else if (yield_type == Svc::YieldType::ToAnyThread) {
143 KScheduler::YieldToAnyThread(kernel);
144 } else {
145 // Nintendo does nothing at all if an otherwise invalid value is passed.
146 ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
147 }
148}
149
150/// Gets the thread context
151Result GetThreadContext3(Core::System& system, VAddr out_context, Handle thread_handle) {
152 LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
153 thread_handle);
154
155 auto& kernel = system.Kernel();
156
157 // Get the thread from its handle.
158 KScopedAutoObject thread =
159 kernel.CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
160 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
161
162 // Require the handle be to a non-current thread in the current process.
163 const auto* current_process = kernel.CurrentProcess();
164 R_UNLESS(current_process == thread->GetOwnerProcess(), ResultInvalidId);
165
166 // Verify that the thread isn't terminated.
167 R_UNLESS(thread->GetState() != ThreadState::Terminated, ResultTerminationRequested);
168
169 /// Check that the thread is not the current one.
170 /// NOTE: Nintendo does not check this, and thus the following loop will deadlock.
171 R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(kernel), ResultInvalidId);
172
173 // Try to get the thread context until the thread isn't current on any core.
174 while (true) {
175 KScopedSchedulerLock sl{kernel};
176
177 // TODO(bunnei): Enforce that thread is suspended for debug here.
178
179 // If the thread's raw state isn't runnable, check if it's current on some core.
180 if (thread->GetRawState() != ThreadState::Runnable) {
181 bool current = false;
182 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
183 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
184 current = true;
185 break;
186 }
187 }
188
189 // If the thread is current, retry until it isn't.
190 if (current) {
191 continue;
192 }
193 }
194
195 // Get the thread context.
196 std::vector<u8> context;
197 R_TRY(thread->GetThreadContext3(context));
198
199 // Copy the thread context to user space.
200 system.Memory().WriteBlock(out_context, context.data(), context.size());
201
202 return ResultSuccess;
203 }
204
205 return ResultSuccess;
206}
207
208/// Gets the priority for the specified thread
209Result GetThreadPriority(Core::System& system, s32* out_priority, Handle handle) {
210 LOG_TRACE(Kernel_SVC, "called");
211
212 // Get the thread from its handle.
213 KScopedAutoObject thread =
214 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(handle);
215 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
216
217 // Get the thread's priority.
218 *out_priority = thread->GetPriority();
219 return ResultSuccess;
220}
221
222/// Sets the priority for the specified thread
223Result SetThreadPriority(Core::System& system, Handle thread_handle, s32 priority) {
224 // Get the current process.
225 KProcess& process = *system.Kernel().CurrentProcess();
226
227 // Validate the priority.
228 R_UNLESS(HighestThreadPriority <= priority && priority <= LowestThreadPriority,
229 ResultInvalidPriority);
230 R_UNLESS(process.CheckThreadPriority(priority), ResultInvalidPriority);
231
232 // Get the thread from its handle.
233 KScopedAutoObject thread = process.GetHandleTable().GetObject<KThread>(thread_handle);
234 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
235
236 // Set the thread priority.
237 thread->SetBasePriority(priority);
238 return ResultSuccess;
239}
240
241Result GetThreadList(Core::System& system, s32* out_num_threads, VAddr out_thread_ids,
242 s32 out_thread_ids_size, Handle debug_handle) {
243 // TODO: Handle this case when debug events are supported.
244 UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
245
246 LOG_DEBUG(Kernel_SVC, "called. out_thread_ids=0x{:016X}, out_thread_ids_size={}",
247 out_thread_ids, out_thread_ids_size);
248
249 // If the size is negative or larger than INT32_MAX / sizeof(u64)
250 if ((out_thread_ids_size & 0xF0000000) != 0) {
251 LOG_ERROR(Kernel_SVC, "Supplied size outside [0, 0x0FFFFFFF] range. size={}",
252 out_thread_ids_size);
253 return ResultOutOfRange;
254 }
255
256 auto* const current_process = system.Kernel().CurrentProcess();
257 const auto total_copy_size = out_thread_ids_size * sizeof(u64);
258
259 if (out_thread_ids_size > 0 &&
260 !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {
261 LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",
262 out_thread_ids, out_thread_ids + total_copy_size);
263 return ResultInvalidCurrentMemory;
264 }
265
266 auto& memory = system.Memory();
267 const auto& thread_list = current_process->GetThreadList();
268 const auto num_threads = thread_list.size();
269 const auto copy_amount = std::min(static_cast<std::size_t>(out_thread_ids_size), num_threads);
270
271 auto list_iter = thread_list.cbegin();
272 for (std::size_t i = 0; i < copy_amount; ++i, ++list_iter) {
273 memory.Write64(out_thread_ids, (*list_iter)->GetThreadID());
274 out_thread_ids += sizeof(u64);
275 }
276
277 *out_num_threads = static_cast<u32>(num_threads);
278 return ResultSuccess;
279}
280
281Result GetThreadCoreMask(Core::System& system, s32* out_core_id, u64* out_affinity_mask,
282 Handle thread_handle) {
283 LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
284
285 // Get the thread from its handle.
286 KScopedAutoObject thread =
287 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
288 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
289
290 // Get the core mask.
291 R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
292
293 return ResultSuccess;
294}
295
296Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
297 u64 affinity_mask) {
298 // Determine the core id/affinity mask.
299 if (core_id == IdealCoreUseProcessValue) {
300 core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
301 affinity_mask = (1ULL << core_id);
302 } else {
303 // Validate the affinity mask.
304 const u64 process_core_mask = system.Kernel().CurrentProcess()->GetCoreMask();
305 R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, ResultInvalidCoreId);
306 R_UNLESS(affinity_mask != 0, ResultInvalidCombination);
307
308 // Validate the core id.
309 if (IsValidVirtualCoreId(core_id)) {
310 R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, ResultInvalidCombination);
311 } else {
312 R_UNLESS(core_id == IdealCoreNoUpdate || core_id == IdealCoreDontCare,
313 ResultInvalidCoreId);
314 }
315 }
316
317 // Get the thread from its handle.
318 KScopedAutoObject thread =
319 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
320 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
321
322 // Set the core mask.
323 R_TRY(thread->SetCoreMask(core_id, affinity_mask));
324
325 return ResultSuccess;
326}
327
328/// Get the ID for the specified thread.
329Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
330 // Get the thread from its handle.
331 KScopedAutoObject thread =
332 system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
333 R_UNLESS(thread.IsNotNull(), ResultInvalidHandle);
334
335 // Get the thread's id.
336 *out_thread_id = thread->GetId();
337 return ResultSuccess;
338}
339
340Result CreateThread64(Core::System& system, Handle* out_handle, uint64_t func, uint64_t arg,
341 uint64_t stack_bottom, int32_t priority, int32_t core_id) {
342 R_RETURN(CreateThread(system, out_handle, func, arg, stack_bottom, priority, core_id));
343}
344
345Result StartThread64(Core::System& system, Handle thread_handle) {
346 R_RETURN(StartThread(system, thread_handle));
347}
348
349void ExitThread64(Core::System& system) {
350 return ExitThread(system);
351}
352
353void SleepThread64(Core::System& system, int64_t ns) {
354 return SleepThread(system, ns);
355}
356
357Result GetThreadPriority64(Core::System& system, int32_t* out_priority, Handle thread_handle) {
358 R_RETURN(GetThreadPriority(system, out_priority, thread_handle));
359}
360
361Result SetThreadPriority64(Core::System& system, Handle thread_handle, int32_t priority) {
362 R_RETURN(SetThreadPriority(system, thread_handle, priority));
363}
364
365Result GetThreadCoreMask64(Core::System& system, int32_t* out_core_id, uint64_t* out_affinity_mask,
366 Handle thread_handle) {
367 R_RETURN(GetThreadCoreMask(system, out_core_id, out_affinity_mask, thread_handle));
368}
369
370Result SetThreadCoreMask64(Core::System& system, Handle thread_handle, int32_t core_id,
371 uint64_t affinity_mask) {
372 R_RETURN(SetThreadCoreMask(system, thread_handle, core_id, affinity_mask));
373}
374
375Result GetThreadId64(Core::System& system, uint64_t* out_thread_id, Handle thread_handle) {
376 R_RETURN(GetThreadId(system, out_thread_id, thread_handle));
377}
378
379Result GetThreadContext364(Core::System& system, uint64_t out_context, Handle thread_handle) {
380 R_RETURN(GetThreadContext3(system, out_context, thread_handle));
381}
382
383Result GetThreadList64(Core::System& system, int32_t* out_num_threads, uint64_t out_thread_ids,
384 int32_t max_out_count, Handle debug_handle) {
385 R_RETURN(GetThreadList(system, out_num_threads, out_thread_ids, max_out_count, debug_handle));
386}
387
388Result CreateThread64From32(Core::System& system, Handle* out_handle, uint32_t func, uint32_t arg,
389 uint32_t stack_bottom, int32_t priority, int32_t core_id) {
390 R_RETURN(CreateThread(system, out_handle, func, arg, stack_bottom, priority, core_id));
391}
392
393Result StartThread64From32(Core::System& system, Handle thread_handle) {
394 R_RETURN(StartThread(system, thread_handle));
395}
396
397void ExitThread64From32(Core::System& system) {
398 return ExitThread(system);
399}
400
401void SleepThread64From32(Core::System& system, int64_t ns) {
402 return SleepThread(system, ns);
403}
404
405Result GetThreadPriority64From32(Core::System& system, int32_t* out_priority,
406 Handle thread_handle) {
407 R_RETURN(GetThreadPriority(system, out_priority, thread_handle));
408}
409
410Result SetThreadPriority64From32(Core::System& system, Handle thread_handle, int32_t priority) {
411 R_RETURN(SetThreadPriority(system, thread_handle, priority));
412}
413
414Result GetThreadCoreMask64From32(Core::System& system, int32_t* out_core_id,
415 uint64_t* out_affinity_mask, Handle thread_handle) {
416 R_RETURN(GetThreadCoreMask(system, out_core_id, out_affinity_mask, thread_handle));
417}
418
419Result SetThreadCoreMask64From32(Core::System& system, Handle thread_handle, int32_t core_id,
420 uint64_t affinity_mask) {
421 R_RETURN(SetThreadCoreMask(system, thread_handle, core_id, affinity_mask));
422}
423
424Result GetThreadId64From32(Core::System& system, uint64_t* out_thread_id, Handle thread_handle) {
425 R_RETURN(GetThreadId(system, out_thread_id, thread_handle));
426}
427
428Result GetThreadContext364From32(Core::System& system, uint32_t out_context, Handle thread_handle) {
429 R_RETURN(GetThreadContext3(system, out_context, thread_handle));
430}
431
432Result GetThreadList64From32(Core::System& system, int32_t* out_num_threads,
433 uint32_t out_thread_ids, int32_t max_out_count, Handle debug_handle) {
434 R_RETURN(GetThreadList(system, out_num_threads, out_thread_ids, max_out_count, debug_handle));
435}
436
437} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_thread_profiler.cpp b/src/core/hle/kernel/svc/svc_thread_profiler.cpp
new file mode 100644
index 000000000..40de7708b
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_thread_profiler.cpp
@@ -0,0 +1,60 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/kernel/svc.h"
5#include "core/hle/kernel/svc_results.h"
6
7namespace Kernel::Svc {
8
9Result GetDebugFutureThreadInfo(Core::System& system, lp64::LastThreadContext* out_context,
10 uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
11 UNIMPLEMENTED();
12 R_THROW(ResultNotImplemented);
13}
14
15Result GetLastThreadInfo(Core::System& system, lp64::LastThreadContext* out_context,
16 uint64_t* out_tls_address, uint32_t* out_flags) {
17 UNIMPLEMENTED();
18 R_THROW(ResultNotImplemented);
19}
20
21Result GetDebugFutureThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context,
22 uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
23 R_RETURN(GetDebugFutureThreadInfo(system, out_context, out_thread_id, debug_handle, ns));
24}
25
26Result GetLastThreadInfo64(Core::System& system, lp64::LastThreadContext* out_context,
27 uint64_t* out_tls_address, uint32_t* out_flags) {
28 R_RETURN(GetLastThreadInfo(system, out_context, out_tls_address, out_flags));
29}
30
31Result GetDebugFutureThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context,
32 uint64_t* out_thread_id, Handle debug_handle, int64_t ns) {
33 lp64::LastThreadContext context{};
34 R_TRY(
35 GetDebugFutureThreadInfo(system, std::addressof(context), out_thread_id, debug_handle, ns));
36
37 *out_context = {
38 .fp = static_cast<u32>(context.fp),
39 .sp = static_cast<u32>(context.sp),
40 .lr = static_cast<u32>(context.lr),
41 .pc = static_cast<u32>(context.pc),
42 };
43 R_SUCCEED();
44}
45
46Result GetLastThreadInfo64From32(Core::System& system, ilp32::LastThreadContext* out_context,
47 uint64_t* out_tls_address, uint32_t* out_flags) {
48 lp64::LastThreadContext context{};
49 R_TRY(GetLastThreadInfo(system, std::addressof(context), out_tls_address, out_flags));
50
51 *out_context = {
52 .fp = static_cast<u32>(context.fp),
53 .sp = static_cast<u32>(context.sp),
54 .lr = static_cast<u32>(context.lr),
55 .pc = static_cast<u32>(context.pc),
56 };
57 R_SUCCEED();
58}
59
60} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_tick.cpp b/src/core/hle/kernel/svc/svc_tick.cpp
new file mode 100644
index 000000000..561336482
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_tick.cpp
@@ -0,0 +1,35 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/core.h"
5#include "core/core_timing.h"
6#include "core/hle/kernel/kernel.h"
7#include "core/hle/kernel/svc.h"
8
9namespace Kernel::Svc {
10
11/// This returns the total CPU ticks elapsed since the CPU was powered-on
12int64_t GetSystemTick(Core::System& system) {
13 LOG_TRACE(Kernel_SVC, "called");
14
15 auto& core_timing = system.CoreTiming();
16
17 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
18 const u64 result{core_timing.GetClockTicks()};
19
20 if (!system.Kernel().IsMulticore()) {
21 core_timing.AddTicks(400U);
22 }
23
24 return static_cast<int64_t>(result);
25}
26
27int64_t GetSystemTick64(Core::System& system) {
28 return GetSystemTick(system);
29}
30
31int64_t GetSystemTick64From32(Core::System& system) {
32 return GetSystemTick(system);
33}
34
35} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
new file mode 100644
index 000000000..a4c040e49
--- /dev/null
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -0,0 +1,117 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/scope_exit.h"
5#include "core/core.h"
6#include "core/hle/kernel/k_process.h"
7#include "core/hle/kernel/k_scoped_resource_reservation.h"
8#include "core/hle/kernel/k_transfer_memory.h"
9#include "core/hle/kernel/svc.h"
10
11namespace Kernel::Svc {
12namespace {
13
14constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
15 switch (perm) {
16 case MemoryPermission::None:
17 case MemoryPermission::Read:
18 case MemoryPermission::ReadWrite:
19 return true;
20 default:
21 return false;
22 }
23}
24
25} // Anonymous namespace
26
27/// Creates a TransferMemory object
28Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
29 MemoryPermission map_perm) {
30 auto& kernel = system.Kernel();
31
32 // Validate the size.
33 R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
34 R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
35 R_UNLESS(size > 0, ResultInvalidSize);
36 R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
37
38 // Validate the permissions.
39 R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidNewMemoryPermission);
40
41 // Get the current process and handle table.
42 auto& process = *kernel.CurrentProcess();
43 auto& handle_table = process.GetHandleTable();
44
45 // Reserve a new transfer memory from the process resource limit.
46 KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(),
47 LimitableResource::TransferMemoryCountMax);
48 R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached);
49
50 // Create the transfer memory.
51 KTransferMemory* trmem = KTransferMemory::Create(kernel);
52 R_UNLESS(trmem != nullptr, ResultOutOfResource);
53
54 // Ensure the only reference is in the handle table when we're done.
55 SCOPE_EXIT({ trmem->Close(); });
56
57 // Ensure that the region is in range.
58 R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory);
59
60 // Initialize the transfer memory.
61 R_TRY(trmem->Initialize(address, size, map_perm));
62
63 // Commit the reservation.
64 trmem_reservation.Commit();
65
66 // Register the transfer memory.
67 KTransferMemory::Register(kernel, trmem);
68
69 // Add the transfer memory to the handle table.
70 R_TRY(handle_table.Add(out, trmem));
71
72 return ResultSuccess;
73}
74
75Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size,
76 MemoryPermission owner_perm) {
77 UNIMPLEMENTED();
78 R_THROW(ResultNotImplemented);
79}
80
81Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address,
82 uint64_t size) {
83 UNIMPLEMENTED();
84 R_THROW(ResultNotImplemented);
85}
86
87Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
88 uint64_t size, MemoryPermission owner_perm) {
89 R_RETURN(MapTransferMemory(system, trmem_handle, address, size, owner_perm));
90}
91
92Result UnmapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
93 uint64_t size) {
94 R_RETURN(UnmapTransferMemory(system, trmem_handle, address, size));
95}
96
97Result CreateTransferMemory64(Core::System& system, Handle* out_handle, uint64_t address,
98 uint64_t size, MemoryPermission map_perm) {
99 R_RETURN(CreateTransferMemory(system, out_handle, address, size, map_perm));
100}
101
102Result MapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address,
103 uint32_t size, MemoryPermission owner_perm) {
104 R_RETURN(MapTransferMemory(system, trmem_handle, address, size, owner_perm));
105}
106
107Result UnmapTransferMemory64From32(Core::System& system, Handle trmem_handle, uint32_t address,
108 uint32_t size) {
109 R_RETURN(UnmapTransferMemory(system, trmem_handle, address, size));
110}
111
112Result CreateTransferMemory64From32(Core::System& system, Handle* out_handle, uint32_t address,
113 uint32_t size, MemoryPermission map_perm) {
114 R_RETURN(CreateTransferMemory(system, out_handle, address, size, map_perm));
115}
116
117} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_generator.py b/src/core/hle/kernel/svc_generator.py
new file mode 100644
index 000000000..b0a5707ec
--- /dev/null
+++ b/src/core/hle/kernel/svc_generator.py
@@ -0,0 +1,716 @@
1# SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2# SPDX-License-Identifier: GPL-2.0-or-later
3
4# Raw SVC definitions from the kernel.
5#
6# Avoid modifying the prototypes; see below for how to customize generation
7# for a given typename.
8SVCS = [
9 [0x01, "Result SetHeapSize(Address* out_address, Size size);"],
10 [0x02, "Result SetMemoryPermission(Address address, Size size, MemoryPermission perm);"],
11 [0x03, "Result SetMemoryAttribute(Address address, Size size, uint32_t mask, uint32_t attr);"],
12 [0x04, "Result MapMemory(Address dst_address, Address src_address, Size size);"],
13 [0x05, "Result UnmapMemory(Address dst_address, Address src_address, Size size);"],
14 [0x06, "Result QueryMemory(Address out_memory_info, PageInfo* out_page_info, Address address);"],
15 [0x07, "void ExitProcess();"],
16 [0x08, "Result CreateThread(Handle* out_handle, ThreadFunc func, Address arg, Address stack_bottom, int32_t priority, int32_t core_id);"],
17 [0x09, "Result StartThread(Handle thread_handle);"],
18 [0x0A, "void ExitThread();"],
19 [0x0B, "void SleepThread(int64_t ns);"],
20 [0x0C, "Result GetThreadPriority(int32_t* out_priority, Handle thread_handle);"],
21 [0x0D, "Result SetThreadPriority(Handle thread_handle, int32_t priority);"],
22 [0x0E, "Result GetThreadCoreMask(int32_t* out_core_id, uint64_t* out_affinity_mask, Handle thread_handle);"],
23 [0x0F, "Result SetThreadCoreMask(Handle thread_handle, int32_t core_id, uint64_t affinity_mask);"],
24 [0x10, "int32_t GetCurrentProcessorNumber();"],
25 [0x11, "Result SignalEvent(Handle event_handle);"],
26 [0x12, "Result ClearEvent(Handle event_handle);"],
27 [0x13, "Result MapSharedMemory(Handle shmem_handle, Address address, Size size, MemoryPermission map_perm);"],
28 [0x14, "Result UnmapSharedMemory(Handle shmem_handle, Address address, Size size);"],
29 [0x15, "Result CreateTransferMemory(Handle* out_handle, Address address, Size size, MemoryPermission map_perm);"],
30 [0x16, "Result CloseHandle(Handle handle);"],
31 [0x17, "Result ResetSignal(Handle handle);"],
32 [0x18, "Result WaitSynchronization(int32_t* out_index, Address handles, int32_t num_handles, int64_t timeout_ns);"],
33 [0x19, "Result CancelSynchronization(Handle handle);"],
34 [0x1A, "Result ArbitrateLock(Handle thread_handle, Address address, uint32_t tag);"],
35 [0x1B, "Result ArbitrateUnlock(Address address);"],
36 [0x1C, "Result WaitProcessWideKeyAtomic(Address address, Address cv_key, uint32_t tag, int64_t timeout_ns);"],
37 [0x1D, "void SignalProcessWideKey(Address cv_key, int32_t count);"],
38 [0x1E, "int64_t GetSystemTick();"],
39 [0x1F, "Result ConnectToNamedPort(Handle* out_handle, Address name);"],
40 [0x20, "Result SendSyncRequestLight(Handle session_handle);"],
41 [0x21, "Result SendSyncRequest(Handle session_handle);"],
42 [0x22, "Result SendSyncRequestWithUserBuffer(Address message_buffer, Size message_buffer_size, Handle session_handle);"],
43 [0x23, "Result SendAsyncRequestWithUserBuffer(Handle* out_event_handle, Address message_buffer, Size message_buffer_size, Handle session_handle);"],
44 [0x24, "Result GetProcessId(uint64_t* out_process_id, Handle process_handle);"],
45 [0x25, "Result GetThreadId(uint64_t* out_thread_id, Handle thread_handle);"],
46 [0x26, "void Break(BreakReason break_reason, Address arg, Size size);"],
47 [0x27, "Result OutputDebugString(Address debug_str, Size len);"],
48 [0x28, "void ReturnFromException(Result result);"],
49 [0x29, "Result GetInfo(uint64_t* out, InfoType info_type, Handle handle, uint64_t info_subtype);"],
50 [0x2A, "void FlushEntireDataCache();"],
51 [0x2B, "Result FlushDataCache(Address address, Size size);"],
52 [0x2C, "Result MapPhysicalMemory(Address address, Size size);"],
53 [0x2D, "Result UnmapPhysicalMemory(Address address, Size size);"],
54 [0x2E, "Result GetDebugFutureThreadInfo(LastThreadContext* out_context, uint64_t* out_thread_id, Handle debug_handle, int64_t ns);"],
55 [0x2F, "Result GetLastThreadInfo(LastThreadContext* out_context, Address* out_tls_address, uint32_t* out_flags);"],
56 [0x30, "Result GetResourceLimitLimitValue(int64_t* out_limit_value, Handle resource_limit_handle, LimitableResource which);"],
57 [0x31, "Result GetResourceLimitCurrentValue(int64_t* out_current_value, Handle resource_limit_handle, LimitableResource which);"],
58 [0x32, "Result SetThreadActivity(Handle thread_handle, ThreadActivity thread_activity);"],
59 [0x33, "Result GetThreadContext3(Address out_context, Handle thread_handle);"],
60 [0x34, "Result WaitForAddress(Address address, ArbitrationType arb_type, int32_t value, int64_t timeout_ns);"],
61 [0x35, "Result SignalToAddress(Address address, SignalType signal_type, int32_t value, int32_t count);"],
62 [0x36, "void SynchronizePreemptionState();"],
63 [0x37, "Result GetResourceLimitPeakValue(int64_t* out_peak_value, Handle resource_limit_handle, LimitableResource which);"],
64
65 [0x39, "Result CreateIoPool(Handle* out_handle, IoPoolType which);"],
66 [0x3A, "Result CreateIoRegion(Handle* out_handle, Handle io_pool, PhysicalAddress physical_address, Size size, MemoryMapping mapping, MemoryPermission perm);"],
67
68 [0x3C, "void KernelDebug(KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2);"],
69 [0x3D, "void ChangeKernelTraceState(KernelTraceState kern_trace_state);"],
70
71 [0x40, "Result CreateSession(Handle* out_server_session_handle, Handle* out_client_session_handle, bool is_light, Address name);"],
72 [0x41, "Result AcceptSession(Handle* out_handle, Handle port);"],
73 [0x42, "Result ReplyAndReceiveLight(Handle handle);"],
74 [0x43, "Result ReplyAndReceive(int32_t* out_index, Address handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"],
75 [0x44, "Result ReplyAndReceiveWithUserBuffer(int32_t* out_index, Address message_buffer, Size message_buffer_size, Address handles, int32_t num_handles, Handle reply_target, int64_t timeout_ns);"],
76 [0x45, "Result CreateEvent(Handle* out_write_handle, Handle* out_read_handle);"],
77 [0x46, "Result MapIoRegion(Handle io_region, Address address, Size size, MemoryPermission perm);"],
78 [0x47, "Result UnmapIoRegion(Handle io_region, Address address, Size size);"],
79 [0x48, "Result MapPhysicalMemoryUnsafe(Address address, Size size);"],
80 [0x49, "Result UnmapPhysicalMemoryUnsafe(Address address, Size size);"],
81 [0x4A, "Result SetUnsafeLimit(Size limit);"],
82 [0x4B, "Result CreateCodeMemory(Handle* out_handle, Address address, Size size);"],
83 [0x4C, "Result ControlCodeMemory(Handle code_memory_handle, CodeMemoryOperation operation, uint64_t address, uint64_t size, MemoryPermission perm);"],
84 [0x4D, "void SleepSystem();"],
85 [0x4E, "Result ReadWriteRegister(uint32_t* out_value, PhysicalAddress address, uint32_t mask, uint32_t value);"],
86 [0x4F, "Result SetProcessActivity(Handle process_handle, ProcessActivity process_activity);"],
87 [0x50, "Result CreateSharedMemory(Handle* out_handle, Size size, MemoryPermission owner_perm, MemoryPermission remote_perm);"],
88 [0x51, "Result MapTransferMemory(Handle trmem_handle, Address address, Size size, MemoryPermission owner_perm);"],
89 [0x52, "Result UnmapTransferMemory(Handle trmem_handle, Address address, Size size);"],
90 [0x53, "Result CreateInterruptEvent(Handle* out_read_handle, int32_t interrupt_id, InterruptType interrupt_type);"],
91 [0x54, "Result QueryPhysicalAddress(PhysicalMemoryInfo* out_info, Address address);"],
92 [0x55, "Result QueryIoMapping(Address* out_address, Size* out_size, PhysicalAddress physical_address, Size size);"],
93 [0x56, "Result CreateDeviceAddressSpace(Handle* out_handle, uint64_t das_address, uint64_t das_size);"],
94 [0x57, "Result AttachDeviceAddressSpace(DeviceName device_name, Handle das_handle);"],
95 [0x58, "Result DetachDeviceAddressSpace(DeviceName device_name, Handle das_handle);"],
96 [0x59, "Result MapDeviceAddressSpaceByForce(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address, uint32_t option);"],
97 [0x5A, "Result MapDeviceAddressSpaceAligned(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address, uint32_t option);"],
98 [0x5C, "Result UnmapDeviceAddressSpace(Handle das_handle, Handle process_handle, uint64_t process_address, Size size, uint64_t device_address);"],
99 [0x5D, "Result InvalidateProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
100 [0x5E, "Result StoreProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
101 [0x5F, "Result FlushProcessDataCache(Handle process_handle, uint64_t address, uint64_t size);"],
102 [0x60, "Result DebugActiveProcess(Handle* out_handle, uint64_t process_id);"],
103 [0x61, "Result BreakDebugProcess(Handle debug_handle);"],
104 [0x62, "Result TerminateDebugProcess(Handle debug_handle);"],
105 [0x63, "Result GetDebugEvent(Address out_info, Handle debug_handle);"],
106 [0x64, "Result ContinueDebugEvent(Handle debug_handle, uint32_t flags, Address thread_ids, int32_t num_thread_ids);"],
107 [0x65, "Result GetProcessList(int32_t* out_num_processes, Address out_process_ids, int32_t max_out_count);"],
108 [0x66, "Result GetThreadList(int32_t* out_num_threads, Address out_thread_ids, int32_t max_out_count, Handle debug_handle);"],
109 [0x67, "Result GetDebugThreadContext(Address out_context, Handle debug_handle, uint64_t thread_id, uint32_t context_flags);"],
110 [0x68, "Result SetDebugThreadContext(Handle debug_handle, uint64_t thread_id, Address context, uint32_t context_flags);"],
111 [0x69, "Result QueryDebugProcessMemory(Address out_memory_info, PageInfo* out_page_info, Handle process_handle, Address address);"],
112 [0x6A, "Result ReadDebugProcessMemory(Address buffer, Handle debug_handle, Address address, Size size);"],
113 [0x6B, "Result WriteDebugProcessMemory(Handle debug_handle, Address buffer, Address address, Size size);"],
114 [0x6C, "Result SetHardwareBreakPoint(HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value);"],
115 [0x6D, "Result GetDebugThreadParam(uint64_t* out_64, uint32_t* out_32, Handle debug_handle, uint64_t thread_id, DebugThreadParam param);"],
116
117 [0x6F, "Result GetSystemInfo(uint64_t* out, SystemInfoType info_type, Handle handle, uint64_t info_subtype);"],
118 [0x70, "Result CreatePort(Handle* out_server_handle, Handle* out_client_handle, int32_t max_sessions, bool is_light, Address name);"],
119 [0x71, "Result ManageNamedPort(Handle* out_server_handle, Address name, int32_t max_sessions);"],
120 [0x72, "Result ConnectToPort(Handle* out_handle, Handle port);"],
121 [0x73, "Result SetProcessMemoryPermission(Handle process_handle, uint64_t address, uint64_t size, MemoryPermission perm);"],
122 [0x74, "Result MapProcessMemory(Address dst_address, Handle process_handle, uint64_t src_address, Size size);"],
123 [0x75, "Result UnmapProcessMemory(Address dst_address, Handle process_handle, uint64_t src_address, Size size);"],
124 [0x76, "Result QueryProcessMemory(Address out_memory_info, PageInfo* out_page_info, Handle process_handle, uint64_t address);"],
125 [0x77, "Result MapProcessCodeMemory(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"],
126 [0x78, "Result UnmapProcessCodeMemory(Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size);"],
127 [0x79, "Result CreateProcess(Handle* out_handle, Address parameters, Address caps, int32_t num_caps);"],
128 [0x7A, "Result StartProcess(Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size);"],
129 [0x7B, "Result TerminateProcess(Handle process_handle);"],
130 [0x7C, "Result GetProcessInfo(int64_t* out_info, Handle process_handle, ProcessInfoType info_type);"],
131 [0x7D, "Result CreateResourceLimit(Handle* out_handle);"],
132 [0x7E, "Result SetResourceLimitLimitValue(Handle resource_limit_handle, LimitableResource which, int64_t limit_value);"],
133 [0x7F, "void CallSecureMonitor(SecureMonitorArguments args);"],
134
135 [0x90, "Result MapInsecureMemory(Address address, Size size);"],
136 [0x91, "Result UnmapInsecureMemory(Address address, Size size);"],
137]
138
139# These use a custom ABI, and therefore require custom wrappers
140SKIP_WRAPPERS = {
141 0x20: "SendSyncRequestLight",
142 0x42: "ReplyAndReceiveLight",
143 0x7F: "CallSecureMonitor",
144}
145
146BIT_32 = 0
147BIT_64 = 1
148
149REG_SIZES = [4, 8]
150SUFFIX_NAMES = ["64From32", "64"]
151TYPE_SIZES = {
152 # SVC types
153 "ArbitrationType": 4,
154 "BreakReason": 4,
155 "CodeMemoryOperation": 4,
156 "DebugThreadParam": 4,
157 "DeviceName": 4,
158 "HardwareBreakPointRegisterName": 4,
159 "Handle": 4,
160 "InfoType": 4,
161 "InterruptType": 4,
162 "IoPoolType": 4,
163 "KernelDebugType": 4,
164 "KernelTraceState": 4,
165 "LimitableResource": 4,
166 "MemoryMapping": 4,
167 "MemoryPermission": 4,
168 "PageInfo": 4,
169 "ProcessActivity": 4,
170 "ProcessInfoType": 4,
171 "Result": 4,
172 "SignalType": 4,
173 "SystemInfoType": 4,
174 "ThreadActivity": 4,
175
176 # Arch-specific types
177 "ilp32::LastThreadContext": 16,
178 "ilp32::PhysicalMemoryInfo": 16,
179 "ilp32::SecureMonitorArguments": 32,
180 "lp64::LastThreadContext": 32,
181 "lp64::PhysicalMemoryInfo": 24,
182 "lp64::SecureMonitorArguments": 64,
183
184 # Generic types
185 "bool": 1,
186 "int32_t": 4,
187 "int64_t": 8,
188 "uint32_t": 4,
189 "uint64_t": 8,
190 "void": 0,
191}
192
193TYPE_REPLACEMENTS = {
194 "Address": ["uint32_t", "uint64_t"],
195 "LastThreadContext": ["ilp32::LastThreadContext", "lp64::LastThreadContext"],
196 "PhysicalAddress": ["uint64_t", "uint64_t"],
197 "PhysicalMemoryInfo": ["ilp32::PhysicalMemoryInfo", "lp64::PhysicalMemoryInfo"],
198 "SecureMonitorArguments": ["ilp32::SecureMonitorArguments", "lp64::SecureMonitorArguments"],
199 "Size": ["uint32_t", "uint64_t"],
200 "ThreadFunc": ["uint32_t", "uint64_t"],
201}
202
203# Statically verify that the hardcoded sizes match the intended
204# sizes in C++.
205def emit_size_check():
206 lines = []
207
208 for type, size in TYPE_SIZES.items():
209 if type != "void":
210 lines.append(f"static_assert(sizeof({type}) == {size});")
211
212 return "\n".join(lines)
213
214
215# Replaces a type with an arch-specific one, if it exists.
216def substitute_type(name, bitness):
217 if name in TYPE_REPLACEMENTS:
218 return TYPE_REPLACEMENTS[name][bitness]
219 else:
220 return name
221
222
223class Argument:
224 def __init__(self, type_name, var_name, is_output, is_outptr, is_address):
225 self.type_name = type_name
226 self.var_name = var_name
227 self.is_output = is_output
228 self.is_outptr = is_outptr
229 self.is_address = is_address
230
231
232# Parses C-style string declarations for SVCs.
233def parse_declaration(declaration, bitness):
234 return_type, rest = declaration.split(" ", 1)
235 func_name, rest = rest.split("(", 1)
236 arg_names, rest = rest.split(")", 1)
237 argument_types = []
238
239 return_type = substitute_type(return_type, bitness)
240 assert return_type in TYPE_SIZES, f"Unknown type '{return_type}'"
241
242 if arg_names:
243 for arg_name in arg_names.split(", "):
244 type_name, var_name = arg_name.replace("*", "").split(" ", 1)
245
246 # All outputs must contain out_ in the name.
247 is_output = var_name == "out" or var_name.find("out_") != -1
248
249 # User-pointer outputs are not written to registers.
250 is_outptr = is_output and arg_name.find("*") == -1
251
252 # Special handling is performed for output addresses to avoid awkwardness
253 # in conversion for the 32-bit equivalents.
254 is_address = is_output and not is_outptr and \
255 type_name in ["Address", "Size"]
256 type_name = substitute_type(type_name, bitness)
257
258 assert type_name in TYPE_SIZES, f"Unknown type '{type_name}'"
259
260 argument_types.append(
261 Argument(type_name, var_name, is_output, is_outptr, is_address))
262
263 return (return_type, func_name, argument_types)
264
265
266class RegisterAllocator:
267 def __init__(self, num_regs, byte_size, parameter_count):
268 self.registers = {}
269 self.num_regs = num_regs
270 self.byte_size = byte_size
271 self.parameter_count = parameter_count
272
273 # Mark the given register as allocated, for use in layout
274 # calculation if the NGRN exceeds the ABI parameter count.
275 def allocate(self, i):
276 assert i not in self.registers, f"Register R{i} already allocated"
277 self.registers[i] = True
278 return i
279
280 # Calculate the next available location for a register;
281 # the NGRN has exceeded the ABI parameter count.
282 def allocate_first_free(self):
283 for i in range(0, self.num_regs):
284 if i in self.registers:
285 continue
286
287 self.allocate(i)
288 return i
289
290 assert False, "No registers available"
291
292 # Add a single register at the given NGRN.
293 # If the index exceeds the ABI parameter count, try to find a
294 # location to add it. Returns the output location and increment.
295 def add_single(self, ngrn):
296 if ngrn >= self.parameter_count:
297 return (self.allocate_first_free(), 0)
298 else:
299 return (self.allocate(ngrn), 1)
300
301 # Add registers at the given NGRN for a data type of
302 # the given size. Returns the output locations and increment.
303 def add(self, ngrn, data_size, align=True):
304 if data_size <= self.byte_size:
305 r, i = self.add_single(ngrn)
306 return ([r], i)
307
308 regs = []
309 inc = ngrn % 2 if align else 0
310 remaining_size = data_size
311 while remaining_size > 0:
312 r, i = self.add_single(ngrn + inc)
313 regs.append(r)
314 inc += i
315 remaining_size -= self.byte_size
316
317 return (regs, inc)
318
319
320def reg_alloc(bitness):
321 if bitness == 0:
322 # aapcs32: 4 4-byte registers
323 return RegisterAllocator(8, 4, 4)
324 elif bitness == 1:
325 # aapcs64: 8 8-byte registers
326 return RegisterAllocator(8, 8, 8)
327
328
329# Converts a parsed SVC declaration into register lists for
330# the return value, outputs, and inputs.
331def get_registers(parse_result, bitness):
332 output_alloc = reg_alloc(bitness)
333 input_alloc = reg_alloc(bitness)
334 return_type, _, arguments = parse_result
335
336 return_write = []
337 output_writes = []
338 input_reads = []
339
340 input_ngrn = 0
341 output_ngrn = 0
342
343 # Run the input calculation.
344 for arg in arguments:
345 if arg.is_output and not arg.is_outptr:
346 input_ngrn += 1
347 continue
348
349 regs, increment = input_alloc.add(
350 input_ngrn, TYPE_SIZES[arg.type_name], align=True)
351 input_reads.append([arg.type_name, arg.var_name, regs])
352 input_ngrn += increment
353
354 # Include the return value if this SVC returns a value.
355 if return_type != "void":
356 regs, increment = output_alloc.add(
357 output_ngrn, TYPE_SIZES[return_type], align=False)
358 return_write.append([return_type, regs])
359 output_ngrn += increment
360
361 # Run the output calculation.
362 for arg in arguments:
363 if not arg.is_output or arg.is_outptr:
364 continue
365
366 regs, increment = output_alloc.add(
367 output_ngrn, TYPE_SIZES[arg.type_name], align=False)
368 output_writes.append(
369 [arg.type_name, arg.var_name, regs, arg.is_address])
370 output_ngrn += increment
371
372 return (return_write, output_writes, input_reads)
373
374
375# Collects possibly multiple source registers into the named C++ value.
376def emit_gather(sources, name, type_name, reg_size):
377 get_fn = f"GetReg{reg_size*8}"
378
379 if len(sources) == 1:
380 s, = sources
381 line = f"{name} = Convert<{type_name}>({get_fn}(system, {s}));"
382 return [line]
383
384 var_type = f"std::array<uint{reg_size*8}_t, {len(sources)}>"
385 lines = [
386 f"{var_type} {name}_gather{{}};"
387 ]
388 for i in range(0, len(sources)):
389 lines.append(
390 f"{name}_gather[{i}] = {get_fn}(system, {sources[i]});")
391
392 lines.append(f"{name} = Convert<{type_name}>({name}_gather);")
393 return lines
394
395
396# Produces one or more statements which assign the named C++ value
397# into possibly multiple registers.
398def emit_scatter(destinations, name, reg_size):
399 set_fn = f"SetReg{reg_size*8}"
400 reg_type = f"uint{reg_size*8}_t"
401
402 if len(destinations) == 1:
403 d, = destinations
404 line = f"{set_fn}(system, {d}, Convert<{reg_type}>({name}));"
405 return [line]
406
407 var_type = f"std::array<{reg_type}, {len(destinations)}>"
408 lines = [
409 f"auto {name}_scatter = Convert<{var_type}>({name});"
410 ]
411
412 for i in range(0, len(destinations)):
413 lines.append(
414 f"{set_fn}(system, {destinations[i]}, {name}_scatter[{i}]);")
415
416 return lines
417
418
419def emit_lines(lines, indent=' '):
420 output_lines = []
421 first = True
422 for line in lines:
423 if line and not first:
424 output_lines.append(indent + line)
425 else:
426 output_lines.append(line)
427 first = False
428
429 return "\n".join(output_lines)
430
431
432# Emit a C++ function to wrap a guest SVC.
433def emit_wrapper(wrapped_fn, suffix, register_info, arguments, byte_size):
434 return_write, output_writes, input_reads = register_info
435 lines = [
436 f"static void SvcWrap_{wrapped_fn}{suffix}(Core::System& system) {{"
437 ]
438
439 # Get everything ready.
440 for return_type, _ in return_write:
441 lines.append(f"{return_type} ret{{}};")
442 if return_write:
443 lines.append("")
444
445 for output_type, var_name, _, is_address in output_writes:
446 output_type = "uintptr_t" if is_address else output_type
447 lines.append(f"{output_type} {var_name}{{}};")
448 for input_type, var_name, _ in input_reads:
449 lines.append(f"{input_type} {var_name}{{}};")
450
451 if output_writes or input_reads:
452 lines.append("")
453
454 for input_type, var_name, sources in input_reads:
455 lines += emit_gather(sources, var_name, input_type, byte_size)
456 if input_reads:
457 lines.append("")
458
459 # Build the call.
460 call_arguments = ["system"]
461 for arg in arguments:
462 if arg.is_output and not arg.is_outptr:
463 call_arguments.append(f"&{arg.var_name}")
464 else:
465 call_arguments.append(arg.var_name)
466
467 line = ""
468 if return_write:
469 line += "ret = "
470
471 line += f"{wrapped_fn}{suffix}({', '.join(call_arguments)});"
472 lines.append(line)
473
474 if return_write or output_writes:
475 lines.append("")
476
477 # Write back the return value and outputs.
478 for _, destinations in return_write:
479 lines += emit_scatter(destinations, "ret", byte_size)
480 for _, var_name, destinations, _ in output_writes:
481 lines += emit_scatter(destinations, var_name, byte_size)
482
483 # Finish.
484 return emit_lines(lines) + "\n}"
485
486
487COPYRIGHT = """\
488// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
489// SPDX-License-Identifier: GPL-2.0-or-later
490
491// This file is automatically generated using svc_generator.py.
492"""
493
494PROLOGUE_H = """
495#pragma once
496
497namespace Core {
498class System;
499}
500
501#include "common/common_types.h"
502#include "core/hle/kernel/svc_types.h"
503#include "core/hle/result.h"
504
505namespace Kernel::Svc {
506
507// clang-format off
508"""
509
510EPILOGUE_H = """
511// clang-format on
512
513// Custom ABI.
514Result ReplyAndReceiveLight(Core::System& system, Handle handle, uint32_t* args);
515Result ReplyAndReceiveLight64From32(Core::System& system, Handle handle, uint32_t* args);
516Result ReplyAndReceiveLight64(Core::System& system, Handle handle, uint32_t* args);
517
518Result SendSyncRequestLight(Core::System& system, Handle session_handle, uint32_t* args);
519Result SendSyncRequestLight64From32(Core::System& system, Handle session_handle, uint32_t* args);
520Result SendSyncRequestLight64(Core::System& system, Handle session_handle, uint32_t* args);
521
522void CallSecureMonitor(Core::System& system, lp64::SecureMonitorArguments* args);
523void CallSecureMonitor64From32(Core::System& system, ilp32::SecureMonitorArguments* args);
524void CallSecureMonitor64(Core::System& system, lp64::SecureMonitorArguments* args);
525
526// Defined in svc_light_ipc.cpp.
527void SvcWrap_ReplyAndReceiveLight64From32(Core::System& system);
528void SvcWrap_ReplyAndReceiveLight64(Core::System& system);
529
530void SvcWrap_SendSyncRequestLight64From32(Core::System& system);
531void SvcWrap_SendSyncRequestLight64(Core::System& system);
532
533// Defined in svc_secure_monitor_call.cpp.
534void SvcWrap_CallSecureMonitor64From32(Core::System& system);
535void SvcWrap_CallSecureMonitor64(Core::System& system);
536
537// Perform a supervisor call by index.
538void Call(Core::System& system, u32 imm);
539
540} // namespace Kernel::Svc
541"""
542
543PROLOGUE_CPP = """
544#include <type_traits>
545
546#include "core/arm/arm_interface.h"
547#include "core/core.h"
548#include "core/hle/kernel/k_process.h"
549#include "core/hle/kernel/svc.h"
550
551namespace Kernel::Svc {
552
553static uint32_t GetReg32(Core::System& system, int n) {
554 return static_cast<uint32_t>(system.CurrentArmInterface().GetReg(n));
555}
556
557static void SetReg32(Core::System& system, int n, uint32_t result) {
558 system.CurrentArmInterface().SetReg(n, static_cast<uint64_t>(result));
559}
560
561static uint64_t GetReg64(Core::System& system, int n) {
562 return system.CurrentArmInterface().GetReg(n);
563}
564
565static void SetReg64(Core::System& system, int n, uint64_t result) {
566 system.CurrentArmInterface().SetReg(n, result);
567}
568
569// Like bit_cast, but handles the case when the source and dest
570// are differently-sized.
571template <typename To, typename From>
572 requires(std::is_trivial_v<To> && std::is_trivially_copyable_v<From>)
573static To Convert(const From& from) {
574 To to{};
575
576 if constexpr (sizeof(To) >= sizeof(From)) {
577 std::memcpy(&to, &from, sizeof(From));
578 } else {
579 std::memcpy(&to, &from, sizeof(To));
580 }
581
582 return to;
583}
584
585// clang-format off
586"""
587
588EPILOGUE_CPP = """
589// clang-format on
590
591void Call(Core::System& system, u32 imm) {
592 auto& kernel = system.Kernel();
593 kernel.EnterSVCProfile();
594
595 if (system.CurrentProcess()->Is64BitProcess()) {
596 Call64(system, imm);
597 } else {
598 Call32(system, imm);
599 }
600
601 kernel.ExitSVCProfile();
602}
603
604} // namespace Kernel::Svc
605"""
606
607
608def emit_call(bitness, names, suffix):
609 bit_size = REG_SIZES[bitness]*8
610 indent = " "
611 lines = [
612 f"static void Call{bit_size}(Core::System& system, u32 imm) {{",
613 f"{indent}switch (static_cast<SvcId>(imm)) {{"
614 ]
615
616 for _, name in names:
617 lines.append(f"{indent}case SvcId::{name}:")
618 lines.append(f"{indent*2}return SvcWrap_{name}{suffix}(system);")
619
620 lines.append(f"{indent}default:")
621 lines.append(
622 f"{indent*2}LOG_CRITICAL(Kernel_SVC, \"Unknown SVC {{:x}}!\", imm);")
623 lines.append(f"{indent*2}break;")
624 lines.append(f"{indent}}}")
625 lines.append("}")
626
627 return "\n".join(lines)
628
629
630def build_fn_declaration(return_type, name, arguments):
631 arg_list = ["Core::System& system"]
632 for arg in arguments:
633 type_name = "uintptr_t" if arg.is_address else arg.type_name
634 pointer = "*" if arg.is_output and not arg.is_outptr else ""
635 arg_list.append(f"{type_name}{pointer} {arg.var_name}")
636
637 return f"{return_type} {name}({', '.join(arg_list)});"
638
639
640def build_enum_declarations():
641 lines = ["enum class SvcId : u32 {"]
642 indent = " "
643
644 for imm, decl in SVCS:
645 _, name, _ = parse_declaration(decl, BIT_64)
646 lines.append(f"{indent}{name} = {hex(imm)},")
647
648 lines.append("};")
649 return "\n".join(lines)
650
651
652def main():
653 arch_fw_declarations = [[], []]
654 svc_fw_declarations = []
655 wrapper_fns = []
656 names = []
657
658 for imm, decl in SVCS:
659 return_type, name, arguments = parse_declaration(decl, BIT_64)
660
661 if imm not in SKIP_WRAPPERS:
662 svc_fw_declarations.append(
663 build_fn_declaration(return_type, name, arguments))
664
665 names.append([imm, name])
666
667 for bitness in range(2):
668 byte_size = REG_SIZES[bitness]
669 suffix = SUFFIX_NAMES[bitness]
670
671 for imm, decl in SVCS:
672 if imm in SKIP_WRAPPERS:
673 continue
674
675 parse_result = parse_declaration(decl, bitness)
676 return_type, name, arguments = parse_result
677
678 register_info = get_registers(parse_result, bitness)
679 wrapper_fns.append(
680 emit_wrapper(name, suffix, register_info, arguments, byte_size))
681 arch_fw_declarations[bitness].append(
682 build_fn_declaration(return_type, name + suffix, arguments))
683
684 call_32 = emit_call(BIT_32, names, SUFFIX_NAMES[BIT_32])
685 call_64 = emit_call(BIT_64, names, SUFFIX_NAMES[BIT_64])
686 enum_decls = build_enum_declarations()
687
688 with open("svc.h", "w") as f:
689 f.write(COPYRIGHT)
690 f.write(PROLOGUE_H)
691 f.write("\n".join(svc_fw_declarations))
692 f.write("\n\n")
693 f.write("\n".join(arch_fw_declarations[BIT_32]))
694 f.write("\n\n")
695 f.write("\n".join(arch_fw_declarations[BIT_64]))
696 f.write("\n\n")
697 f.write(enum_decls)
698 f.write(EPILOGUE_H)
699
700 with open("svc.cpp", "w") as f:
701 f.write(COPYRIGHT)
702 f.write(PROLOGUE_CPP)
703 f.write(emit_size_check())
704 f.write("\n\n")
705 f.write("\n\n".join(wrapper_fns))
706 f.write("\n\n")
707 f.write(call_32)
708 f.write("\n\n")
709 f.write(call_64)
710 f.write(EPILOGUE_CPP)
711
712 print(f"Done (emitted {len(names)} definitions)")
713
714
715if __name__ == "__main__":
716 main()
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index b7ca53085..e1ad78607 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -11,6 +11,7 @@ namespace Kernel {
11 11
12constexpr Result ResultOutOfSessions{ErrorModule::Kernel, 7}; 12constexpr Result ResultOutOfSessions{ErrorModule::Kernel, 7};
13constexpr Result ResultInvalidArgument{ErrorModule::Kernel, 14}; 13constexpr Result ResultInvalidArgument{ErrorModule::Kernel, 14};
14constexpr Result ResultNotImplemented{ErrorModule::Kernel, 33};
14constexpr Result ResultNoSynchronizationObject{ErrorModule::Kernel, 57}; 15constexpr Result ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
15constexpr Result ResultTerminationRequested{ErrorModule::Kernel, 59}; 16constexpr Result ResultTerminationRequested{ErrorModule::Kernel, 59};
16constexpr Result ResultInvalidSize{ErrorModule::Kernel, 101}; 17constexpr Result ResultInvalidSize{ErrorModule::Kernel, 101};
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 33eebcef6..542c13461 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -3,6 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <bitset>
7
8#include "common/bit_field.h"
6#include "common/common_funcs.h" 9#include "common/common_funcs.h"
7#include "common/common_types.h" 10#include "common/common_types.h"
8 11
@@ -165,6 +168,7 @@ enum class BreakReason : u32 {
165 168
166 NotificationOnlyFlag = 0x80000000, 169 NotificationOnlyFlag = 0x80000000,
167}; 170};
171DECLARE_ENUM_FLAG_OPERATORS(BreakReason);
168 172
169enum class DebugEvent : u32 { 173enum class DebugEvent : u32 {
170 CreateProcess = 0, 174 CreateProcess = 0,
@@ -496,6 +500,19 @@ enum class MemoryMapping : u32 {
496 Memory = 2, 500 Memory = 2,
497}; 501};
498 502
503enum class MapDeviceAddressSpaceFlag : u32 {
504 None = (0U << 0),
505 NotIoRegister = (1U << 0),
506};
507DECLARE_ENUM_FLAG_OPERATORS(MapDeviceAddressSpaceFlag);
508
509union MapDeviceAddressSpaceOption {
510 u32 raw;
511 BitField<0, 16, MemoryPermission> permission;
512 BitField<16, 1, MapDeviceAddressSpaceFlag> flags;
513 BitField<17, 15, u32> reserved;
514};
515
499enum class KernelDebugType : u32 { 516enum class KernelDebugType : u32 {
500 Thread = 0, 517 Thread = 0,
501 ThreadCallStack = 1, 518 ThreadCallStack = 1,
@@ -580,6 +597,11 @@ enum class ProcessInfoType : u32 {
580 ProcessState = 0, 597 ProcessState = 0,
581}; 598};
582 599
600enum class ProcessActivity : u32 {
601 Runnable,
602 Paused,
603};
604
583struct CreateProcessParameter { 605struct CreateProcessParameter {
584 std::array<char, 12> name; 606 std::array<char, 12> name;
585 u32 version; 607 u32 version;
@@ -592,4 +614,12 @@ struct CreateProcessParameter {
592}; 614};
593static_assert(sizeof(CreateProcessParameter) == 0x30); 615static_assert(sizeof(CreateProcessParameter) == 0x30);
594 616
617constexpr size_t NumSupervisorCalls = 0xC0;
618using SvcAccessFlagSet = std::bitset<NumSupervisorCalls>;
619
620enum class InitialProcessIdRangeInfo : u64 {
621 Minimum = 0,
622 Maximum = 1,
623};
624
595} // namespace Kernel::Svc 625} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_version.h b/src/core/hle/kernel/svc_version.h
new file mode 100644
index 000000000..3eb95aa7b
--- /dev/null
+++ b/src/core/hle/kernel/svc_version.h
@@ -0,0 +1,58 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8#include "common/literals.h"
9
10namespace Kernel::Svc {
11
12constexpr inline u32 ConvertToSvcMajorVersion(u32 sdk) {
13 return sdk + 4;
14}
15constexpr inline u32 ConvertToSdkMajorVersion(u32 svc) {
16 return svc - 4;
17}
18
19constexpr inline u32 ConvertToSvcMinorVersion(u32 sdk) {
20 return sdk;
21}
22constexpr inline u32 ConvertToSdkMinorVersion(u32 svc) {
23 return svc;
24}
25
26union KernelVersion {
27 u32 value;
28 BitField<0, 4, u32> minor_version;
29 BitField<4, 13, u32> major_version;
30};
31
32constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) {
33 return decltype(KernelVersion::minor_version)::FormatValue(minor) |
34 decltype(KernelVersion::major_version)::FormatValue(major);
35}
36
37constexpr inline u32 GetKernelMajorVersion(u32 encoded) {
38 return decltype(KernelVersion::major_version)::ExtractValue(encoded);
39}
40
41constexpr inline u32 GetKernelMinorVersion(u32 encoded) {
42 return decltype(KernelVersion::minor_version)::ExtractValue(encoded);
43}
44
45// Nintendo doesn't support programs targeting SVC versions < 3.0.
46constexpr inline u32 RequiredKernelMajorVersion = 3;
47constexpr inline u32 RequiredKernelMinorVersion = 0;
48constexpr inline u32 RequiredKernelVersion =
49 EncodeKernelVersion(RequiredKernelMajorVersion, RequiredKernelMinorVersion);
50
51// This is the highest SVC version supported, to be updated on new kernel releases.
52// NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor.
53constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(15);
54constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion(3);
55constexpr inline u32 SupportedKernelVersion =
56 EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion);
57
58} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
deleted file mode 100644
index 1ea8c7fbc..000000000
--- a/src/core/hle/kernel/svc_wrap.h
+++ /dev/null
@@ -1,733 +0,0 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/arm/arm_interface.h"
8#include "core/core.h"
9#include "core/hle/kernel/svc_types.h"
10#include "core/hle/result.h"
11#include "core/memory.h"
12
13namespace Kernel {
14
15static inline u64 Param(const Core::System& system, int n) {
16 return system.CurrentArmInterface().GetReg(n);
17}
18
19static inline u32 Param32(const Core::System& system, int n) {
20 return static_cast<u32>(system.CurrentArmInterface().GetReg(n));
21}
22
23/**
24 * HLE a function return from the current ARM userland process
25 * @param system System context
26 * @param result Result to return
27 */
28static inline void FuncReturn(Core::System& system, u64 result) {
29 system.CurrentArmInterface().SetReg(0, result);
30}
31
32static inline void FuncReturn32(Core::System& system, u32 result) {
33 system.CurrentArmInterface().SetReg(0, (u64)result);
34}
35
36////////////////////////////////////////////////////////////////////////////////////////////////////
37// Function wrappers that return type Result
38
39template <Result func(Core::System&, u64)>
40void SvcWrap64(Core::System& system) {
41 FuncReturn(system, func(system, Param(system, 0)).raw);
42}
43
44template <Result func(Core::System&, u64, u64)>
45void SvcWrap64(Core::System& system) {
46 FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
47}
48
49template <Result func(Core::System&, u32)>
50void SvcWrap64(Core::System& system) {
51 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
52}
53
54template <Result func(Core::System&, u32, u32)>
55void SvcWrap64(Core::System& system) {
56 FuncReturn(
57 system,
58 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw);
59}
60
61// Used by SetThreadActivity
62template <Result func(Core::System&, Handle, Svc::ThreadActivity)>
63void SvcWrap64(Core::System& system) {
64 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
65 static_cast<Svc::ThreadActivity>(Param(system, 1)))
66 .raw);
67}
68
69template <Result func(Core::System&, u32, u64, u64, u64)>
70void SvcWrap64(Core::System& system) {
71 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
72 Param(system, 2), Param(system, 3))
73 .raw);
74}
75
76// Used by MapProcessMemory and UnmapProcessMemory
77template <Result func(Core::System&, u64, u32, u64, u64)>
78void SvcWrap64(Core::System& system) {
79 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
80 Param(system, 2), Param(system, 3))
81 .raw);
82}
83
84// Used by ControlCodeMemory
85template <Result func(Core::System&, Handle, u32, VAddr, size_t, Svc::MemoryPermission)>
86void SvcWrap64(Core::System& system) {
87 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
88 static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
89 static_cast<Svc::MemoryPermission>(Param(system, 4)))
90 .raw);
91}
92
93template <Result func(Core::System&, u32*)>
94void SvcWrap64(Core::System& system) {
95 u32 param = 0;
96 const u32 retval = func(system, &param).raw;
97 system.CurrentArmInterface().SetReg(1, param);
98 FuncReturn(system, retval);
99}
100
101template <Result func(Core::System&, u32*, u32)>
102void SvcWrap64(Core::System& system) {
103 u32 param_1 = 0;
104 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
105 system.CurrentArmInterface().SetReg(1, param_1);
106 FuncReturn(system, retval);
107}
108
109template <Result func(Core::System&, u32*, u32*)>
110void SvcWrap64(Core::System& system) {
111 u32 param_1 = 0;
112 u32 param_2 = 0;
113 const u32 retval = func(system, &param_1, &param_2).raw;
114
115 auto& arm_interface = system.CurrentArmInterface();
116 arm_interface.SetReg(1, param_1);
117 arm_interface.SetReg(2, param_2);
118
119 FuncReturn(system, retval);
120}
121
122template <Result func(Core::System&, u32*, u64)>
123void SvcWrap64(Core::System& system) {
124 u32 param_1 = 0;
125 const u32 retval = func(system, &param_1, Param(system, 1)).raw;
126 system.CurrentArmInterface().SetReg(1, param_1);
127 FuncReturn(system, retval);
128}
129
130template <Result func(Core::System&, u32*, u64, u32)>
131void SvcWrap64(Core::System& system) {
132 u32 param_1 = 0;
133 const u32 retval =
134 func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2))).raw;
135
136 system.CurrentArmInterface().SetReg(1, param_1);
137 FuncReturn(system, retval);
138}
139
140template <Result func(Core::System&, u64*, u32)>
141void SvcWrap64(Core::System& system) {
142 u64 param_1 = 0;
143 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
144
145 system.CurrentArmInterface().SetReg(1, param_1);
146 FuncReturn(system, retval);
147}
148
149template <Result func(Core::System&, u64, u32)>
150void SvcWrap64(Core::System& system) {
151 FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
152}
153
154template <Result func(Core::System&, u64*, u64)>
155void SvcWrap64(Core::System& system) {
156 u64 param_1 = 0;
157 const u32 retval = func(system, &param_1, Param(system, 1)).raw;
158
159 system.CurrentArmInterface().SetReg(1, param_1);
160 FuncReturn(system, retval);
161}
162
163template <Result func(Core::System&, u64*, u32, u32)>
164void SvcWrap64(Core::System& system) {
165 u64 param_1 = 0;
166 const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
167 static_cast<u32>(Param(system, 2)))
168 .raw;
169
170 system.CurrentArmInterface().SetReg(1, param_1);
171 FuncReturn(system, retval);
172}
173
174// Used by GetResourceLimitLimitValue.
175template <Result func(Core::System&, u64*, Handle, LimitableResource)>
176void SvcWrap64(Core::System& system) {
177 u64 param_1 = 0;
178 const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)),
179 static_cast<LimitableResource>(Param(system, 2)))
180 .raw;
181
182 system.CurrentArmInterface().SetReg(1, param_1);
183 FuncReturn(system, retval);
184}
185
186template <Result func(Core::System&, u32, u64)>
187void SvcWrap64(Core::System& system) {
188 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
189}
190
191// Used by SetResourceLimitLimitValue
192template <Result func(Core::System&, Handle, LimitableResource, u64)>
193void SvcWrap64(Core::System& system) {
194 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
195 static_cast<LimitableResource>(Param(system, 1)), Param(system, 2))
196 .raw);
197}
198
199// Used by SetThreadCoreMask
200template <Result func(Core::System&, Handle, s32, u64)>
201void SvcWrap64(Core::System& system) {
202 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
203 static_cast<s32>(Param(system, 1)), Param(system, 2))
204 .raw);
205}
206
207// Used by GetThreadCoreMask
208template <Result func(Core::System&, Handle, s32*, u64*)>
209void SvcWrap64(Core::System& system) {
210 s32 param_1 = 0;
211 u64 param_2 = 0;
212 const Result retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
213
214 system.CurrentArmInterface().SetReg(1, param_1);
215 system.CurrentArmInterface().SetReg(2, param_2);
216 FuncReturn(system, retval.raw);
217}
218
219template <Result func(Core::System&, u64, u64, u32, u32)>
220void SvcWrap64(Core::System& system) {
221 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
222 static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
223 .raw);
224}
225
226template <Result func(Core::System&, u64, u64, u32, u64)>
227void SvcWrap64(Core::System& system) {
228 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
229 static_cast<u32>(Param(system, 2)), Param(system, 3))
230 .raw);
231}
232
233template <Result func(Core::System&, u32, u64, u32)>
234void SvcWrap64(Core::System& system) {
235 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
236 static_cast<u32>(Param(system, 2)))
237 .raw);
238}
239
240template <Result func(Core::System&, u64, u64, u64)>
241void SvcWrap64(Core::System& system) {
242 FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
243}
244
245template <Result func(Core::System&, u64, u64, u32)>
246void SvcWrap64(Core::System& system) {
247 FuncReturn(
248 system,
249 func(system, Param(system, 0), Param(system, 1), static_cast<u32>(Param(system, 2))).raw);
250}
251
252// Used by SetMemoryPermission
253template <Result func(Core::System&, u64, u64, Svc::MemoryPermission)>
254void SvcWrap64(Core::System& system) {
255 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
256 static_cast<Svc::MemoryPermission>(Param(system, 2)))
257 .raw);
258}
259
260// Used by MapSharedMemory
261template <Result func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
262void SvcWrap64(Core::System& system) {
263 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), Param(system, 1),
264 Param(system, 2), static_cast<Svc::MemoryPermission>(Param(system, 3)))
265 .raw);
266}
267
268template <Result func(Core::System&, u32, u64, u64)>
269void SvcWrap64(Core::System& system) {
270 FuncReturn(
271 system,
272 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2)).raw);
273}
274
275// Used by WaitSynchronization
276template <Result func(Core::System&, s32*, u64, s32, s64)>
277void SvcWrap64(Core::System& system) {
278 s32 param_1 = 0;
279 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<s32>(Param(system, 2)),
280 static_cast<s64>(Param(system, 3)))
281 .raw;
282
283 system.CurrentArmInterface().SetReg(1, param_1);
284 FuncReturn(system, retval);
285}
286
287template <Result func(Core::System&, u64, u64, u32, s64)>
288void SvcWrap64(Core::System& system) {
289 FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
290 static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
291 .raw);
292}
293
294// Used by GetInfo
295template <Result func(Core::System&, u64*, u64, Handle, u64)>
296void SvcWrap64(Core::System& system) {
297 u64 param_1 = 0;
298 const u32 retval = func(system, &param_1, Param(system, 1),
299 static_cast<Handle>(Param(system, 2)), Param(system, 3))
300 .raw;
301
302 system.CurrentArmInterface().SetReg(1, param_1);
303 FuncReturn(system, retval);
304}
305
306template <Result func(Core::System&, u32*, u64, u64, u64, u32, s32)>
307void SvcWrap64(Core::System& system) {
308 u32 param_1 = 0;
309 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
310 static_cast<u32>(Param(system, 4)), static_cast<s32>(Param(system, 5)))
311 .raw;
312
313 system.CurrentArmInterface().SetReg(1, param_1);
314 FuncReturn(system, retval);
315}
316
317// Used by CreateTransferMemory
318template <Result func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)>
319void SvcWrap64(Core::System& system) {
320 u32 param_1 = 0;
321 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
322 static_cast<Svc::MemoryPermission>(Param(system, 3)))
323 .raw;
324
325 system.CurrentArmInterface().SetReg(1, param_1);
326 FuncReturn(system, retval);
327}
328
329// Used by CreateCodeMemory
330template <Result func(Core::System&, Handle*, VAddr, size_t)>
331void SvcWrap64(Core::System& system) {
332 u32 param_1 = 0;
333 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
334
335 system.CurrentArmInterface().SetReg(1, param_1);
336 FuncReturn(system, retval);
337}
338
339template <Result func(Core::System&, Handle*, u64, u32, u32)>
340void SvcWrap64(Core::System& system) {
341 u32 param_1 = 0;
342 const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
343 static_cast<u32>(Param(system, 3)))
344 .raw;
345
346 system.CurrentArmInterface().SetReg(1, param_1);
347 FuncReturn(system, retval);
348}
349
350// Used by CreateSession
351template <Result func(Core::System&, Handle*, Handle*, u32, u64)>
352void SvcWrap64(Core::System& system) {
353 Handle param_1 = 0;
354 Handle param_2 = 0;
355 const u32 retval = func(system, &param_1, &param_2, static_cast<u32>(Param(system, 2)),
356 static_cast<u32>(Param(system, 3)))
357 .raw;
358
359 system.CurrentArmInterface().SetReg(1, param_1);
360 system.CurrentArmInterface().SetReg(2, param_2);
361 FuncReturn(system, retval);
362}
363
364// Used by ReplyAndReceive
365template <Result func(Core::System&, s32*, Handle*, s32, Handle, s64)>
366void SvcWrap64(Core::System& system) {
367 s32 param_1 = 0;
368 s32 num_handles = static_cast<s32>(Param(system, 2));
369
370 std::vector<Handle> handles(num_handles);
371 system.Memory().ReadBlock(Param(system, 1), handles.data(), num_handles * sizeof(Handle));
372
373 const u32 retval = func(system, &param_1, handles.data(), num_handles,
374 static_cast<s32>(Param(system, 3)), static_cast<s64>(Param(system, 4)))
375 .raw;
376
377 system.CurrentArmInterface().SetReg(1, param_1);
378 FuncReturn(system, retval);
379}
380
381// Used by WaitForAddress
382template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
383void SvcWrap64(Core::System& system) {
384 FuncReturn(system,
385 func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)),
386 static_cast<s32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
387 .raw);
388}
389
390// Used by SignalToAddress
391template <Result func(Core::System&, u64, Svc::SignalType, s32, s32)>
392void SvcWrap64(Core::System& system) {
393 FuncReturn(system,
394 func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)),
395 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
396 .raw);
397}
398
399////////////////////////////////////////////////////////////////////////////////////////////////////
400// Function wrappers that return type u32
401
402template <u32 func(Core::System&)>
403void SvcWrap64(Core::System& system) {
404 FuncReturn(system, func(system));
405}
406
407////////////////////////////////////////////////////////////////////////////////////////////////////
408// Function wrappers that return type u64
409
410template <u64 func(Core::System&)>
411void SvcWrap64(Core::System& system) {
412 FuncReturn(system, func(system));
413}
414
415////////////////////////////////////////////////////////////////////////////////////////////////////
416/// Function wrappers that return type void
417
418template <void func(Core::System&)>
419void SvcWrap64(Core::System& system) {
420 func(system);
421}
422
423template <void func(Core::System&, u32)>
424void SvcWrap64(Core::System& system) {
425 func(system, static_cast<u32>(Param(system, 0)));
426}
427
428template <void func(Core::System&, u32, u64, u64, u64)>
429void SvcWrap64(Core::System& system) {
430 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2),
431 Param(system, 3));
432}
433
434template <void func(Core::System&, s64)>
435void SvcWrap64(Core::System& system) {
436 func(system, static_cast<s64>(Param(system, 0)));
437}
438
439template <void func(Core::System&, u64, s32)>
440void SvcWrap64(Core::System& system) {
441 func(system, Param(system, 0), static_cast<s32>(Param(system, 1)));
442}
443
444template <void func(Core::System&, u64, u64)>
445void SvcWrap64(Core::System& system) {
446 func(system, Param(system, 0), Param(system, 1));
447}
448
449template <void func(Core::System&, u64, u64, u64)>
450void SvcWrap64(Core::System& system) {
451 func(system, Param(system, 0), Param(system, 1), Param(system, 2));
452}
453
454template <void func(Core::System&, u32, u64, u64)>
455void SvcWrap64(Core::System& system) {
456 func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), Param(system, 2));
457}
458
459// Used by QueryMemory32, ArbitrateLock32
460template <Result func(Core::System&, u32, u32, u32)>
461void SvcWrap32(Core::System& system) {
462 FuncReturn32(system,
463 func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
464}
465
466// Used by Break32
467template <void func(Core::System&, u32, u32, u32)>
468void SvcWrap32(Core::System& system) {
469 func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2));
470}
471
472// Used by ExitProcess32, ExitThread32
473template <void func(Core::System&)>
474void SvcWrap32(Core::System& system) {
475 func(system);
476}
477
478// Used by GetCurrentProcessorNumber32
479template <u32 func(Core::System&)>
480void SvcWrap32(Core::System& system) {
481 FuncReturn32(system, func(system));
482}
483
484// Used by SleepThread32
485template <void func(Core::System&, u32, u32)>
486void SvcWrap32(Core::System& system) {
487 func(system, Param32(system, 0), Param32(system, 1));
488}
489
490// Used by CreateThread32
491template <Result func(Core::System&, Handle*, u32, u32, u32, u32, s32)>
492void SvcWrap32(Core::System& system) {
493 Handle param_1 = 0;
494
495 const u32 retval = func(system, &param_1, Param32(system, 0), Param32(system, 1),
496 Param32(system, 2), Param32(system, 3), Param32(system, 4))
497 .raw;
498
499 system.CurrentArmInterface().SetReg(1, param_1);
500 FuncReturn(system, retval);
501}
502
503// Used by GetInfo32
504template <Result func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
505void SvcWrap32(Core::System& system) {
506 u32 param_1 = 0;
507 u32 param_2 = 0;
508
509 const u32 retval = func(system, &param_1, &param_2, Param32(system, 0), Param32(system, 1),
510 Param32(system, 2), Param32(system, 3))
511 .raw;
512
513 system.CurrentArmInterface().SetReg(1, param_1);
514 system.CurrentArmInterface().SetReg(2, param_2);
515 FuncReturn(system, retval);
516}
517
518// Used by GetThreadPriority32, ConnectToNamedPort32
519template <Result func(Core::System&, u32*, u32)>
520void SvcWrap32(Core::System& system) {
521 u32 param_1 = 0;
522 const u32 retval = func(system, &param_1, Param32(system, 1)).raw;
523 system.CurrentArmInterface().SetReg(1, param_1);
524 FuncReturn(system, retval);
525}
526
527// Used by GetThreadId32
528template <Result func(Core::System&, u32*, u32*, u32)>
529void SvcWrap32(Core::System& system) {
530 u32 param_1 = 0;
531 u32 param_2 = 0;
532
533 const u32 retval = func(system, &param_1, &param_2, Param32(system, 1)).raw;
534 system.CurrentArmInterface().SetReg(1, param_1);
535 system.CurrentArmInterface().SetReg(2, param_2);
536 FuncReturn(system, retval);
537}
538
539// Used by GetSystemTick32
540template <void func(Core::System&, u32*, u32*)>
541void SvcWrap32(Core::System& system) {
542 u32 param_1 = 0;
543 u32 param_2 = 0;
544
545 func(system, &param_1, &param_2);
546 system.CurrentArmInterface().SetReg(0, param_1);
547 system.CurrentArmInterface().SetReg(1, param_2);
548}
549
550// Used by CreateEvent32
551template <Result func(Core::System&, Handle*, Handle*)>
552void SvcWrap32(Core::System& system) {
553 Handle param_1 = 0;
554 Handle param_2 = 0;
555
556 const u32 retval = func(system, &param_1, &param_2).raw;
557 system.CurrentArmInterface().SetReg(1, param_1);
558 system.CurrentArmInterface().SetReg(2, param_2);
559 FuncReturn(system, retval);
560}
561
562// Used by GetThreadId32
563template <Result func(Core::System&, Handle, u32*, u32*, u32*)>
564void SvcWrap32(Core::System& system) {
565 u32 param_1 = 0;
566 u32 param_2 = 0;
567 u32 param_3 = 0;
568
569 const u32 retval = func(system, Param32(system, 2), &param_1, &param_2, &param_3).raw;
570 system.CurrentArmInterface().SetReg(1, param_1);
571 system.CurrentArmInterface().SetReg(2, param_2);
572 system.CurrentArmInterface().SetReg(3, param_3);
573 FuncReturn(system, retval);
574}
575
576// Used by GetThreadCoreMask32
577template <Result func(Core::System&, Handle, s32*, u32*, u32*)>
578void SvcWrap32(Core::System& system) {
579 s32 param_1 = 0;
580 u32 param_2 = 0;
581 u32 param_3 = 0;
582
583 const u32 retval = func(system, Param32(system, 2), &param_1, &param_2, &param_3).raw;
584 system.CurrentArmInterface().SetReg(1, param_1);
585 system.CurrentArmInterface().SetReg(2, param_2);
586 system.CurrentArmInterface().SetReg(3, param_3);
587 FuncReturn(system, retval);
588}
589
590// Used by SignalProcessWideKey32
591template <void func(Core::System&, u32, s32)>
592void SvcWrap32(Core::System& system) {
593 func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1)));
594}
595
596// Used by SetThreadActivity32
597template <Result func(Core::System&, Handle, Svc::ThreadActivity)>
598void SvcWrap32(Core::System& system) {
599 const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
600 static_cast<Svc::ThreadActivity>(Param(system, 1)))
601 .raw;
602 FuncReturn(system, retval);
603}
604
605// Used by SetThreadPriority32
606template <Result func(Core::System&, Handle, u32)>
607void SvcWrap32(Core::System& system) {
608 const u32 retval =
609 func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw;
610 FuncReturn(system, retval);
611}
612
613// Used by SetMemoryAttribute32
614template <Result func(Core::System&, Handle, u32, u32, u32)>
615void SvcWrap32(Core::System& system) {
616 const u32 retval =
617 func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
618 static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
619 .raw;
620 FuncReturn(system, retval);
621}
622
623// Used by MapSharedMemory32
624template <Result func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)>
625void SvcWrap32(Core::System& system) {
626 const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
627 static_cast<u32>(Param(system, 1)), static_cast<u32>(Param(system, 2)),
628 static_cast<Svc::MemoryPermission>(Param(system, 3)))
629 .raw;
630 FuncReturn(system, retval);
631}
632
633// Used by SetThreadCoreMask32
634template <Result func(Core::System&, Handle, s32, u32, u32)>
635void SvcWrap32(Core::System& system) {
636 const u32 retval =
637 func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)),
638 static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
639 .raw;
640 FuncReturn(system, retval);
641}
642
643// Used by WaitProcessWideKeyAtomic32
644template <Result func(Core::System&, u32, u32, Handle, u32, u32)>
645void SvcWrap32(Core::System& system) {
646 const u32 retval =
647 func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
648 static_cast<Handle>(Param(system, 2)), static_cast<u32>(Param(system, 3)),
649 static_cast<u32>(Param(system, 4)))
650 .raw;
651 FuncReturn(system, retval);
652}
653
654// Used by WaitForAddress32
655template <Result func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>
656void SvcWrap32(Core::System& system) {
657 const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
658 static_cast<Svc::ArbitrationType>(Param(system, 1)),
659 static_cast<s32>(Param(system, 2)), static_cast<u32>(Param(system, 3)),
660 static_cast<u32>(Param(system, 4)))
661 .raw;
662 FuncReturn(system, retval);
663}
664
665// Used by SignalToAddress32
666template <Result func(Core::System&, u32, Svc::SignalType, s32, s32)>
667void SvcWrap32(Core::System& system) {
668 const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
669 static_cast<Svc::SignalType>(Param(system, 1)),
670 static_cast<s32>(Param(system, 2)), static_cast<s32>(Param(system, 3)))
671 .raw;
672 FuncReturn(system, retval);
673}
674
675// Used by SendSyncRequest32, ArbitrateUnlock32
676template <Result func(Core::System&, u32)>
677void SvcWrap32(Core::System& system) {
678 FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
679}
680
681// Used by CreateTransferMemory32
682template <Result func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)>
683void SvcWrap32(Core::System& system) {
684 Handle handle = 0;
685 const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2),
686 static_cast<Svc::MemoryPermission>(Param32(system, 3)))
687 .raw;
688 system.CurrentArmInterface().SetReg(1, handle);
689 FuncReturn(system, retval);
690}
691
692// Used by WaitSynchronization32
693template <Result func(Core::System&, u32, u32, s32, u32, s32*)>
694void SvcWrap32(Core::System& system) {
695 s32 param_1 = 0;
696 const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
697 Param32(system, 3), &param_1)
698 .raw;
699 system.CurrentArmInterface().SetReg(1, param_1);
700 FuncReturn(system, retval);
701}
702
703// Used by CreateCodeMemory32
704template <Result func(Core::System&, Handle*, u32, u32)>
705void SvcWrap32(Core::System& system) {
706 Handle handle = 0;
707
708 const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw;
709
710 system.CurrentArmInterface().SetReg(1, handle);
711 FuncReturn(system, retval);
712}
713
714// Used by ControlCodeMemory32
715template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
716void SvcWrap32(Core::System& system) {
717 const u32 retval =
718 func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4),
719 static_cast<Svc::MemoryPermission>(Param32(system, 6)))
720 .raw;
721
722 FuncReturn(system, retval);
723}
724
725// Used by Invalidate/Store/FlushProcessDataCache32
726template <Result func(Core::System&, Handle, u64, u64)>
727void SvcWrap32(Core::System& system) {
728 const u64 address = (Param(system, 3) << 32) | Param(system, 2);
729 const u64 size = (Param(system, 4) << 32) | Param(system, 1);
730 FuncReturn32(system, func(system, Param32(system, 0), address, size).raw);
731}
732
733} // namespace Kernel
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index cab44bf9c..447d624e1 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -1083,7 +1083,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
1083} 1083}
1084 1084
1085void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { 1085void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) {
1086 const auto raw = ctx.ReadBuffer(); 1086 const auto raw = ctx.ReadBufferCopy();
1087 auto log = Common::StringFromFixedZeroTerminatedBuffer( 1087 auto log = Common::StringFromFixedZeroTerminatedBuffer(
1088 reinterpret_cast<const char*>(raw.data()), raw.size()); 1088 reinterpret_cast<const char*>(raw.data()), raw.size());
1089 1089
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 84edc8839..80eba22e8 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -428,6 +428,9 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
428 return; 428 return;
429 } 429 }
430 430
431 // This function is unique to yuzu for the turbo buttons to work properly
432 controller.device->TurboButtonUpdate();
433
431 auto& pad_entry = controller.npad_pad_state; 434 auto& pad_entry = controller.npad_pad_state;
432 auto& trigger_entry = controller.npad_trigger_state; 435 auto& trigger_entry = controller.npad_trigger_state;
433 const auto button_state = controller.device->GetNpadButtons(); 436 const auto button_state = controller.device->GetNpadButtons();
@@ -755,12 +758,20 @@ Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
755 return hid_core.GetSupportedStyleTag(); 758 return hid_core.GetSupportedStyleTag();
756} 759}
757 760
758void Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) { 761Result Controller_NPad::SetSupportedNpadIdTypes(std::span<const u8> data) {
762 constexpr std::size_t max_number_npad_ids = 0xa;
759 const auto length = data.size(); 763 const auto length = data.size();
760 ASSERT(length > 0 && (length % sizeof(u32)) == 0); 764 ASSERT(length > 0 && (length % sizeof(u32)) == 0);
765 const std::size_t elements = length / sizeof(u32);
766
767 if (elements > max_number_npad_ids) {
768 return InvalidArraySize;
769 }
770
761 supported_npad_id_types.clear(); 771 supported_npad_id_types.clear();
762 supported_npad_id_types.resize(length / sizeof(u32)); 772 supported_npad_id_types.resize(elements);
763 std::memcpy(supported_npad_id_types.data(), data.data(), length); 773 std::memcpy(supported_npad_id_types.data(), data.data(), length);
774 return ResultSuccess;
764} 775}
765 776
766void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { 777void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 1f7d33459..02cc00920 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -96,7 +96,7 @@ public:
96 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set); 96 void SetSupportedStyleSet(Core::HID::NpadStyleTag style_set);
97 Core::HID::NpadStyleTag GetSupportedStyleSet() const; 97 Core::HID::NpadStyleTag GetSupportedStyleSet() const;
98 98
99 void SetSupportedNpadIdTypes(std::span<const u8> data); 99 Result SetSupportedNpadIdTypes(std::span<const u8> data);
100 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length); 100 void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
101 std::size_t GetSupportedNpadIdTypesSize() const; 101 std::size_t GetSupportedNpadIdTypesSize() const;
102 102
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 76208e9a4..9585bdaf0 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -18,6 +18,7 @@ constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
18constexpr Result NpadIsSameType{ErrorModule::HID, 602}; 18constexpr Result NpadIsSameType{ErrorModule::HID, 602};
19constexpr Result InvalidNpadId{ErrorModule::HID, 709}; 19constexpr Result InvalidNpadId{ErrorModule::HID, 709};
20constexpr Result NpadNotConnected{ErrorModule::HID, 710}; 20constexpr Result NpadNotConnected{ErrorModule::HID, 710};
21constexpr Result InvalidArraySize{ErrorModule::HID, 715};
21constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302}; 22constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
22 23
23} // namespace Service::HID 24} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index f15f1a6bb..ac2c0c76d 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1025,13 +1025,13 @@ void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
1025 IPC::RequestParser rp{ctx}; 1025 IPC::RequestParser rp{ctx};
1026 const auto applet_resource_user_id{rp.Pop<u64>()}; 1026 const auto applet_resource_user_id{rp.Pop<u64>()};
1027 1027
1028 applet_resource->GetController<Controller_NPad>(HidController::NPad) 1028 const auto result = applet_resource->GetController<Controller_NPad>(HidController::NPad)
1029 .SetSupportedNpadIdTypes(ctx.ReadBuffer()); 1029 .SetSupportedNpadIdTypes(ctx.ReadBuffer());
1030 1030
1031 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 1031 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
1032 1032
1033 IPC::ResponseBuilder rb{ctx, 2}; 1033 IPC::ResponseBuilder rb{ctx, 2};
1034 rb.Push(ResultSuccess); 1034 rb.Push(result);
1035} 1035}
1036 1036
1037void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) { 1037void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 4fcfb4510..afc33db57 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -16,7 +16,7 @@ namespace InputCommon {
16 16
17Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) { 17Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
18 // Avoid conflicting with SDL driver 18 // Avoid conflicting with SDL driver
19 if (!Settings::values.enable_joycon_driver) { 19 if (!Settings::values.enable_joycon_driver && !Settings::values.enable_procon_driver) {
20 return; 20 return;
21 } 21 }
22 LOG_INFO(Input, "Joycon driver Initialization started"); 22 LOG_INFO(Input, "Joycon driver Initialization started");
@@ -46,6 +46,12 @@ void Joycons::Reset() {
46 } 46 }
47 device->Stop(); 47 device->Stop();
48 } 48 }
49 for (const auto& device : pro_controller) {
50 if (!device) {
51 continue;
52 }
53 device->Stop();
54 }
49 SDL_hid_exit(); 55 SDL_hid_exit();
50} 56}
51 57
@@ -61,6 +67,11 @@ void Joycons::Setup() {
61 PreSetController(GetIdentifier(port, Joycon::ControllerType::Right)); 67 PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
62 device = std::make_shared<Joycon::JoyconDriver>(port++); 68 device = std::make_shared<Joycon::JoyconDriver>(port++);
63 } 69 }
70 port = 0;
71 for (auto& device : pro_controller) {
72 PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro));
73 device = std::make_shared<Joycon::JoyconDriver>(port++);
74 }
64 75
65 scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); }); 76 scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
66} 77}
@@ -116,6 +127,9 @@ bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
116 // Check if device already exist 127 // Check if device already exist
117 switch (type) { 128 switch (type) {
118 case Joycon::ControllerType::Left: 129 case Joycon::ControllerType::Left:
130 if (!Settings::values.enable_joycon_driver) {
131 return false;
132 }
119 for (const auto& device : left_joycons) { 133 for (const auto& device : left_joycons) {
120 if (is_handle_identical(device)) { 134 if (is_handle_identical(device)) {
121 return false; 135 return false;
@@ -123,12 +137,25 @@ bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
123 } 137 }
124 break; 138 break;
125 case Joycon::ControllerType::Right: 139 case Joycon::ControllerType::Right:
140 if (!Settings::values.enable_joycon_driver) {
141 return false;
142 }
126 for (const auto& device : right_joycons) { 143 for (const auto& device : right_joycons) {
127 if (is_handle_identical(device)) { 144 if (is_handle_identical(device)) {
128 return false; 145 return false;
129 } 146 }
130 } 147 }
131 break; 148 break;
149 case Joycon::ControllerType::Pro:
150 if (!Settings::values.enable_procon_driver) {
151 return false;
152 }
153 for (const auto& device : pro_controller) {
154 if (is_handle_identical(device)) {
155 return false;
156 }
157 }
158 break;
132 default: 159 default:
133 return false; 160 return false;
134 } 161 }
@@ -199,6 +226,14 @@ std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
199 return *unconnected_device; 226 return *unconnected_device;
200 } 227 }
201 } 228 }
229 if (type == Joycon::ControllerType::Pro) {
230 const auto unconnected_device = std::ranges::find_if(
231 pro_controller, [](auto& device) { return !device->IsConnected(); });
232
233 if (unconnected_device != pro_controller.end()) {
234 return *unconnected_device;
235 }
236 }
202 return nullptr; 237 return nullptr;
203} 238}
204 239
@@ -409,6 +444,15 @@ std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifie
409 } 444 }
410 } 445 }
411 446
447 if (type == Joycon::ControllerType::Pro) {
448 const auto matching_device = std::ranges::find_if(
449 pro_controller, [is_handle_active](auto& device) { return is_handle_active(device); });
450
451 if (matching_device != pro_controller.end()) {
452 return *matching_device;
453 }
454 }
455
412 return nullptr; 456 return nullptr;
413} 457}
414 458
@@ -455,6 +499,9 @@ std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
455 for (const auto& controller : right_joycons) { 499 for (const auto& controller : right_joycons) {
456 add_entry(controller); 500 add_entry(controller);
457 } 501 }
502 for (const auto& controller : pro_controller) {
503 add_entry(controller);
504 }
458 505
459 // List dual joycon pairs 506 // List dual joycon pairs
460 for (std::size_t i = 0; i < MaxSupportedControllers; i++) { 507 for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index 2149ab7fd..473ba1b9e 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -106,6 +106,7 @@ private:
106 // Joycon types are split by type to ease supporting dualjoycon configurations 106 // Joycon types are split by type to ease supporting dualjoycon configurations
107 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{}; 107 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> left_joycons{};
108 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{}; 108 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> right_joycons{};
109 std::array<std::shared_ptr<Joycon::JoyconDriver>, MaxSupportedControllers> pro_controller{};
109}; 110};
110 111
111} // namespace InputCommon 112} // namespace InputCommon
diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp
index d975eb815..88cacd615 100644
--- a/src/input_common/drivers/sdl_driver.cpp
+++ b/src/input_common/drivers/sdl_driver.cpp
@@ -343,6 +343,14 @@ void SDLDriver::InitJoystick(int joystick_index) {
343 } 343 }
344 } 344 }
345 345
346 if (Settings::values.enable_procon_driver) {
347 if (guid.uuid[5] == 0x05 && guid.uuid[4] == 0x7e && guid.uuid[8] == 0x09) {
348 LOG_WARNING(Input, "Preferring joycon driver for device index {}", joystick_index);
349 SDL_JoystickClose(sdl_joystick);
350 return;
351 }
352 }
353
346 std::scoped_lock lock{joystick_map_mutex}; 354 std::scoped_lock lock{joystick_map_mutex};
347 if (joystick_map.find(guid) == joystick_map.end()) { 355 if (joystick_map.find(guid) == joystick_map.end()) {
348 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller); 356 auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
@@ -465,13 +473,19 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en
465 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1"); 473 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
466 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); 474 SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
467 475
468 // Disable hidapi drivers for switch controllers when the custom joycon driver is enabled 476 // Disable hidapi drivers for joycon controllers when the custom joycon driver is enabled
469 if (Settings::values.enable_joycon_driver) { 477 if (Settings::values.enable_joycon_driver) {
470 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0"); 478 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "0");
471 } else { 479 } else {
472 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); 480 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
473 } 481 }
474 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1"); 482
483 // Disable hidapi drivers for pro controllers when the custom joycon driver is enabled
484 if (Settings::values.enable_procon_driver) {
485 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "0");
486 } else {
487 SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
488 }
475 489
476 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native 490 // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native
477 // driver on Linux. 491 // driver on Linux.
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index 8f94c9f45..e65b6b845 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -543,9 +543,10 @@ void JoyconDriver::SetCallbacks(const JoyconCallbacks& callbacks) {
543 543
544DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info, 544DriverResult JoyconDriver::GetDeviceType(SDL_hid_device_info* device_info,
545 ControllerType& controller_type) { 545 ControllerType& controller_type) {
546 static constexpr std::array<std::pair<u32, ControllerType>, 2> supported_devices{ 546 static constexpr std::array<std::pair<u32, ControllerType>, 6> supported_devices{
547 std::pair<u32, ControllerType>{0x2006, ControllerType::Left}, 547 std::pair<u32, ControllerType>{0x2006, ControllerType::Left},
548 {0x2007, ControllerType::Right}, 548 {0x2007, ControllerType::Right},
549 {0x2009, ControllerType::Pro},
549 }; 550 };
550 constexpr u16 nintendo_vendor_id = 0x057e; 551 constexpr u16 nintendo_vendor_id = 0x057e;
551 552
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index 096c23b07..a6be6dac1 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -15,6 +15,9 @@ public:
15 // do not play nicely with the theoretical maximum range. 15 // do not play nicely with the theoretical maximum range.
16 // Using a value one lower from the maximum emulates real stick behavior. 16 // Using a value one lower from the maximum emulates real stick behavior.
17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f; 17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
18 static constexpr float TAU = Common::PI * 2.0f;
19 // Use wider angle to ease the transition.
20 static constexpr float APERTURE = TAU * 0.15f;
18 21
19 using Button = std::unique_ptr<Common::Input::InputDevice>; 22 using Button = std::unique_ptr<Common::Input::InputDevice>;
20 23
@@ -61,30 +64,23 @@ public:
61 } 64 }
62 65
63 bool IsAngleGreater(float old_angle, float new_angle) const { 66 bool IsAngleGreater(float old_angle, float new_angle) const {
64 constexpr float TAU = Common::PI * 2.0f; 67 const float top_limit = new_angle + APERTURE;
65 // Use wider angle to ease the transition.
66 constexpr float aperture = TAU * 0.15f;
67 const float top_limit = new_angle + aperture;
68 return (old_angle > new_angle && old_angle <= top_limit) || 68 return (old_angle > new_angle && old_angle <= top_limit) ||
69 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); 69 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
70 } 70 }
71 71
72 bool IsAngleSmaller(float old_angle, float new_angle) const { 72 bool IsAngleSmaller(float old_angle, float new_angle) const {
73 constexpr float TAU = Common::PI * 2.0f; 73 const float bottom_limit = new_angle - APERTURE;
74 // Use wider angle to ease the transition.
75 constexpr float aperture = TAU * 0.15f;
76 const float bottom_limit = new_angle - aperture;
77 return (old_angle >= bottom_limit && old_angle < new_angle) || 74 return (old_angle >= bottom_limit && old_angle < new_angle) ||
78 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); 75 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
79 } 76 }
80 77
81 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { 78 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
82 constexpr float TAU = Common::PI * 2.0f;
83 float new_angle = angle; 79 float new_angle = angle;
84 80
85 auto time_difference = static_cast<float>( 81 auto time_difference = static_cast<float>(
86 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); 82 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
87 time_difference /= 1000.0f * 1000.0f; 83 time_difference /= 1000.0f;
88 if (time_difference > 0.5f) { 84 if (time_difference > 0.5f) {
89 time_difference = 0.5f; 85 time_difference = 0.5f;
90 } 86 }
@@ -201,8 +197,6 @@ public:
201 } 197 }
202 198
203 void UpdateStatus() { 199 void UpdateStatus() {
204 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
205
206 bool r = right_status; 200 bool r = right_status;
207 bool l = left_status; 201 bool l = left_status;
208 bool u = up_status; 202 bool u = up_status;
@@ -220,7 +214,7 @@ public:
220 214
221 // Move if a key is pressed 215 // Move if a key is pressed
222 if (r || l || u || d) { 216 if (r || l || u || d) {
223 amplitude = coef; 217 amplitude = modifier_status.value ? modifier_scale : MAX_RANGE;
224 } else { 218 } else {
225 amplitude = 0; 219 amplitude = 0;
226 } 220 }
@@ -274,30 +268,17 @@ public:
274 Common::Input::StickStatus status{}; 268 Common::Input::StickStatus status{};
275 status.x.properties = properties; 269 status.x.properties = properties;
276 status.y.properties = properties; 270 status.y.properties = properties;
271
277 if (Settings::values.emulate_analog_keyboard) { 272 if (Settings::values.emulate_analog_keyboard) {
278 const auto now = std::chrono::steady_clock::now(); 273 const auto now = std::chrono::steady_clock::now();
279 float angle_ = GetAngle(now); 274 const float angle_ = GetAngle(now);
280 status.x.raw_value = std::cos(angle_) * amplitude; 275 status.x.raw_value = std::cos(angle_) * amplitude;
281 status.y.raw_value = std::sin(angle_) * amplitude; 276 status.y.raw_value = std::sin(angle_) * amplitude;
282 return status; 277 return status;
283 } 278 }
284 constexpr float SQRT_HALF = 0.707106781f; 279
285 int x = 0, y = 0; 280 status.x.raw_value = std::cos(goal_angle) * amplitude;
286 if (right_status) { 281 status.y.raw_value = std::sin(goal_angle) * amplitude;
287 ++x;
288 }
289 if (left_status) {
290 --x;
291 }
292 if (up_status) {
293 ++y;
294 }
295 if (down_status) {
296 --y;
297 }
298 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
299 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
300 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
301 return status; 282 return status;
302 } 283 }
303 284
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 15cbf7e5f..8c6a6521a 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -16,10 +16,10 @@ public:
16 16
17class InputFromButton final : public Common::Input::InputDevice { 17class InputFromButton final : public Common::Input::InputDevice {
18public: 18public:
19 explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, 19 explicit InputFromButton(PadIdentifier identifier_, int button_, bool turbo_, bool toggle_,
20 InputEngine* input_engine_) 20 bool inverted_, InputEngine* input_engine_)
21 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), 21 : identifier(identifier_), button(button_), turbo(turbo_), toggle(toggle_),
22 input_engine(input_engine_) { 22 inverted(inverted_), input_engine(input_engine_) {
23 UpdateCallback engine_callback{[this]() { OnChange(); }}; 23 UpdateCallback engine_callback{[this]() { OnChange(); }};
24 const InputIdentifier input_identifier{ 24 const InputIdentifier input_identifier{
25 .identifier = identifier, 25 .identifier = identifier,
@@ -40,6 +40,7 @@ public:
40 .value = input_engine->GetButton(identifier, button), 40 .value = input_engine->GetButton(identifier, button),
41 .inverted = inverted, 41 .inverted = inverted,
42 .toggle = toggle, 42 .toggle = toggle,
43 .turbo = turbo,
43 }; 44 };
44 } 45 }
45 46
@@ -68,6 +69,7 @@ public:
68private: 69private:
69 const PadIdentifier identifier; 70 const PadIdentifier identifier;
70 const int button; 71 const int button;
72 const bool turbo;
71 const bool toggle; 73 const bool toggle;
72 const bool inverted; 74 const bool inverted;
73 int callback_key; 75 int callback_key;
@@ -77,10 +79,10 @@ private:
77 79
78class InputFromHatButton final : public Common::Input::InputDevice { 80class InputFromHatButton final : public Common::Input::InputDevice {
79public: 81public:
80 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_, 82 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool turbo_,
81 bool inverted_, InputEngine* input_engine_) 83 bool toggle_, bool inverted_, InputEngine* input_engine_)
82 : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_), 84 : identifier(identifier_), button(button_), direction(direction_), turbo(turbo_),
83 inverted(inverted_), input_engine(input_engine_) { 85 toggle(toggle_), inverted(inverted_), input_engine(input_engine_) {
84 UpdateCallback engine_callback{[this]() { OnChange(); }}; 86 UpdateCallback engine_callback{[this]() { OnChange(); }};
85 const InputIdentifier input_identifier{ 87 const InputIdentifier input_identifier{
86 .identifier = identifier, 88 .identifier = identifier,
@@ -101,6 +103,7 @@ public:
101 .value = input_engine->GetHatButton(identifier, button, direction), 103 .value = input_engine->GetHatButton(identifier, button, direction),
102 .inverted = inverted, 104 .inverted = inverted,
103 .toggle = toggle, 105 .toggle = toggle,
106 .turbo = turbo,
104 }; 107 };
105 } 108 }
106 109
@@ -130,6 +133,7 @@ private:
130 const PadIdentifier identifier; 133 const PadIdentifier identifier;
131 const int button; 134 const int button;
132 const u8 direction; 135 const u8 direction;
136 const bool turbo;
133 const bool toggle; 137 const bool toggle;
134 const bool inverted; 138 const bool inverted;
135 int callback_key; 139 int callback_key;
@@ -853,14 +857,15 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
853 const auto keyboard_key = params.Get("code", 0); 857 const auto keyboard_key = params.Get("code", 0);
854 const auto toggle = params.Get("toggle", false) != 0; 858 const auto toggle = params.Get("toggle", false) != 0;
855 const auto inverted = params.Get("inverted", false) != 0; 859 const auto inverted = params.Get("inverted", false) != 0;
860 const auto turbo = params.Get("turbo", false) != 0;
856 input_engine->PreSetController(identifier); 861 input_engine->PreSetController(identifier);
857 input_engine->PreSetButton(identifier, button_id); 862 input_engine->PreSetButton(identifier, button_id);
858 input_engine->PreSetButton(identifier, keyboard_key); 863 input_engine->PreSetButton(identifier, keyboard_key);
859 if (keyboard_key != 0) { 864 if (keyboard_key != 0) {
860 return std::make_unique<InputFromButton>(identifier, keyboard_key, toggle, inverted, 865 return std::make_unique<InputFromButton>(identifier, keyboard_key, turbo, toggle, inverted,
861 input_engine.get()); 866 input_engine.get());
862 } 867 }
863 return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted, 868 return std::make_unique<InputFromButton>(identifier, button_id, turbo, toggle, inverted,
864 input_engine.get()); 869 input_engine.get());
865} 870}
866 871
@@ -876,11 +881,12 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
876 const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); 881 const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
877 const auto toggle = params.Get("toggle", false) != 0; 882 const auto toggle = params.Get("toggle", false) != 0;
878 const auto inverted = params.Get("inverted", false) != 0; 883 const auto inverted = params.Get("inverted", false) != 0;
884 const auto turbo = params.Get("turbo", false) != 0;
879 885
880 input_engine->PreSetController(identifier); 886 input_engine->PreSetController(identifier);
881 input_engine->PreSetHatButton(identifier, button_id); 887 input_engine->PreSetHatButton(identifier, button_id);
882 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted, 888 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, turbo, toggle,
883 input_engine.get()); 889 inverted, input_engine.get());
884} 890}
885 891
886std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice( 892std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index b7bc11416..85ee27333 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -59,6 +59,13 @@ std::string Image(EmitContext& ctx, IR::TextureInstInfo info,
59 } 59 }
60} 60}
61 61
62bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
63 if (info.type == TextureType::Buffer) {
64 return false;
65 }
66 return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
67}
68
62std::string_view TextureType(IR::TextureInstInfo info, bool is_ms = false) { 69std::string_view TextureType(IR::TextureInstInfo info, bool is_ms = false) {
63 if (info.is_depth) { 70 if (info.is_depth) {
64 switch (info.type) { 71 switch (info.type) {
@@ -535,7 +542,8 @@ void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value&
535 ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) { 542 ScalarS32 lod, [[maybe_unused]] const IR::Value& skip_mips) {
536 const auto info{inst.Flags<IR::TextureInstInfo>()}; 543 const auto info{inst.Flags<IR::TextureInstInfo>()};
537 const std::string texture{Texture(ctx, info, index)}; 544 const std::string texture{Texture(ctx, info, index)};
538 const std::string_view type{TextureType(info)}; 545 const bool is_msaa{IsTextureMsaa(ctx, info)};
546 const std::string_view type{TextureType(info, is_msaa)};
539 ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type); 547 ctx.Add("TXQ {},{},{},{};", inst, lod, texture, type);
540} 548}
541 549
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index 4be2c25ec..f335c8af0 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -25,6 +25,13 @@ std::string Image(EmitContext& ctx, const IR::TextureInstInfo& info, const IR::V
25 return fmt::format("img{}{}", def.binding, index_offset); 25 return fmt::format("img{}{}", def.binding, index_offset);
26} 26}
27 27
28bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
29 if (info.type == TextureType::Buffer) {
30 return false;
31 }
32 return ctx.info.texture_descriptors.at(info.descriptor_index).is_multisample;
33}
34
28std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) { 35std::string CastToIntVec(std::string_view value, const IR::TextureInstInfo& info) {
29 switch (info.type) { 36 switch (info.type) {
30 case TextureType::Color1D: 37 case TextureType::Color1D:
@@ -463,26 +470,33 @@ void EmitImageQueryDimensions(EmitContext& ctx, IR::Inst& inst, const IR::Value&
463 std::string_view lod, const IR::Value& skip_mips_val) { 470 std::string_view lod, const IR::Value& skip_mips_val) {
464 const auto info{inst.Flags<IR::TextureInstInfo>()}; 471 const auto info{inst.Flags<IR::TextureInstInfo>()};
465 const auto texture{Texture(ctx, info, index)}; 472 const auto texture{Texture(ctx, info, index)};
473 const bool is_msaa{IsTextureMsaa(ctx, info)};
466 const bool skip_mips{skip_mips_val.U1()}; 474 const bool skip_mips{skip_mips_val.U1()};
467 const auto mips{ 475 const auto mips{skip_mips ? "0u" : fmt::format("uint(textureQueryLevels({}))", texture)};
468 [&] { return skip_mips ? "0u" : fmt::format("uint(textureQueryLevels({}))", texture); }}; 476 if (is_msaa && !skip_mips) {
477 throw NotImplementedException("EmitImageQueryDimensions MSAA QueryLevels");
478 }
479 if (info.type == TextureType::Buffer && !skip_mips) {
480 throw NotImplementedException("EmitImageQueryDimensions TextureType::Buffer QueryLevels");
481 }
482 const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
483 const auto lod_str{uses_lod ? fmt::format(",int({})", lod) : ""};
469 switch (info.type) { 484 switch (info.type) {
470 case TextureType::Color1D: 485 case TextureType::Color1D:
471 return ctx.AddU32x4("{}=uvec4(uint(textureSize({},int({}))),0u,0u,{});", inst, texture, lod, 486 return ctx.AddU32x4("{}=uvec4(uint(textureSize({}{})),0u,0u,{});", inst, texture, lod_str,
472 mips()); 487 mips);
473 case TextureType::ColorArray1D: 488 case TextureType::ColorArray1D:
474 case TextureType::Color2D: 489 case TextureType::Color2D:
475 case TextureType::ColorCube: 490 case TextureType::ColorCube:
476 case TextureType::Color2DRect: 491 case TextureType::Color2DRect:
477 return ctx.AddU32x4("{}=uvec4(uvec2(textureSize({},int({}))),0u,{});", inst, texture, lod, 492 return ctx.AddU32x4("{}=uvec4(uvec2(textureSize({}{})),0u,{});", inst, texture, lod_str,
478 mips()); 493 mips);
479 case TextureType::ColorArray2D: 494 case TextureType::ColorArray2D:
480 case TextureType::Color3D: 495 case TextureType::Color3D:
481 case TextureType::ColorArrayCube: 496 case TextureType::ColorArrayCube:
482 return ctx.AddU32x4("{}=uvec4(uvec3(textureSize({},int({}))),{});", inst, texture, lod, 497 return ctx.AddU32x4("{}=uvec4(uvec3(textureSize({}{})),{});", inst, texture, lod_str, mips);
483 mips());
484 case TextureType::Buffer: 498 case TextureType::Buffer:
485 throw NotImplementedException("EmitImageQueryDimensions Texture buffers"); 499 return ctx.AddU32x4("{}=uvec4(uint(textureSize({})),0u,0u,{});", inst, texture, mips);
486 } 500 }
487 throw LogicError("Unspecified image type {}", info.type.Value()); 501 throw LogicError("Unspecified image type {}", info.type.Value());
488} 502}
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index 1b006e811..c3c2281bb 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -310,12 +310,6 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
310 if (runtime_info.force_early_z) { 310 if (runtime_info.force_early_z) {
311 header += "layout(early_fragment_tests)in;"; 311 header += "layout(early_fragment_tests)in;";
312 } 312 }
313 if (info.uses_sample_id) {
314 header += "in int gl_SampleID;";
315 }
316 if (info.stores_sample_mask) {
317 header += "out int gl_SampleMask[];";
318 }
319 break; 313 break;
320 case Stage::Compute: 314 case Stage::Compute:
321 stage_name = "cs"; 315 stage_name = "cs";
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 3b969d915..02073c420 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -201,6 +201,13 @@ Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
201 } 201 }
202} 202}
203 203
204bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
205 if (info.type == TextureType::Buffer) {
206 return false;
207 }
208 return ctx.textures.at(info.descriptor_index).is_multisample;
209}
210
204Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { 211Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
205 const auto info{inst->Flags<IR::TextureInstInfo>()}; 212 const auto info{inst->Flags<IR::TextureInstInfo>()};
206 if (info.relaxed_precision != 0) { 213 if (info.relaxed_precision != 0) {
@@ -452,24 +459,26 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
452 const Id zero{ctx.u32_zero_value}; 459 const Id zero{ctx.u32_zero_value};
453 const bool skip_mips{skip_mips_val.U1()}; 460 const bool skip_mips{skip_mips_val.U1()};
454 const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }}; 461 const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
462 const bool is_msaa{IsTextureMsaa(ctx, info)};
463 const bool uses_lod{!is_msaa && info.type != TextureType::Buffer};
464 const auto query{[&](Id type) {
465 return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
466 : ctx.OpImageQuerySize(type, image);
467 }};
455 switch (info.type) { 468 switch (info.type) {
456 case TextureType::Color1D: 469 case TextureType::Color1D:
457 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[1], image, lod), 470 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
458 zero, zero, mips());
459 case TextureType::ColorArray1D: 471 case TextureType::ColorArray1D:
460 case TextureType::Color2D: 472 case TextureType::Color2D:
461 case TextureType::ColorCube: 473 case TextureType::ColorCube:
462 case TextureType::Color2DRect: 474 case TextureType::Color2DRect:
463 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[2], image, lod), 475 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips());
464 zero, mips());
465 case TextureType::ColorArray2D: 476 case TextureType::ColorArray2D:
466 case TextureType::Color3D: 477 case TextureType::Color3D:
467 case TextureType::ColorArrayCube: 478 case TextureType::ColorArrayCube:
468 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySizeLod(ctx.U32[3], image, lod), 479 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips());
469 mips());
470 case TextureType::Buffer: 480 case TextureType::Buffer:
471 return ctx.OpCompositeConstruct(ctx.U32[4], ctx.OpImageQuerySize(ctx.U32[1], image), zero, 481 return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
472 zero, mips());
473 } 482 }
474 throw LogicError("Unspecified image type {}", info.type.Value()); 483 throw LogicError("Unspecified image type {}", info.type.Value());
475} 484}
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 3b97721e1..d48d4860e 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -1288,6 +1288,7 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
1288 .pointer_type = pointer_type, 1288 .pointer_type = pointer_type,
1289 .image_type = image_type, 1289 .image_type = image_type,
1290 .count = desc.count, 1290 .count = desc.count,
1291 .is_multisample = desc.is_multisample,
1291 }); 1292 });
1292 if (profile.supported_spirv >= 0x00010400) { 1293 if (profile.supported_spirv >= 0x00010400) {
1293 interfaces.push_back(id); 1294 interfaces.push_back(id);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index dbc5c55b9..768a4fbb5 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -35,6 +35,7 @@ struct TextureDefinition {
35 Id pointer_type; 35 Id pointer_type;
36 Id image_type; 36 Id image_type;
37 u32 count; 37 u32 count;
38 bool is_multisample;
38}; 39};
39 40
40struct TextureBufferDefinition { 41struct TextureBufferDefinition {
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 22e89dd1b..c27546b0e 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -43,7 +43,6 @@ public:
43 explicit Value(u8 value) noexcept; 43 explicit Value(u8 value) noexcept;
44 explicit Value(u16 value) noexcept; 44 explicit Value(u16 value) noexcept;
45 explicit Value(u32 value) noexcept; 45 explicit Value(u32 value) noexcept;
46 explicit Value(s32 value) noexcept;
47 explicit Value(f32 value) noexcept; 46 explicit Value(f32 value) noexcept;
48 explicit Value(u64 value) noexcept; 47 explicit Value(u64 value) noexcept;
49 explicit Value(f64 value) noexcept; 48 explicit Value(f64 value) noexcept;
@@ -66,7 +65,6 @@ public:
66 [[nodiscard]] u8 U8() const; 65 [[nodiscard]] u8 U8() const;
67 [[nodiscard]] u16 U16() const; 66 [[nodiscard]] u16 U16() const;
68 [[nodiscard]] u32 U32() const; 67 [[nodiscard]] u32 U32() const;
69 [[nodiscard]] s32 S32() const;
70 [[nodiscard]] f32 F32() const; 68 [[nodiscard]] f32 F32() const;
71 [[nodiscard]] u64 U64() const; 69 [[nodiscard]] u64 U64() const;
72 [[nodiscard]] f64 F64() const; 70 [[nodiscard]] f64 F64() const;
@@ -86,7 +84,6 @@ private:
86 u8 imm_u8; 84 u8 imm_u8;
87 u16 imm_u16; 85 u16 imm_u16;
88 u32 imm_u32; 86 u32 imm_u32;
89 s32 imm_s32;
90 f32 imm_f32; 87 f32 imm_f32;
91 u64 imm_u64; 88 u64 imm_u64;
92 f64 imm_f64; 89 f64 imm_f64;
@@ -378,14 +375,6 @@ inline u32 Value::U32() const {
378 return imm_u32; 375 return imm_u32;
379} 376}
380 377
381inline s32 Value::S32() const {
382 if (IsIdentity()) {
383 return inst->Arg(0).S32();
384 }
385 DEBUG_ASSERT(type == Type::S32);
386 return imm_s32;
387}
388
389inline f32 Value::F32() const { 378inline f32 Value::F32() const {
390 if (IsIdentity()) { 379 if (IsIdentity()) {
391 return inst->Arg(0).F32(); 380 return inst->Arg(0).F32();
diff --git a/src/tests/video_core/buffer_base.cpp b/src/tests/video_core/buffer_base.cpp
index 1275cca24..734dbf4b6 100644
--- a/src/tests/video_core/buffer_base.cpp
+++ b/src/tests/video_core/buffer_base.cpp
@@ -538,7 +538,7 @@ TEST_CASE("BufferBase: Cached write downloads") {
538 int num = 0; 538 int num = 0;
539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; }); 539 buffer.ForEachDownloadRangeAndClear(c, WORD, [&](u64 offset, u64 size) { ++num; });
540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; }); 540 buffer.ForEachUploadRange(c, WORD, [&](u64 offset, u64 size) { ++num; });
541 REQUIRE(num == 1); 541 REQUIRE(num == 0);
542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE)); 542 REQUIRE(!buffer.IsRegionCpuModified(c + PAGE, PAGE));
543 REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE)); 543 REQUIRE(!buffer.IsRegionGpuModified(c + PAGE, PAGE));
544 buffer.FlushCachedWrites(); 544 buffer.FlushCachedWrites();
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index c47b7d866..92d77eef2 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -430,7 +430,7 @@ private:
430 if (query_begin >= SizeBytes() || size < 0) { 430 if (query_begin >= SizeBytes() || size < 0) {
431 return; 431 return;
432 } 432 }
433 [[maybe_unused]] u64* const untracked_words = Array<Type::Untracked>(); 433 u64* const untracked_words = Array<Type::Untracked>();
434 u64* const state_words = Array<type>(); 434 u64* const state_words = Array<type>();
435 const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes()); 435 const u64 query_end = query_begin + std::min(static_cast<u64>(size), SizeBytes());
436 u64* const words_begin = state_words + query_begin / BYTES_PER_WORD; 436 u64* const words_begin = state_words + query_begin / BYTES_PER_WORD;
@@ -483,7 +483,7 @@ private:
483 NotifyRasterizer<true>(word_index, current_bits, ~u64{0}); 483 NotifyRasterizer<true>(word_index, current_bits, ~u64{0});
484 } 484 }
485 // Exclude CPU modified pages when visiting GPU pages 485 // Exclude CPU modified pages when visiting GPU pages
486 const u64 word = current_word; 486 const u64 word = current_word & ~(type == Type::GPU ? untracked_words[word_index] : 0);
487 u64 page = page_begin; 487 u64 page = page_begin;
488 page_begin = 0; 488 page_begin = 0;
489 489
@@ -531,7 +531,7 @@ private:
531 [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept { 531 [[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
532 static_assert(type != Type::Untracked); 532 static_assert(type != Type::Untracked);
533 533
534 [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>(); 534 const u64* const untracked_words = Array<Type::Untracked>();
535 const u64* const state_words = Array<type>(); 535 const u64* const state_words = Array<type>();
536 const u64 num_query_words = size / BYTES_PER_WORD + 1; 536 const u64 num_query_words = size / BYTES_PER_WORD + 1;
537 const u64 word_begin = offset / BYTES_PER_WORD; 537 const u64 word_begin = offset / BYTES_PER_WORD;
@@ -539,7 +539,8 @@ private:
539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); 539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; 540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { 541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
542 const u64 word = state_words[word_index]; 542 const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0;
543 const u64 word = state_words[word_index] & ~off_word;
543 if (word == 0) { 544 if (word == 0) {
544 continue; 545 continue;
545 } 546 }
@@ -563,7 +564,7 @@ private:
563 [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept { 564 [[nodiscard]] std::pair<u64, u64> ModifiedRegion(u64 offset, u64 size) const noexcept {
564 static_assert(type != Type::Untracked); 565 static_assert(type != Type::Untracked);
565 566
566 [[maybe_unused]] const u64* const untracked_words = Array<Type::Untracked>(); 567 const u64* const untracked_words = Array<Type::Untracked>();
567 const u64* const state_words = Array<type>(); 568 const u64* const state_words = Array<type>();
568 const u64 num_query_words = size / BYTES_PER_WORD + 1; 569 const u64 num_query_words = size / BYTES_PER_WORD + 1;
569 const u64 word_begin = offset / BYTES_PER_WORD; 570 const u64 word_begin = offset / BYTES_PER_WORD;
@@ -573,7 +574,8 @@ private:
573 u64 begin = std::numeric_limits<u64>::max(); 574 u64 begin = std::numeric_limits<u64>::max();
574 u64 end = 0; 575 u64 end = 0;
575 for (u64 word_index = word_begin; word_index < word_end; ++word_index) { 576 for (u64 word_index = word_begin; word_index < word_end; ++word_index) {
576 const u64 word = state_words[word_index]; 577 const u64 off_word = type == Type::GPU ? untracked_words[word_index] : 0;
578 const u64 word = state_words[word_index] & ~off_word;
577 if (word == 0) { 579 if (word == 0) {
578 continue; 580 continue;
579 } 581 }
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index c6d54be63..7024a19cf 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -99,7 +99,7 @@ struct GPU::Impl {
99 99
100 /// Signal the ending of command list. 100 /// Signal the ending of command list.
101 void OnCommandListEnd() { 101 void OnCommandListEnd() {
102 gpu_thread.OnCommandListEnd(); 102 rasterizer->ReleaseFences();
103 } 103 }
104 104
105 /// Request a host GPU memory flush from the CPU. 105 /// Request a host GPU memory flush from the CPU.
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 164a5252a..9c103c0d4 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -40,8 +40,6 @@ static void RunThread(std::stop_token stop_token, Core::System& system,
40 scheduler.Push(submit_list->channel, std::move(submit_list->entries)); 40 scheduler.Push(submit_list->channel, std::move(submit_list->entries));
41 } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) { 41 } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) {
42 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); 42 renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
43 } else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
44 rasterizer->ReleaseFences();
45 } else if (std::holds_alternative<GPUTickCommand>(next.data)) { 43 } else if (std::holds_alternative<GPUTickCommand>(next.data)) {
46 system.GPU().TickWork(); 44 system.GPU().TickWork();
47 } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { 45 } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) {
@@ -110,10 +108,6 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
110 rasterizer->OnCPUWrite(addr, size); 108 rasterizer->OnCPUWrite(addr, size);
111} 109}
112 110
113void ThreadManager::OnCommandListEnd() {
114 PushCommand(OnCommandListEndCommand());
115}
116
117u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) { 111u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
118 if (!is_async) { 112 if (!is_async) {
119 // In synchronous GPU mode, block the caller until the command has executed 113 // In synchronous GPU mode, block the caller until the command has executed
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index c71a419c7..90bcb5958 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -77,16 +77,12 @@ struct FlushAndInvalidateRegionCommand final {
77 u64 size; 77 u64 size;
78}; 78};
79 79
80/// Command called within the gpu, to schedule actions after a command list end
81struct OnCommandListEndCommand final {};
82
83/// Command to make the gpu look into pending requests 80/// Command to make the gpu look into pending requests
84struct GPUTickCommand final {}; 81struct GPUTickCommand final {};
85 82
86using CommandData = 83using CommandData =
87 std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 84 std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
88 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand, 85 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, GPUTickCommand>;
89 GPUTickCommand>;
90 86
91struct CommandDataContainer { 87struct CommandDataContainer {
92 CommandDataContainer() = default; 88 CommandDataContainer() = default;
@@ -134,8 +130,6 @@ public:
134 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated 130 /// Notify rasterizer that any caches of the specified region should be flushed and invalidated
135 void FlushAndInvalidateRegion(VAddr addr, u64 size); 131 void FlushAndInvalidateRegion(VAddr addr, u64 size);
136 132
137 void OnCommandListEnd();
138
139 void TickGPU(); 133 void TickGPU();
140 134
141private: 135private:
diff --git a/src/video_core/host1x/vic.cpp b/src/video_core/host1x/vic.cpp
index 36a04e4e0..10d7ef884 100644
--- a/src/video_core/host1x/vic.cpp
+++ b/src/video_core/host1x/vic.cpp
@@ -189,9 +189,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
189 for (std::size_t y = 0; y < frame_height; ++y) { 189 for (std::size_t y = 0; y < frame_height; ++y) {
190 const std::size_t src = y * stride; 190 const std::size_t src = y * stride;
191 const std::size_t dst = y * aligned_width; 191 const std::size_t dst = y * aligned_width;
192 for (std::size_t x = 0; x < frame_width; ++x) { 192 std::memcpy(luma_buffer.data() + dst, luma_src + src, frame_width);
193 luma_buffer[dst + x] = luma_src[src + x];
194 }
195 } 193 }
196 host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(), 194 host1x.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(),
197 luma_buffer.size()); 195 luma_buffer.size());
@@ -205,15 +203,15 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
205 // Frame from FFmpeg software 203 // Frame from FFmpeg software
206 // Populate chroma buffer from both channels with interleaving. 204 // Populate chroma buffer from both channels with interleaving.
207 const std::size_t half_width = frame_width / 2; 205 const std::size_t half_width = frame_width / 2;
206 u8* chroma_buffer_data = chroma_buffer.data();
208 const u8* chroma_b_src = frame->data[1]; 207 const u8* chroma_b_src = frame->data[1];
209 const u8* chroma_r_src = frame->data[2]; 208 const u8* chroma_r_src = frame->data[2];
210 for (std::size_t y = 0; y < half_height; ++y) { 209 for (std::size_t y = 0; y < half_height; ++y) {
211 const std::size_t src = y * half_stride; 210 const std::size_t src = y * half_stride;
212 const std::size_t dst = y * aligned_width; 211 const std::size_t dst = y * aligned_width;
213
214 for (std::size_t x = 0; x < half_width; ++x) { 212 for (std::size_t x = 0; x < half_width; ++x) {
215 chroma_buffer[dst + x * 2] = chroma_b_src[src + x]; 213 chroma_buffer_data[dst + x * 2] = chroma_b_src[src + x];
216 chroma_buffer[dst + x * 2 + 1] = chroma_r_src[src + x]; 214 chroma_buffer_data[dst + x * 2 + 1] = chroma_r_src[src + x];
217 } 215 }
218 } 216 }
219 break; 217 break;
@@ -225,9 +223,7 @@ void Vic::WriteYUVFrame(const AVFrame* frame, const VicConfig& config) {
225 for (std::size_t y = 0; y < half_height; ++y) { 223 for (std::size_t y = 0; y < half_height; ++y) {
226 const std::size_t src = y * stride; 224 const std::size_t src = y * stride;
227 const std::size_t dst = y * aligned_width; 225 const std::size_t dst = y * aligned_width;
228 for (std::size_t x = 0; x < frame_width; ++x) { 226 std::memcpy(chroma_buffer.data() + dst, chroma_src + src, frame_width);
229 chroma_buffer[dst + x] = chroma_src[src + x];
230 }
231 } 227 }
232 break; 228 break;
233 } 229 }
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 26d066004..1a0cea9b7 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -30,7 +30,7 @@ bool ComputePipelineKey::operator==(const ComputePipelineKey& rhs) const noexcep
30ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_, 30ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cache_,
31 BufferCache& buffer_cache_, ProgramManager& program_manager_, 31 BufferCache& buffer_cache_, ProgramManager& program_manager_,
32 const Shader::Info& info_, std::string code, 32 const Shader::Info& info_, std::string code,
33 std::vector<u32> code_v) 33 std::vector<u32> code_v, bool force_context_flush)
34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, 34 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_},
35 program_manager{program_manager_}, info{info_} { 35 program_manager{program_manager_}, info{info_} {
36 switch (device.GetShaderBackend()) { 36 switch (device.GetShaderBackend()) {
@@ -63,6 +63,15 @@ ComputePipeline::ComputePipeline(const Device& device, TextureCache& texture_cac
63 writes_global_memory = !use_storage_buffers && 63 writes_global_memory = !use_storage_buffers &&
64 std::ranges::any_of(info.storage_buffers_descriptors, 64 std::ranges::any_of(info.storage_buffers_descriptors,
65 [](const auto& desc) { return desc.is_written; }); 65 [](const auto& desc) { return desc.is_written; });
66 if (force_context_flush) {
67 std::scoped_lock lock{built_mutex};
68 built_fence.Create();
69 // Flush this context to ensure compilation commands and fence are in the GPU pipe.
70 glFlush();
71 built_condvar.notify_one();
72 } else {
73 is_built = true;
74 }
66} 75}
67 76
68void ComputePipeline::Configure() { 77void ComputePipeline::Configure() {
@@ -142,6 +151,9 @@ void ComputePipeline::Configure() {
142 } 151 }
143 texture_cache.FillComputeImageViews(std::span(views.data(), views.size())); 152 texture_cache.FillComputeImageViews(std::span(views.data(), views.size()));
144 153
154 if (!is_built) {
155 WaitForBuild();
156 }
145 if (assembly_program.handle != 0) { 157 if (assembly_program.handle != 0) {
146 program_manager.BindComputeAssemblyProgram(assembly_program.handle); 158 program_manager.BindComputeAssemblyProgram(assembly_program.handle);
147 } else { 159 } else {
@@ -223,4 +235,13 @@ void ComputePipeline::Configure() {
223 } 235 }
224} 236}
225 237
238void ComputePipeline::WaitForBuild() {
239 if (built_fence.handle == 0) {
240 std::unique_lock lock{built_mutex};
241 built_condvar.wait(lock, [this] { return built_fence.handle != 0; });
242 }
243 ASSERT(glClientWaitSync(built_fence.handle, 0, GL_TIMEOUT_IGNORED) != GL_WAIT_FAILED);
244 is_built = true;
245}
246
226} // namespace OpenGL 247} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.h b/src/video_core/renderer_opengl/gl_compute_pipeline.h
index 6534dec32..9bcc72b59 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.h
@@ -50,7 +50,8 @@ class ComputePipeline {
50public: 50public:
51 explicit ComputePipeline(const Device& device, TextureCache& texture_cache_, 51 explicit ComputePipeline(const Device& device, TextureCache& texture_cache_,
52 BufferCache& buffer_cache_, ProgramManager& program_manager_, 52 BufferCache& buffer_cache_, ProgramManager& program_manager_,
53 const Shader::Info& info_, std::string code, std::vector<u32> code_v); 53 const Shader::Info& info_, std::string code, std::vector<u32> code_v,
54 bool force_context_flush = false);
54 55
55 void Configure(); 56 void Configure();
56 57
@@ -65,6 +66,8 @@ public:
65 } 66 }
66 67
67private: 68private:
69 void WaitForBuild();
70
68 TextureCache& texture_cache; 71 TextureCache& texture_cache;
69 BufferCache& buffer_cache; 72 BufferCache& buffer_cache;
70 Tegra::MemoryManager* gpu_memory; 73 Tegra::MemoryManager* gpu_memory;
@@ -81,6 +84,11 @@ private:
81 84
82 bool use_storage_buffers{}; 85 bool use_storage_buffers{};
83 bool writes_global_memory{}; 86 bool writes_global_memory{};
87
88 std::mutex built_mutex;
89 std::condition_variable built_condvar;
90 OGLSync built_fence{};
91 bool is_built{false};
84}; 92};
85 93
86} // namespace OpenGL 94} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index c115dabe1..29491e762 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -176,7 +176,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
176 std::array<std::string, 5> sources, 176 std::array<std::string, 5> sources,
177 std::array<std::vector<u32>, 5> sources_spirv, 177 std::array<std::vector<u32>, 5> sources_spirv,
178 const std::array<const Shader::Info*, 5>& infos, 178 const std::array<const Shader::Info*, 5>& infos,
179 const GraphicsPipelineKey& key_) 179 const GraphicsPipelineKey& key_, bool force_context_flush)
180 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, 180 : texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_},
181 state_tracker{state_tracker_}, key{key_} { 181 state_tracker{state_tracker_}, key{key_} {
182 if (shader_notify) { 182 if (shader_notify) {
@@ -231,7 +231,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
231 const bool in_parallel = thread_worker != nullptr; 231 const bool in_parallel = thread_worker != nullptr;
232 const auto backend = device.GetShaderBackend(); 232 const auto backend = device.GetShaderBackend();
233 auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv), 233 auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv),
234 shader_notify, backend, in_parallel](ShaderContext::Context*) mutable { 234 shader_notify, backend, in_parallel,
235 force_context_flush](ShaderContext::Context*) mutable {
235 for (size_t stage = 0; stage < 5; ++stage) { 236 for (size_t stage = 0; stage < 5; ++stage) {
236 switch (backend) { 237 switch (backend) {
237 case Settings::ShaderBackend::GLSL: 238 case Settings::ShaderBackend::GLSL:
@@ -251,7 +252,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
251 break; 252 break;
252 } 253 }
253 } 254 }
254 if (in_parallel) { 255 if (force_context_flush || in_parallel) {
255 std::scoped_lock lock{built_mutex}; 256 std::scoped_lock lock{built_mutex};
256 built_fence.Create(); 257 built_fence.Create();
257 // Flush this context to ensure compilation commands and fence are in the GPU pipe. 258 // Flush this context to ensure compilation commands and fence are in the GPU pipe.
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.h b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
index 1c06b3655..7bab3be0a 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.h
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.h
@@ -78,7 +78,7 @@ public:
78 std::array<std::string, 5> sources, 78 std::array<std::string, 5> sources,
79 std::array<std::vector<u32>, 5> sources_spirv, 79 std::array<std::vector<u32>, 5> sources_spirv,
80 const std::array<const Shader::Info*, 5>& infos, 80 const std::array<const Shader::Info*, 5>& infos,
81 const GraphicsPipelineKey& key_); 81 const GraphicsPipelineKey& key_, bool force_context_flush = false);
82 82
83 void Configure(bool is_indexed) { 83 void Configure(bool is_indexed) {
84 configure_func(this, is_indexed); 84 configure_func(this, is_indexed);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 7dd854e0f..626ea7dcb 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -286,7 +286,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
286 file.read(reinterpret_cast<char*>(&key), sizeof(key)); 286 file.read(reinterpret_cast<char*>(&key), sizeof(key));
287 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { 287 queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
288 ctx->pools.ReleaseContents(); 288 ctx->pools.ReleaseContents();
289 auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; 289 auto pipeline{CreateComputePipeline(ctx->pools, key, env, true)};
290 std::scoped_lock lock{state.mutex}; 290 std::scoped_lock lock{state.mutex};
291 if (pipeline) { 291 if (pipeline) {
292 compute_cache.emplace(key, std::move(pipeline)); 292 compute_cache.emplace(key, std::move(pipeline));
@@ -307,7 +307,7 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
307 env_ptrs.push_back(&env); 307 env_ptrs.push_back(&env);
308 } 308 }
309 ctx->pools.ReleaseContents(); 309 ctx->pools.ReleaseContents();
310 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; 310 auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false, true)};
311 std::scoped_lock lock{state.mutex}; 311 std::scoped_lock lock{state.mutex};
312 if (pipeline) { 312 if (pipeline) {
313 graphics_cache.emplace(key, std::move(pipeline)); 313 graphics_cache.emplace(key, std::move(pipeline));
@@ -439,7 +439,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
439 439
440std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( 440std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
441 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key, 441 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
442 std::span<Shader::Environment* const> envs, bool build_in_parallel) try { 442 std::span<Shader::Environment* const> envs, bool use_shader_workers,
443 bool force_context_flush) try {
443 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); 444 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
444 size_t env_index{}; 445 size_t env_index{};
445 u32 total_storage_buffers{}; 446 u32 total_storage_buffers{};
@@ -531,10 +532,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
531 } 532 }
532 previous_program = &program; 533 previous_program = &program;
533 } 534 }
534 auto* const thread_worker{build_in_parallel ? workers.get() : nullptr}; 535 auto* const thread_worker{use_shader_workers ? workers.get() : nullptr};
535 return std::make_unique<GraphicsPipeline>(device, texture_cache, buffer_cache, program_manager, 536 return std::make_unique<GraphicsPipeline>(device, texture_cache, buffer_cache, program_manager,
536 state_tracker, thread_worker, &shader_notify, sources, 537 state_tracker, thread_worker, &shader_notify, sources,
537 sources_spirv, infos, key); 538 sources_spirv, infos, key, force_context_flush);
538 539
539} catch (Shader::Exception& exception) { 540} catch (Shader::Exception& exception) {
540 LOG_ERROR(Render_OpenGL, "{}", exception.what()); 541 LOG_ERROR(Render_OpenGL, "{}", exception.what());
@@ -559,8 +560,8 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
559} 560}
560 561
561std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline( 562std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
562 ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, 563 ShaderContext::ShaderPools& pools, const ComputePipelineKey& key, Shader::Environment& env,
563 Shader::Environment& env) try { 564 bool force_context_flush) try {
564 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); 565 LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
565 566
566 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 567 Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
@@ -589,7 +590,7 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
589 } 590 }
590 591
591 return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, program_manager, 592 return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, program_manager,
592 program.info, code, code_spirv); 593 program.info, code, code_spirv, force_context_flush);
593} catch (Shader::Exception& exception) { 594} catch (Shader::Exception& exception) {
594 LOG_ERROR(Render_OpenGL, "{}", exception.what()); 595 LOG_ERROR(Render_OpenGL, "{}", exception.what());
595 return nullptr; 596 return nullptr;
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index f82420592..6b9732fca 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -50,14 +50,16 @@ private:
50 50
51 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline( 51 std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
52 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key, 52 ShaderContext::ShaderPools& pools, const GraphicsPipelineKey& key,
53 std::span<Shader::Environment* const> envs, bool build_in_parallel); 53 std::span<Shader::Environment* const> envs, bool use_shader_workers,
54 bool force_context_flush = false);
54 55
55 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key, 56 std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key,
56 const VideoCommon::ShaderInfo* shader); 57 const VideoCommon::ShaderInfo* shader);
57 58
58 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools, 59 std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderContext::ShaderPools& pools,
59 const ComputePipelineKey& key, 60 const ComputePipelineKey& key,
60 Shader::Environment& env); 61 Shader::Environment& env,
62 bool force_context_flush = false);
61 63
62 std::unique_ptr<ShaderWorker> CreateWorkers() const; 64 std::unique_ptr<ShaderWorker> CreateWorkers() const;
63 65
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index f91bb5a1d..baedc4424 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -548,31 +548,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
548 static_vector<VkVertexInputBindingDescription, 32> vertex_bindings; 548 static_vector<VkVertexInputBindingDescription, 32> vertex_bindings;
549 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; 549 static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors;
550 static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; 550 static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes;
551 if (key.state.dynamic_vertex_input) { 551 if (!key.state.dynamic_vertex_input) {
552 const size_t num_vertex_arrays = std::min(
553 key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings()));
554 for (size_t index = 0; index < num_vertex_arrays; ++index) {
555 const u32 type = key.state.DynamicAttributeType(index);
556 if (!stage_infos[0].loads.Generic(index) || type == 0) {
557 continue;
558 }
559 vertex_attributes.push_back({
560 .location = static_cast<u32>(index),
561 .binding = 0,
562 .format = type == 1 ? VK_FORMAT_R32_SFLOAT
563 : type == 2 ? VK_FORMAT_R32_SINT
564 : VK_FORMAT_R32_UINT,
565 .offset = 0,
566 });
567 }
568 if (!vertex_attributes.empty()) {
569 vertex_bindings.push_back({
570 .binding = 0,
571 .stride = 4,
572 .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
573 });
574 }
575 } else {
576 const size_t num_vertex_arrays = std::min( 552 const size_t num_vertex_arrays = std::min(
577 Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings())); 553 Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings()));
578 for (size_t index = 0; index < num_vertex_arrays; ++index) { 554 for (size_t index = 0; index < num_vertex_arrays; ++index) {
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index dfc675cc8..06d982d9b 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -353,7 +353,7 @@ if (USE_DISCORD_PRESENCE)
353 discord_impl.cpp 353 discord_impl.cpp
354 discord_impl.h 354 discord_impl.h
355 ) 355 )
356 target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc) 356 target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib)
357 target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) 357 target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
358endif() 358endif()
359 359
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index 734b0ea40..4ae49506d 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -575,7 +575,7 @@ void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
575 QDialog::resize(size); 575 QDialog::resize(size);
576 576
577 // High DPI 577 // High DPI
578 const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f; 578 const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f;
579 579
580 RescaleKeyboardElements(size.width(), size.height(), dpi_scale); 580 RescaleKeyboardElements(size.width(), size.height(), dpi_scale);
581} 581}
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 35fef506a..31209fb2e 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -441,6 +441,7 @@ void Config::ReadControlValues() {
441 Settings::values.mouse_panning = false; 441 Settings::values.mouse_panning = false;
442 ReadBasicSetting(Settings::values.mouse_panning_sensitivity); 442 ReadBasicSetting(Settings::values.mouse_panning_sensitivity);
443 ReadBasicSetting(Settings::values.enable_joycon_driver); 443 ReadBasicSetting(Settings::values.enable_joycon_driver);
444 ReadBasicSetting(Settings::values.enable_procon_driver);
444 445
445 ReadBasicSetting(Settings::values.tas_enable); 446 ReadBasicSetting(Settings::values.tas_enable);
446 ReadBasicSetting(Settings::values.tas_loop); 447 ReadBasicSetting(Settings::values.tas_loop);
@@ -1141,6 +1142,7 @@ void Config::SaveControlValues() {
1141 WriteGlobalSetting(Settings::values.motion_enabled); 1142 WriteGlobalSetting(Settings::values.motion_enabled);
1142 WriteBasicSetting(Settings::values.enable_raw_input); 1143 WriteBasicSetting(Settings::values.enable_raw_input);
1143 WriteBasicSetting(Settings::values.enable_joycon_driver); 1144 WriteBasicSetting(Settings::values.enable_joycon_driver);
1145 WriteBasicSetting(Settings::values.enable_procon_driver);
1144 WriteBasicSetting(Settings::values.keyboard_enabled); 1146 WriteBasicSetting(Settings::values.keyboard_enabled);
1145 WriteBasicSetting(Settings::values.emulate_analog_keyboard); 1147 WriteBasicSetting(Settings::values.emulate_analog_keyboard);
1146 WriteBasicSetting(Settings::values.mouse_panning_sensitivity); 1148 WriteBasicSetting(Settings::values.mouse_panning_sensitivity);
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
index 77b976e74..8d81322f3 100644
--- a/src/yuzu/configuration/configure_input_advanced.cpp
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -139,6 +139,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked(); 139 Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked(); 140 Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
141 Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked(); 141 Settings::values.enable_joycon_driver = ui->enable_joycon_driver->isChecked();
142 Settings::values.enable_procon_driver = ui->enable_procon_driver->isChecked();
142} 143}
143 144
144void ConfigureInputAdvanced::LoadConfiguration() { 145void ConfigureInputAdvanced::LoadConfiguration() {
@@ -174,6 +175,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
174 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue()); 175 ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
175 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue()); 176 ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
176 ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue()); 177 ui->enable_joycon_driver->setChecked(Settings::values.enable_joycon_driver.GetValue());
178 ui->enable_procon_driver->setChecked(Settings::values.enable_procon_driver.GetValue());
177 179
178 UpdateUIEnabled(); 180 UpdateUIEnabled();
179} 181}
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
index 75d96d3ab..0eb2b34bc 100644
--- a/src/yuzu/configuration/configure_input_advanced.ui
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -2712,6 +2712,22 @@
2712 </widget> 2712 </widget>
2713 </item> 2713 </item>
2714 <item row="6" column="0"> 2714 <item row="6" column="0">
2715 <widget class="QCheckBox" name="enable_procon_driver">
2716 <property name="toolTip">
2717 <string>Requires restarting yuzu</string>
2718 </property>
2719 <property name="minimumSize">
2720 <size>
2721 <width>0</width>
2722 <height>23</height>
2723 </size>
2724 </property>
2725 <property name="text">
2726 <string>Enable direct Pro Controller driver [EXPERIMENTAL]</string>
2727 </property>
2728 </widget>
2729 </item>
2730 <item row="7" column="0">
2715 <widget class="QCheckBox" name="mouse_panning"> 2731 <widget class="QCheckBox" name="mouse_panning">
2716 <property name="minimumSize"> 2732 <property name="minimumSize">
2717 <size> 2733 <size>
@@ -2724,7 +2740,7 @@
2724 </property> 2740 </property>
2725 </widget> 2741 </widget>
2726 </item> 2742 </item>
2727 <item row="6" column="2"> 2743 <item row="7" column="2">
2728 <widget class="QSpinBox" name="mouse_panning_sensitivity"> 2744 <widget class="QSpinBox" name="mouse_panning_sensitivity">
2729 <property name="toolTip"> 2745 <property name="toolTip">
2730 <string>Mouse sensitivity</string> 2746 <string>Mouse sensitivity</string>
@@ -2746,14 +2762,14 @@
2746 </property> 2762 </property>
2747 </widget> 2763 </widget>
2748 </item> 2764 </item>
2749 <item row="7" column="0"> 2765 <item row="8" column="0">
2750 <widget class="QLabel" name="motion_touch"> 2766 <widget class="QLabel" name="motion_touch">
2751 <property name="text"> 2767 <property name="text">
2752 <string>Motion / Touch</string> 2768 <string>Motion / Touch</string>
2753 </property> 2769 </property>
2754 </widget> 2770 </widget>
2755 </item> 2771 </item>
2756 <item row="7" column="2"> 2772 <item row="8" column="2">
2757 <widget class="QPushButton" name="buttonMotionTouch"> 2773 <widget class="QPushButton" name="buttonMotionTouch">
2758 <property name="text"> 2774 <property name="text">
2759 <string>Configure</string> 2775 <string>Configure</string>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 4b7e3b01b..723690e71 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -182,12 +182,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
182 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : ""); 182 const QString toggle = QString::fromStdString(param.Get("toggle", false) ? "~" : "");
183 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : ""); 183 const QString inverted = QString::fromStdString(param.Get("inverted", false) ? "!" : "");
184 const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : ""); 184 const QString invert = QString::fromStdString(param.Get("invert", "+") == "-" ? "-" : "");
185 const QString turbo = QString::fromStdString(param.Get("turbo", false) ? "$" : "");
185 const auto common_button_name = input_subsystem->GetButtonName(param); 186 const auto common_button_name = input_subsystem->GetButtonName(param);
186 187
187 // Retrieve the names from Qt 188 // Retrieve the names from Qt
188 if (param.Get("engine", "") == "keyboard") { 189 if (param.Get("engine", "") == "keyboard") {
189 const QString button_str = GetKeyName(param.Get("code", 0)); 190 const QString button_str = GetKeyName(param.Get("code", 0));
190 return QObject::tr("%1%2%3").arg(toggle, inverted, button_str); 191 return QObject::tr("%1%2%3%4").arg(turbo, toggle, inverted, button_str);
191 } 192 }
192 193
193 if (common_button_name == Common::Input::ButtonNames::Invalid) { 194 if (common_button_name == Common::Input::ButtonNames::Invalid) {
@@ -201,7 +202,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
201 if (common_button_name == Common::Input::ButtonNames::Value) { 202 if (common_button_name == Common::Input::ButtonNames::Value) {
202 if (param.Has("hat")) { 203 if (param.Has("hat")) {
203 const QString hat = GetDirectionName(param.Get("direction", "")); 204 const QString hat = GetDirectionName(param.Get("direction", ""));
204 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, hat); 205 return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, hat);
205 } 206 }
206 if (param.Has("axis")) { 207 if (param.Has("axis")) {
207 const QString axis = QString::fromStdString(param.Get("axis", "")); 208 const QString axis = QString::fromStdString(param.Get("axis", ""));
@@ -219,13 +220,13 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
219 } 220 }
220 if (param.Has("button")) { 221 if (param.Has("button")) {
221 const QString button = QString::fromStdString(param.Get("button", "")); 222 const QString button = QString::fromStdString(param.Get("button", ""));
222 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button); 223 return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button);
223 } 224 }
224 } 225 }
225 226
226 QString button_name = GetButtonName(common_button_name); 227 QString button_name = GetButtonName(common_button_name);
227 if (param.Has("hat")) { 228 if (param.Has("hat")) {
228 return QObject::tr("%1%2Hat %3").arg(toggle, inverted, button_name); 229 return QObject::tr("%1%2%3Hat %4").arg(turbo, toggle, inverted, button_name);
229 } 230 }
230 if (param.Has("axis")) { 231 if (param.Has("axis")) {
231 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); 232 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
@@ -234,7 +235,7 @@ QString ConfigureInputPlayer::ButtonToText(const Common::ParamPackage& param) {
234 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name); 235 return QObject::tr("%1%2Axis %3").arg(toggle, inverted, button_name);
235 } 236 }
236 if (param.Has("button")) { 237 if (param.Has("button")) {
237 return QObject::tr("%1%2Button %3").arg(toggle, inverted, button_name); 238 return QObject::tr("%1%2%3Button %4").arg(turbo, toggle, inverted, button_name);
238 } 239 }
239 240
240 return QObject::tr("[unknown]"); 241 return QObject::tr("[unknown]");
@@ -395,6 +396,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
395 button_map[button_id]->setText(ButtonToText(param)); 396 button_map[button_id]->setText(ButtonToText(param));
396 emulated_controller->SetButtonParam(button_id, param); 397 emulated_controller->SetButtonParam(button_id, param);
397 }); 398 });
399 context_menu.addAction(tr("Turbo button"), [&] {
400 const bool turbo_value = !param.Get("turbo", false);
401 param.Set("turbo", turbo_value);
402 button_map[button_id]->setText(ButtonToText(param));
403 emulated_controller->SetButtonParam(button_id, param);
404 });
398 } 405 }
399 if (param.Has("axis")) { 406 if (param.Has("axis")) {
400 context_menu.addAction(tr("Invert axis"), [&] { 407 context_menu.addAction(tr("Invert axis"), [&] {
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index 68af6c20c..c287220fc 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -81,7 +81,6 @@ void PlayerControlPreview::UpdateColors() {
81 colors.outline = QColor(0, 0, 0); 81 colors.outline = QColor(0, 0, 0);
82 colors.primary = QColor(225, 225, 225); 82 colors.primary = QColor(225, 225, 225);
83 colors.button = QColor(109, 111, 114); 83 colors.button = QColor(109, 111, 114);
84 colors.button2 = QColor(109, 111, 114);
85 colors.button2 = QColor(77, 80, 84); 84 colors.button2 = QColor(77, 80, 84);
86 colors.slider_arrow = QColor(65, 68, 73); 85 colors.slider_arrow = QColor(65, 68, 73);
87 colors.font2 = QColor(0, 0, 0); 86 colors.font2 = QColor(0, 0, 0);
@@ -100,6 +99,7 @@ void PlayerControlPreview::UpdateColors() {
100 colors.led_off = QColor(170, 238, 255); 99 colors.led_off = QColor(170, 238, 255);
101 colors.indicator2 = QColor(59, 165, 93); 100 colors.indicator2 = QColor(59, 165, 93);
102 colors.charging = QColor(250, 168, 26); 101 colors.charging = QColor(250, 168, 26);
102 colors.button_turbo = QColor(217, 158, 4);
103 103
104 colors.left = colors.primary; 104 colors.left = colors.primary;
105 colors.right = colors.primary; 105 colors.right = colors.primary;
@@ -2469,7 +2469,6 @@ void PlayerControlPreview::DrawJoystickDot(QPainter& p, const QPointF center,
2469void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center, 2469void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
2470 const Common::Input::ButtonStatus& pressed, float width, 2470 const Common::Input::ButtonStatus& pressed, float width,
2471 float height, Direction direction, float radius) { 2471 float height, Direction direction, float radius) {
2472 p.setBrush(button_color);
2473 if (pressed.value) { 2472 if (pressed.value) {
2474 switch (direction) { 2473 switch (direction) {
2475 case Direction::Left: 2474 case Direction::Left:
@@ -2487,16 +2486,16 @@ void PlayerControlPreview::DrawRoundButton(QPainter& p, QPointF center,
2487 case Direction::None: 2486 case Direction::None:
2488 break; 2487 break;
2489 } 2488 }
2490 p.setBrush(colors.highlight);
2491 } 2489 }
2492 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f}; 2490 QRectF rect = {center.x() - width, center.y() - height, width * 2.0f, height * 2.0f};
2491 p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo));
2493 p.drawRoundedRect(rect, radius, radius); 2492 p.drawRoundedRect(rect, radius, radius);
2494} 2493}
2495void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center, 2494void PlayerControlPreview::DrawMinusButton(QPainter& p, const QPointF center,
2496 const Common::Input::ButtonStatus& pressed, 2495 const Common::Input::ButtonStatus& pressed,
2497 int button_size) { 2496 int button_size) {
2498 p.setPen(colors.outline); 2497 p.setPen(colors.outline);
2499 p.setBrush(pressed.value ? colors.highlight : colors.button); 2498 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2500 DrawRectangle(p, center, button_size, button_size / 3.0f); 2499 DrawRectangle(p, center, button_size, button_size / 3.0f);
2501} 2500}
2502void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center, 2501void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
@@ -2504,7 +2503,7 @@ void PlayerControlPreview::DrawPlusButton(QPainter& p, const QPointF center,
2504 int button_size) { 2503 int button_size) {
2505 // Draw outer line 2504 // Draw outer line
2506 p.setPen(colors.outline); 2505 p.setPen(colors.outline);
2507 p.setBrush(pressed.value ? colors.highlight : colors.button); 2506 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2508 DrawRectangle(p, center, button_size, button_size / 3.0f); 2507 DrawRectangle(p, center, button_size, button_size / 3.0f);
2509 DrawRectangle(p, center, button_size / 3.0f, button_size); 2508 DrawRectangle(p, center, button_size / 3.0f, button_size);
2510 2509
@@ -2526,7 +2525,7 @@ void PlayerControlPreview::DrawGCButtonX(QPainter& p, const QPointF center,
2526 } 2525 }
2527 2526
2528 p.setPen(colors.outline); 2527 p.setPen(colors.outline);
2529 p.setBrush(pressed.value ? colors.highlight : colors.button); 2528 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2530 DrawPolygon(p, button_x); 2529 DrawPolygon(p, button_x);
2531} 2530}
2532 2531
@@ -2539,7 +2538,7 @@ void PlayerControlPreview::DrawGCButtonY(QPainter& p, const QPointF center,
2539 } 2538 }
2540 2539
2541 p.setPen(colors.outline); 2540 p.setPen(colors.outline);
2542 p.setBrush(pressed.value ? colors.highlight : colors.button); 2541 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2543 DrawPolygon(p, button_x); 2542 DrawPolygon(p, button_x);
2544} 2543}
2545 2544
@@ -2553,17 +2552,15 @@ void PlayerControlPreview::DrawGCButtonZ(QPainter& p, const QPointF center,
2553 } 2552 }
2554 2553
2555 p.setPen(colors.outline); 2554 p.setPen(colors.outline);
2556 p.setBrush(pressed.value ? colors.highlight : colors.button2); 2555 p.setBrush(GetButtonColor(colors.button2, pressed.value, pressed.turbo));
2557 DrawPolygon(p, button_x); 2556 DrawPolygon(p, button_x);
2558} 2557}
2559 2558
2560void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center, 2559void PlayerControlPreview::DrawCircleButton(QPainter& p, const QPointF center,
2561 const Common::Input::ButtonStatus& pressed, 2560 const Common::Input::ButtonStatus& pressed,
2562 float button_size) { 2561 float button_size) {
2563 p.setBrush(button_color); 2562
2564 if (pressed.value) { 2563 p.setBrush(GetButtonColor(button_color, pressed.value, pressed.turbo));
2565 p.setBrush(colors.highlight);
2566 }
2567 p.drawEllipse(center, button_size, button_size); 2564 p.drawEllipse(center, button_size, button_size);
2568} 2565}
2569 2566
@@ -2620,7 +2617,7 @@ void PlayerControlPreview::DrawArrowButton(QPainter& p, const QPointF center,
2620 2617
2621 // Draw arrow button 2618 // Draw arrow button
2622 p.setPen(pressed.value ? colors.highlight : colors.button); 2619 p.setPen(pressed.value ? colors.highlight : colors.button);
2623 p.setBrush(pressed.value ? colors.highlight : colors.button); 2620 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2624 DrawPolygon(p, arrow_button); 2621 DrawPolygon(p, arrow_button);
2625 2622
2626 switch (direction) { 2623 switch (direction) {
@@ -2672,10 +2669,20 @@ void PlayerControlPreview::DrawTriggerButton(QPainter& p, const QPointF center,
2672 2669
2673 // Draw arrow button 2670 // Draw arrow button
2674 p.setPen(colors.outline); 2671 p.setPen(colors.outline);
2675 p.setBrush(pressed.value ? colors.highlight : colors.button); 2672 p.setBrush(GetButtonColor(colors.button, pressed.value, pressed.turbo));
2676 DrawPolygon(p, qtrigger_button); 2673 DrawPolygon(p, qtrigger_button);
2677} 2674}
2678 2675
2676QColor PlayerControlPreview::GetButtonColor(QColor default_color, bool is_pressed, bool turbo) {
2677 if (is_pressed && turbo) {
2678 return colors.button_turbo;
2679 }
2680 if (is_pressed) {
2681 return colors.highlight;
2682 }
2683 return default_color;
2684}
2685
2679void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center, 2686void PlayerControlPreview::DrawBattery(QPainter& p, QPointF center,
2680 Common::Input::BatteryLevel battery) { 2687 Common::Input::BatteryLevel battery) {
2681 if (battery == Common::Input::BatteryLevel::None) { 2688 if (battery == Common::Input::BatteryLevel::None) {
diff --git a/src/yuzu/configuration/configure_input_player_widget.h b/src/yuzu/configuration/configure_input_player_widget.h
index b258c6d77..0e9e95e85 100644
--- a/src/yuzu/configuration/configure_input_player_widget.h
+++ b/src/yuzu/configuration/configure_input_player_widget.h
@@ -81,6 +81,7 @@ private:
81 QColor right{}; 81 QColor right{};
82 QColor button{}; 82 QColor button{};
83 QColor button2{}; 83 QColor button2{};
84 QColor button_turbo{};
84 QColor font{}; 85 QColor font{};
85 QColor font2{}; 86 QColor font2{};
86 QColor highlight{}; 87 QColor highlight{};
@@ -183,6 +184,7 @@ private:
183 const Common::Input::ButtonStatus& pressed, float size = 1.0f); 184 const Common::Input::ButtonStatus& pressed, float size = 1.0f);
184 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction, 185 void DrawTriggerButton(QPainter& p, QPointF center, Direction direction,
185 const Common::Input::ButtonStatus& pressed); 186 const Common::Input::ButtonStatus& pressed);
187 QColor GetButtonColor(QColor default_color, bool is_pressed, bool turbo);
186 188
187 // Draw battery functions 189 // Draw battery functions
188 void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery); 190 void DrawBattery(QPainter& p, QPointF center, Common::Input::BatteryLevel battery);
diff --git a/src/yuzu/discord_impl.cpp b/src/yuzu/discord_impl.cpp
index c351e9b83..de0c307d4 100644
--- a/src/yuzu/discord_impl.cpp
+++ b/src/yuzu/discord_impl.cpp
@@ -4,7 +4,10 @@
4#include <chrono> 4#include <chrono>
5#include <string> 5#include <string>
6#include <discord_rpc.h> 6#include <discord_rpc.h>
7#include <fmt/format.h>
8#include <httplib.h>
7#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/string_util.h"
8#include "core/core.h" 11#include "core/core.h"
9#include "core/loader/loader.h" 12#include "core/loader/loader.h"
10#include "yuzu/discord_impl.h" 13#include "yuzu/discord_impl.h"
@@ -14,7 +17,6 @@ namespace DiscordRPC {
14 17
15DiscordImpl::DiscordImpl(Core::System& system_) : system{system_} { 18DiscordImpl::DiscordImpl(Core::System& system_) : system{system_} {
16 DiscordEventHandlers handlers{}; 19 DiscordEventHandlers handlers{};
17
18 // The number is the client ID for yuzu, it's used for images and the 20 // The number is the client ID for yuzu, it's used for images and the
19 // application name 21 // application name
20 Discord_Initialize("712465656758665259", &handlers, 1, nullptr); 22 Discord_Initialize("712465656758665259", &handlers, 1, nullptr);
@@ -29,23 +31,74 @@ void DiscordImpl::Pause() {
29 Discord_ClearPresence(); 31 Discord_ClearPresence();
30} 32}
31 33
34static std::string GetGameString(const std::string& title) {
35 // Convert to lowercase
36 std::string icon_name = Common::ToLower(title);
37
38 // Replace spaces with dashes
39 std::replace(icon_name.begin(), icon_name.end(), ' ', '-');
40
41 // Remove non-alphanumeric characters but keep dashes
42 std::erase_if(icon_name, [](char c) { return !std::isalnum(c) && c != '-'; });
43
44 // Remove dashes from the start and end of the string
45 icon_name.erase(icon_name.begin(), std::find_if(icon_name.begin(), icon_name.end(),
46 [](int ch) { return ch != '-'; }));
47 icon_name.erase(
48 std::find_if(icon_name.rbegin(), icon_name.rend(), [](int ch) { return ch != '-'; }).base(),
49 icon_name.end());
50
51 // Remove double dashes
52 icon_name.erase(std::unique(icon_name.begin(), icon_name.end(),
53 [](char a, char b) { return a == '-' && b == '-'; }),
54 icon_name.end());
55
56 return icon_name;
57}
58
32void DiscordImpl::Update() { 59void DiscordImpl::Update() {
33 s64 start_time = std::chrono::duration_cast<std::chrono::seconds>( 60 s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
34 std::chrono::system_clock::now().time_since_epoch()) 61 std::chrono::system_clock::now().time_since_epoch())
35 .count(); 62 .count();
63 const std::string default_text = "yuzu is an emulator for the Nintendo Switch";
64 const std::string default_image = "yuzu_logo";
65 std::string game_cover_url = "https://yuzu-emu.org";
36 std::string title; 66 std::string title;
37 if (system.IsPoweredOn()) { 67
38 system.GetAppLoader().ReadTitle(title);
39 }
40 DiscordRichPresence presence{}; 68 DiscordRichPresence presence{};
41 presence.largeImageKey = "yuzu_logo"; 69
42 presence.largeImageText = "yuzu is an emulator for the Nintendo Switch";
43 if (system.IsPoweredOn()) { 70 if (system.IsPoweredOn()) {
71 system.GetAppLoader().ReadTitle(title);
72
73 // Used to format Icon URL for yuzu website game compatibility page
74 std::string icon_name = GetGameString(title);
75
76 // New Check for game cover
77 httplib::Client cli(game_cover_url);
78
79 if (auto res = cli.Head(fmt::format("/images/game/boxart/{}.png", icon_name).c_str())) {
80 if (res->status == 200) {
81 game_cover_url += fmt::format("/images/game/boxart/{}.png", icon_name);
82 } else {
83 game_cover_url = "yuzu_logo";
84 }
85 } else {
86 game_cover_url = "yuzu_logo";
87 }
88
89 presence.largeImageKey = game_cover_url.c_str();
90 presence.largeImageText = title.c_str();
91
92 presence.smallImageKey = default_image.c_str();
93 presence.smallImageText = default_text.c_str();
44 presence.state = title.c_str(); 94 presence.state = title.c_str();
45 presence.details = "Currently in game"; 95 presence.details = "Currently in game";
46 } else { 96 } else {
47 presence.details = "Not in game"; 97 presence.largeImageKey = default_image.c_str();
98 presence.largeImageText = default_text.c_str();
99 presence.details = "Currently not in game";
48 } 100 }
101
49 presence.startTimestamp = start_time; 102 presence.startTimestamp = start_time;
50 Discord_UpdatePresence(&presence); 103 Discord_UpdatePresence(&presence);
51} 104}
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 42b7b64c8..c278d8dab 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -680,8 +680,10 @@ void GMainWindow::SoftwareKeyboardShowNormal() {
680 const auto y = layout.screen.top; 680 const auto y = layout.screen.top;
681 const auto w = layout.screen.GetWidth(); 681 const auto w = layout.screen.GetWidth();
682 const auto h = layout.screen.GetHeight(); 682 const auto h = layout.screen.GetHeight();
683 const auto scale_ratio = devicePixelRatioF();
683 684
684 software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); 685 software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y) / scale_ratio),
686 QSize(w, h) / scale_ratio);
685} 687}
686 688
687void GMainWindow::SoftwareKeyboardShowTextCheck( 689void GMainWindow::SoftwareKeyboardShowTextCheck(
@@ -714,9 +716,11 @@ void GMainWindow::SoftwareKeyboardShowInline(
714 (1.0f - appear_parameters.key_top_scale_y)))); 716 (1.0f - appear_parameters.key_top_scale_y))));
715 const auto w = static_cast<int>(layout.screen.GetWidth() * appear_parameters.key_top_scale_x); 717 const auto w = static_cast<int>(layout.screen.GetWidth() * appear_parameters.key_top_scale_x);
716 const auto h = static_cast<int>(layout.screen.GetHeight() * appear_parameters.key_top_scale_y); 718 const auto h = static_cast<int>(layout.screen.GetHeight() * appear_parameters.key_top_scale_y);
719 const auto scale_ratio = devicePixelRatioF();
717 720
718 software_keyboard->ShowInlineKeyboard(std::move(appear_parameters), 721 software_keyboard->ShowInlineKeyboard(std::move(appear_parameters),
719 render_window->mapToGlobal(QPoint(x, y)), QSize(w, h)); 722 render_window->mapToGlobal(QPoint(x, y) / scale_ratio),
723 QSize(w, h) / scale_ratio);
720} 724}
721 725
722void GMainWindow::SoftwareKeyboardHideInline() { 726void GMainWindow::SoftwareKeyboardHideInline() {
@@ -796,9 +800,12 @@ void GMainWindow::WebBrowserOpenWebPage(const std::string& main_url,
796 } 800 }
797 801
798 const auto& layout = render_window->GetFramebufferLayout(); 802 const auto& layout = render_window->GetFramebufferLayout();
799 web_browser_view.resize(layout.screen.GetWidth(), layout.screen.GetHeight()); 803 const auto scale_ratio = devicePixelRatioF();
800 web_browser_view.move(layout.screen.left, layout.screen.top + menuBar()->height()); 804 web_browser_view.resize(layout.screen.GetWidth() / scale_ratio,
801 web_browser_view.setZoomFactor(static_cast<qreal>(layout.screen.GetWidth()) / 805 layout.screen.GetHeight() / scale_ratio);
806 web_browser_view.move(layout.screen.left / scale_ratio,
807 (layout.screen.top / scale_ratio) + menuBar()->height());
808 web_browser_view.setZoomFactor(static_cast<qreal>(layout.screen.GetWidth() / scale_ratio) /
802 static_cast<qreal>(Layout::ScreenUndocked::Width)); 809 static_cast<qreal>(Layout::ScreenUndocked::Width));
803 810
804 web_browser_view.setFocus(); 811 web_browser_view.setFocus();
@@ -957,6 +964,38 @@ void GMainWindow::InitializeWidgets() {
957 tas_label->setFocusPolicy(Qt::NoFocus); 964 tas_label->setFocusPolicy(Qt::NoFocus);
958 statusBar()->insertPermanentWidget(0, tas_label); 965 statusBar()->insertPermanentWidget(0, tas_label);
959 966
967 volume_popup = new QWidget(this);
968 volume_popup->setWindowFlags(Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint | Qt::Popup);
969 volume_popup->setLayout(new QVBoxLayout());
970 volume_popup->setMinimumWidth(200);
971
972 volume_slider = new QSlider(Qt::Horizontal);
973 volume_slider->setObjectName(QStringLiteral("volume_slider"));
974 volume_slider->setMaximum(200);
975 volume_slider->setPageStep(5);
976 connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) {
977 Settings::values.audio_muted = false;
978 const auto volume = static_cast<u8>(percentage);
979 Settings::values.volume.SetValue(volume);
980 UpdateVolumeUI();
981 });
982 volume_popup->layout()->addWidget(volume_slider);
983
984 volume_button = new QPushButton();
985 volume_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
986 volume_button->setFocusPolicy(Qt::NoFocus);
987 volume_button->setCheckable(true);
988 UpdateVolumeUI();
989 connect(volume_button, &QPushButton::clicked, this, [&] {
990 UpdateVolumeUI();
991 volume_popup->setVisible(!volume_popup->isVisible());
992 QRect rect = volume_button->geometry();
993 QPoint bottomLeft = statusBar()->mapToGlobal(rect.topLeft());
994 bottomLeft.setY(bottomLeft.y() - volume_popup->geometry().height());
995 volume_popup->setGeometry(QRect(bottomLeft, QSize(rect.width(), rect.height())));
996 });
997 statusBar()->insertPermanentWidget(0, volume_button);
998
960 // setup AA button 999 // setup AA button
961 aa_status_button = new QPushButton(); 1000 aa_status_button = new QPushButton();
962 aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); 1001 aa_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
@@ -1119,30 +1158,9 @@ void GMainWindow::InitializeHotkeys() {
1119 &GMainWindow::OnToggleAdaptingFilter); 1158 &GMainWindow::OnToggleAdaptingFilter);
1120 connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode); 1159 connect_shortcut(QStringLiteral("Change Docked Mode"), &GMainWindow::OnToggleDockedMode);
1121 connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy); 1160 connect_shortcut(QStringLiteral("Change GPU Accuracy"), &GMainWindow::OnToggleGpuAccuracy);
1122 connect_shortcut(QStringLiteral("Audio Mute/Unmute"), 1161 connect_shortcut(QStringLiteral("Audio Mute/Unmute"), &GMainWindow::OnMute);
1123 [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); 1162 connect_shortcut(QStringLiteral("Audio Volume Down"), &GMainWindow::OnDecreaseVolume);
1124 connect_shortcut(QStringLiteral("Audio Volume Down"), [] { 1163 connect_shortcut(QStringLiteral("Audio Volume Up"), &GMainWindow::OnIncreaseVolume);
1125 const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
1126 int step = 5;
1127 if (current_volume <= 30) {
1128 step = 2;
1129 }
1130 if (current_volume <= 6) {
1131 step = 1;
1132 }
1133 Settings::values.volume.SetValue(std::max(current_volume - step, 0));
1134 });
1135 connect_shortcut(QStringLiteral("Audio Volume Up"), [] {
1136 const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
1137 int step = 5;
1138 if (current_volume < 30) {
1139 step = 2;
1140 }
1141 if (current_volume < 6) {
1142 step = 1;
1143 }
1144 Settings::values.volume.SetValue(current_volume + step);
1145 });
1146 connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] { 1164 connect_shortcut(QStringLiteral("Toggle Framerate Limit"), [] {
1147 Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue()); 1165 Settings::values.use_speed_limit.SetValue(!Settings::values.use_speed_limit.GetValue());
1148 }); 1166 });
@@ -3456,6 +3474,39 @@ void GMainWindow::OnToggleGpuAccuracy() {
3456 UpdateGPUAccuracyButton(); 3474 UpdateGPUAccuracyButton();
3457} 3475}
3458 3476
3477void GMainWindow::OnMute() {
3478 Settings::values.audio_muted = !Settings::values.audio_muted;
3479 UpdateVolumeUI();
3480}
3481
3482void GMainWindow::OnDecreaseVolume() {
3483 Settings::values.audio_muted = false;
3484 const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
3485 int step = 5;
3486 if (current_volume <= 30) {
3487 step = 2;
3488 }
3489 if (current_volume <= 6) {
3490 step = 1;
3491 }
3492 Settings::values.volume.SetValue(std::max(current_volume - step, 0));
3493 UpdateVolumeUI();
3494}
3495
3496void GMainWindow::OnIncreaseVolume() {
3497 Settings::values.audio_muted = false;
3498 const auto current_volume = static_cast<s32>(Settings::values.volume.GetValue());
3499 int step = 5;
3500 if (current_volume < 30) {
3501 step = 2;
3502 }
3503 if (current_volume < 6) {
3504 step = 1;
3505 }
3506 Settings::values.volume.SetValue(current_volume + step);
3507 UpdateVolumeUI();
3508}
3509
3459void GMainWindow::OnToggleAdaptingFilter() { 3510void GMainWindow::OnToggleAdaptingFilter() {
3460 auto filter = Settings::values.scaling_filter.GetValue(); 3511 auto filter = Settings::values.scaling_filter.GetValue();
3461 if (filter == Settings::ScalingFilter::LastFilter) { 3512 if (filter == Settings::ScalingFilter::LastFilter) {
@@ -3914,6 +3965,18 @@ void GMainWindow::UpdateAAText() {
3914 } 3965 }
3915} 3966}
3916 3967
3968void GMainWindow::UpdateVolumeUI() {
3969 const auto volume_value = static_cast<int>(Settings::values.volume.GetValue());
3970 volume_slider->setValue(volume_value);
3971 if (Settings::values.audio_muted) {
3972 volume_button->setChecked(false);
3973 volume_button->setText(tr("VOLUME: MUTE"));
3974 } else {
3975 volume_button->setChecked(true);
3976 volume_button->setText(tr("VOLUME: %1%", "Volume percentage (e.g. 50%)").arg(volume_value));
3977 }
3978}
3979
3917void GMainWindow::UpdateStatusButtons() { 3980void GMainWindow::UpdateStatusButtons() {
3918 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == 3981 renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() ==
3919 Settings::RendererBackend::Vulkan); 3982 Settings::RendererBackend::Vulkan);
@@ -3922,6 +3985,7 @@ void GMainWindow::UpdateStatusButtons() {
3922 UpdateDockedButton(); 3985 UpdateDockedButton();
3923 UpdateFilterText(); 3986 UpdateFilterText();
3924 UpdateAAText(); 3987 UpdateAAText();
3988 UpdateVolumeUI();
3925} 3989}
3926 3990
3927void GMainWindow::UpdateUISettings() { 3991void GMainWindow::UpdateUISettings() {
@@ -4391,6 +4455,55 @@ void GMainWindow::changeEvent(QEvent* event) {
4391#undef main 4455#undef main
4392#endif 4456#endif
4393 4457
4458static void SetHighDPIAttributes() {
4459#ifdef _WIN32
4460 // For Windows, we want to avoid scaling artifacts on fractional scaling ratios.
4461 // This is done by setting the optimal scaling policy for the primary screen.
4462
4463 // Create a temporary QApplication.
4464 int temp_argc = 0;
4465 char** temp_argv = nullptr;
4466 QApplication temp{temp_argc, temp_argv};
4467
4468 // Get the current screen geometry.
4469 const QScreen* primary_screen = QGuiApplication::primaryScreen();
4470 if (primary_screen == nullptr) {
4471 return;
4472 }
4473
4474 const QRect screen_rect = primary_screen->geometry();
4475 const int real_width = screen_rect.width();
4476 const int real_height = screen_rect.height();
4477 const float real_ratio = primary_screen->logicalDotsPerInch() / 96.0f;
4478
4479 // Recommended minimum width and height for proper window fit.
4480 // Any screen with a lower resolution than this will still have a scale of 1.
4481 constexpr float minimum_width = 1350.0f;
4482 constexpr float minimum_height = 900.0f;
4483
4484 const float width_ratio = std::max(1.0f, real_width / minimum_width);
4485 const float height_ratio = std::max(1.0f, real_height / minimum_height);
4486
4487 // Get the lower of the 2 ratios and truncate, this is the maximum integer scale.
4488 const float max_ratio = std::trunc(std::min(width_ratio, height_ratio));
4489
4490 if (max_ratio > real_ratio) {
4491 QApplication::setHighDpiScaleFactorRoundingPolicy(
4492 Qt::HighDpiScaleFactorRoundingPolicy::Round);
4493 } else {
4494 QApplication::setHighDpiScaleFactorRoundingPolicy(
4495 Qt::HighDpiScaleFactorRoundingPolicy::Floor);
4496 }
4497#else
4498 // Other OSes should be better than Windows at fractional scaling.
4499 QApplication::setHighDpiScaleFactorRoundingPolicy(
4500 Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
4501#endif
4502
4503 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
4504 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
4505}
4506
4394int main(int argc, char* argv[]) { 4507int main(int argc, char* argv[]) {
4395 std::unique_ptr<Config> config = std::make_unique<Config>(); 4508 std::unique_ptr<Config> config = std::make_unique<Config>();
4396 bool has_broken_vulkan = false; 4509 bool has_broken_vulkan = false;
@@ -4446,6 +4559,8 @@ int main(int argc, char* argv[]) {
4446 } 4559 }
4447#endif 4560#endif
4448 4561
4562 SetHighDPIAttributes();
4563
4449#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 4564#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
4450 // Disables the "?" button on all dialogs. Disabled by default on Qt6. 4565 // Disables the "?" button on all dialogs. Disabled by default on Qt6.
4451 QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); 4566 QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton);
@@ -4453,6 +4568,7 @@ int main(int argc, char* argv[]) {
4453 4568
4454 // Enables the core to make the qt created contexts current on std::threads 4569 // Enables the core to make the qt created contexts current on std::threads
4455 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); 4570 QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
4571
4456 QApplication app(argc, argv); 4572 QApplication app(argc, argv);
4457 4573
4458#ifdef _WIN32 4574#ifdef _WIN32
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 0f61abc7a..a23b373a5 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -37,6 +37,8 @@ class QLabel;
37class MultiplayerState; 37class MultiplayerState;
38class QPushButton; 38class QPushButton;
39class QProgressDialog; 39class QProgressDialog;
40class QSlider;
41class QHBoxLayout;
40class WaitTreeWidget; 42class WaitTreeWidget;
41enum class GameListOpenTarget; 43enum class GameListOpenTarget;
42enum class GameListRemoveTarget; 44enum class GameListRemoveTarget;
@@ -312,6 +314,9 @@ private slots:
312 void OnMenuRecentFile(); 314 void OnMenuRecentFile();
313 void OnConfigure(); 315 void OnConfigure();
314 void OnConfigureTas(); 316 void OnConfigureTas();
317 void OnDecreaseVolume();
318 void OnIncreaseVolume();
319 void OnMute();
315 void OnTasStartStop(); 320 void OnTasStartStop();
316 void OnTasRecord(); 321 void OnTasRecord();
317 void OnTasReset(); 322 void OnTasReset();
@@ -364,6 +369,7 @@ private:
364 void UpdateAPIText(); 369 void UpdateAPIText();
365 void UpdateFilterText(); 370 void UpdateFilterText();
366 void UpdateAAText(); 371 void UpdateAAText();
372 void UpdateVolumeUI();
367 void UpdateStatusBar(); 373 void UpdateStatusBar();
368 void UpdateGPUAccuracyButton(); 374 void UpdateGPUAccuracyButton();
369 void UpdateStatusButtons(); 375 void UpdateStatusButtons();
@@ -412,6 +418,9 @@ private:
412 QPushButton* dock_status_button = nullptr; 418 QPushButton* dock_status_button = nullptr;
413 QPushButton* filter_status_button = nullptr; 419 QPushButton* filter_status_button = nullptr;
414 QPushButton* aa_status_button = nullptr; 420 QPushButton* aa_status_button = nullptr;
421 QPushButton* volume_button = nullptr;
422 QWidget* volume_popup = nullptr;
423 QSlider* volume_slider = nullptr;
415 QTimer status_bar_update_timer; 424 QTimer status_bar_update_timer;
416 425
417 std::unique_ptr<Config> config; 426 std::unique_ptr<Config> config;
diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp
index 08c275696..6c93e3511 100644
--- a/src/yuzu/multiplayer/lobby.cpp
+++ b/src/yuzu/multiplayer/lobby.cpp
@@ -77,6 +77,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
77 // UI Buttons 77 // UI Buttons
78 connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); 78 connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby);
79 connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); 79 connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned);
80 connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty);
80 connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); 81 connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull);
81 connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); 82 connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch);
82 connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); 83 connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom);
@@ -329,6 +330,16 @@ bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
329 return true; 330 return true;
330 } 331 }
331 332
333 // filter by empty rooms
334 if (filter_empty) {
335 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
336 int player_count =
337 sourceModel()->data(member_list, LobbyItemMemberList::MemberListRole).toList().size();
338 if (player_count == 0) {
339 return false;
340 }
341 }
342
332 // filter by filled rooms 343 // filter by filled rooms
333 if (filter_full) { 344 if (filter_full) {
334 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent); 345 QModelIndex member_list = sourceModel()->index(sourceRow, Column::MEMBER, sourceParent);
@@ -399,6 +410,11 @@ void LobbyFilterProxyModel::SetFilterOwned(bool filter) {
399 invalidate(); 410 invalidate();
400} 411}
401 412
413void LobbyFilterProxyModel::SetFilterEmpty(bool filter) {
414 filter_empty = filter;
415 invalidate();
416}
417
402void LobbyFilterProxyModel::SetFilterFull(bool filter) { 418void LobbyFilterProxyModel::SetFilterFull(bool filter) {
403 filter_full = filter; 419 filter_full = filter;
404 invalidate(); 420 invalidate();
diff --git a/src/yuzu/multiplayer/lobby.h b/src/yuzu/multiplayer/lobby.h
index 300dad13e..2674ae7c3 100644
--- a/src/yuzu/multiplayer/lobby.h
+++ b/src/yuzu/multiplayer/lobby.h
@@ -130,12 +130,14 @@ public:
130 130
131public slots: 131public slots:
132 void SetFilterOwned(bool); 132 void SetFilterOwned(bool);
133 void SetFilterEmpty(bool);
133 void SetFilterFull(bool); 134 void SetFilterFull(bool);
134 void SetFilterSearch(const QString&); 135 void SetFilterSearch(const QString&);
135 136
136private: 137private:
137 QStandardItemModel* game_list; 138 QStandardItemModel* game_list;
138 bool filter_owned = false; 139 bool filter_owned = false;
140 bool filter_empty = false;
139 bool filter_full = false; 141 bool filter_full = false;
140 QString filter_search; 142 QString filter_search;
141}; 143};
diff --git a/src/yuzu/multiplayer/lobby.ui b/src/yuzu/multiplayer/lobby.ui
index 4c9901c9a..0ef0ef762 100644
--- a/src/yuzu/multiplayer/lobby.ui
+++ b/src/yuzu/multiplayer/lobby.ui
@@ -78,6 +78,13 @@
78 </widget> 78 </widget>
79 </item> 79 </item>
80 <item> 80 <item>
81 <widget class="QCheckBox" name="hide_empty">
82 <property name="text">
83 <string>Hide Empty Rooms</string>
84 </property>
85 </widget>
86 </item>
87 <item>
81 <widget class="QCheckBox" name="hide_full"> 88 <widget class="QCheckBox" name="hide_full">
82 <property name="text"> 89 <property name="text">
83 <string>Hide Full Rooms</string> 90 <string>Hide Full Rooms</string>
diff --git a/src/yuzu/util/overlay_dialog.cpp b/src/yuzu/util/overlay_dialog.cpp
index 796f5bf41..ee35a3e15 100644
--- a/src/yuzu/util/overlay_dialog.cpp
+++ b/src/yuzu/util/overlay_dialog.cpp
@@ -163,7 +163,7 @@ void OverlayDialog::MoveAndResizeWindow() {
163 const auto height = static_cast<float>(parentWidget()->height()); 163 const auto height = static_cast<float>(parentWidget()->height());
164 164
165 // High DPI 165 // High DPI
166 const float dpi_scale = parentWidget()->windowHandle()->screen()->logicalDotsPerInch() / 96.0f; 166 const float dpi_scale = screen()->logicalDotsPerInch() / 96.0f;
167 167
168 const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale; 168 const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
169 const auto body_text_font_size = 169 const auto body_text_font_size =
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 527017282..3b6dce296 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -176,6 +176,10 @@ void Config::ReadValues() {
176 Settings::values.debug_pad_analogs[i] = default_param; 176 Settings::values.debug_pad_analogs[i] = default_param;
177 } 177 }
178 178
179 ReadSetting("ControlsGeneral", Settings::values.enable_raw_input);
180 ReadSetting("ControlsGeneral", Settings::values.enable_joycon_driver);
181 ReadSetting("ControlsGeneral", Settings::values.enable_procon_driver);
182 ReadSetting("ControlsGeneral", Settings::values.emulate_analog_keyboard);
179 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); 183 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
180 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); 184 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
181 ReadSetting("ControlsGeneral", Settings::values.motion_enabled); 185 ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 67d230462..cf3cc4c4e 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -14,6 +14,7 @@ const char* sdl2_config_file =
14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values 14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
15 15
16# Indicates if this player should be connected at boot 16# Indicates if this player should be connected at boot
17# 0 (default): Disabled, 1: Enabled
17connected= 18connected=
18 19
19# for button input, the following devices are available: 20# for button input, the following devices are available:
@@ -94,6 +95,18 @@ motionright=
94# 0 (default): Disabled, 1: Enabled 95# 0 (default): Disabled, 1: Enabled
95debug_pad_enabled = 96debug_pad_enabled =
96 97
98# Enable sdl raw input. Allows to configure up to 8 xinput controllers.
99# 0 (default): Disabled, 1: Enabled
100enable_raw_input =
101
102# Enable yuzu joycon driver instead of SDL drive.
103# 0: Disabled, 1 (default): Enabled
104enable_joycon_driver =
105
106# Emulates an analog input from buttons. Allowing to dial any angle.
107# 0 (default): Disabled, 1: Enabled
108emulate_analog_keyboard =
109
97# Whether to enable or disable vibration 110# Whether to enable or disable vibration
98# 0: Disabled, 1 (default): Enabled 111# 0: Disabled, 1 (default): Enabled
99vibration_enabled= 112vibration_enabled=
@@ -273,11 +286,14 @@ vulkan_device =
273# 0: 0.5x (360p/540p) [EXPERIMENTAL] 286# 0: 0.5x (360p/540p) [EXPERIMENTAL]
274# 1: 0.75x (540p/810p) [EXPERIMENTAL] 287# 1: 0.75x (540p/810p) [EXPERIMENTAL]
275# 2 (default): 1x (720p/1080p) 288# 2 (default): 1x (720p/1080p)
276# 3: 2x (1440p/2160p) 289# 3: 1.5x (1080p/1620p) [EXPERIMENTAL]
277# 4: 3x (2160p/3240p) 290# 4: 2x (1440p/2160p)
278# 5: 4x (2880p/4320p) 291# 5: 3x (2160p/3240p)
279# 6: 5x (3600p/5400p) 292# 6: 4x (2880p/4320p)
280# 7: 6x (4320p/6480p) 293# 7: 5x (3600p/5400p)
294# 8: 6x (4320p/6480p)
295# 9: 7x (5040p/7560p)
296# 10: 8x (5760/8640p)
281resolution_setup = 297resolution_setup =
282 298
283# Pixel filter to use when up- or down-sampling rendered frames. 299# Pixel filter to use when up- or down-sampling rendered frames.
@@ -286,11 +302,11 @@ resolution_setup =
286# 2: Bicubic 302# 2: Bicubic
287# 3: Gaussian 303# 3: Gaussian
288# 4: ScaleForce 304# 4: ScaleForce
289# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only] 305# 5: AMD FidelityFX™️ Super Resolution
290scaling_filter = 306scaling_filter =
291 307
292# Anti-Aliasing (AA) 308# Anti-Aliasing (AA)
293# 0 (default): None, 1: FXAA 309# 0 (default): None, 1: FXAA, 2: SMAA
294anti_aliasing = 310anti_aliasing =
295 311
296# Whether to use fullscreen or borderless window mode 312# Whether to use fullscreen or borderless window mode
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index 31f28a507..5450b8c38 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -18,11 +18,11 @@
18 18
19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_) 19EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_, Core::System& system_)
20 : input_subsystem{input_subsystem_}, system{system_} { 20 : input_subsystem{input_subsystem_}, system{system_} {
21 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { 21 input_subsystem->Initialize();
22 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0) {
22 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting..."); 23 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
23 exit(1); 24 exit(1);
24 } 25 }
25 input_subsystem->Initialize();
26 SDL_SetMainReady(); 26 SDL_SetMainReady();
27} 27}
28 28
@@ -32,10 +32,6 @@ EmuWindow_SDL2::~EmuWindow_SDL2() {
32 SDL_Quit(); 32 SDL_Quit();
33} 33}
34 34
35void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
36 input_subsystem->GetMouse()->MouseMove(x, y, 0, 0, 0, 0);
37}
38
39InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const { 35InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) const {
40 switch (button) { 36 switch (button) {
41 case SDL_BUTTON_LEFT: 37 case SDL_BUTTON_LEFT:
@@ -53,44 +49,36 @@ InputCommon::MouseButton EmuWindow_SDL2::SDLButtonToMouseButton(u32 button) cons
53 } 49 }
54} 50}
55 51
52std::pair<float, float> EmuWindow_SDL2::MouseToTouchPos(s32 touch_x, s32 touch_y) const {
53 int w, h;
54 SDL_GetWindowSize(render_window, &w, &h);
55 const float fx = static_cast<float>(touch_x) / w;
56 const float fy = static_cast<float>(touch_y) / h;
57
58 return {std::clamp<float>(fx, 0.0f, 1.0f), std::clamp<float>(fy, 0.0f, 1.0f)};
59}
60
56void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) { 61void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
57 const auto mouse_button = SDLButtonToMouseButton(button); 62 const auto mouse_button = SDLButtonToMouseButton(button);
58 if (state == SDL_PRESSED) { 63 if (state == SDL_PRESSED) {
59 input_subsystem->GetMouse()->PressButton(x, y, 0, 0, mouse_button); 64 const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
65 input_subsystem->GetMouse()->PressButton(x, y, touch_x, touch_y, mouse_button);
60 } else { 66 } else {
61 input_subsystem->GetMouse()->ReleaseButton(mouse_button); 67 input_subsystem->GetMouse()->ReleaseButton(mouse_button);
62 } 68 }
63} 69}
64 70
65std::pair<unsigned, unsigned> EmuWindow_SDL2::TouchToPixelPos(float touch_x, float touch_y) const { 71void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
66 int w, h; 72 const auto [touch_x, touch_y] = MouseToTouchPos(x, y);
67 SDL_GetWindowSize(render_window, &w, &h); 73 input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, 0, 0);
68
69 touch_x *= w;
70 touch_y *= h;
71
72 return {static_cast<unsigned>(std::max(std::round(touch_x), 0.0f)),
73 static_cast<unsigned>(std::max(std::round(touch_y), 0.0f))};
74} 74}
75 75
76void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) { 76void EmuWindow_SDL2::OnFingerDown(float x, float y, std::size_t id) {
77 int width, height; 77 input_subsystem->GetTouchScreen()->TouchPressed(x, y, id);
78 SDL_GetWindowSize(render_window, &width, &height);
79 const auto [px, py] = TouchToPixelPos(x, y);
80 const float fx = px * 1.0f / width;
81 const float fy = py * 1.0f / height;
82
83 input_subsystem->GetTouchScreen()->TouchPressed(fx, fy, id);
84} 78}
85 79
86void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) { 80void EmuWindow_SDL2::OnFingerMotion(float x, float y, std::size_t id) {
87 int width, height; 81 input_subsystem->GetTouchScreen()->TouchMoved(x, y, id);
88 SDL_GetWindowSize(render_window, &width, &height);
89 const auto [px, py] = TouchToPixelPos(x, y);
90 const float fx = px * 1.0f / width;
91 const float fy = py * 1.0f / height;
92
93 input_subsystem->GetTouchScreen()->TouchMoved(fx, fy, id);
94} 82}
95 83
96void EmuWindow_SDL2::OnFingerUp() { 84void EmuWindow_SDL2::OnFingerUp() {
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index 25c23e2a5..d9b453dee 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -38,17 +38,17 @@ protected:
38 /// Called by WaitEvent when a key is pressed or released. 38 /// Called by WaitEvent when a key is pressed or released.
39 void OnKeyEvent(int key, u8 state); 39 void OnKeyEvent(int key, u8 state);
40 40
41 /// Called by WaitEvent when the mouse moves.
42 void OnMouseMotion(s32 x, s32 y);
43
44 /// Converts a SDL mouse button into MouseInput mouse button 41 /// Converts a SDL mouse button into MouseInput mouse button
45 InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const; 42 InputCommon::MouseButton SDLButtonToMouseButton(u32 button) const;
46 43
44 /// Translates pixel position to float position
45 std::pair<float, float> MouseToTouchPos(s32 touch_x, s32 touch_y) const;
46
47 /// Called by WaitEvent when a mouse button is pressed or released 47 /// Called by WaitEvent when a mouse button is pressed or released
48 void OnMouseButton(u32 button, u8 state, s32 x, s32 y); 48 void OnMouseButton(u32 button, u8 state, s32 x, s32 y);
49 49
50 /// Translates pixel position (0..1) to pixel positions 50 /// Called by WaitEvent when the mouse moves.
51 std::pair<unsigned, unsigned> TouchToPixelPos(float touch_x, float touch_y) const; 51 void OnMouseMotion(s32 x, s32 y);
52 52
53 /// Called by WaitEvent when a finger starts touching the touchscreen 53 /// Called by WaitEvent when a finger starts touching the touchscreen
54 void OnFingerDown(float x, float y, std::size_t id); 54 void OnFingerDown(float x, float y, std::size_t id);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 91133569d..d1f7b1d49 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -62,13 +62,15 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
62static void PrintHelp(const char* argv0) { 62static void PrintHelp(const char* argv0) {
63 std::cout << "Usage: " << argv0 63 std::cout << "Usage: " << argv0
64 << " [options] <filename>\n" 64 << " [options] <filename>\n"
65 "-m, --multiplayer=nick:password@address:port" 65 "-c, --config Load the specified configuration file\n"
66 " Nickname, password, address and port for multiplayer\n"
67 "-f, --fullscreen Start in fullscreen mode\n" 66 "-f, --fullscreen Start in fullscreen mode\n"
67 "-g, --game File path of the game to load\n"
68 "-h, --help Display this help and exit\n" 68 "-h, --help Display this help and exit\n"
69 "-v, --version Output version information and exit\n" 69 "-m, --multiplayer=nick:password@address:port"
70 " Nickname, password, address and port for multiplayer\n"
70 "-p, --program Pass following string as arguments to executable\n" 71 "-p, --program Pass following string as arguments to executable\n"
71 "-c, --config Load the specified configuration file\n"; 72 "-u, --user Select a specific user profile from 0 to 7\n"
73 "-v, --version Output version information and exit\n";
72} 74}
73 75
74static void PrintVersion() { 76static void PrintVersion() {
@@ -199,6 +201,7 @@ int main(int argc, char** argv) {
199 std::string filepath; 201 std::string filepath;
200 std::optional<std::string> config_path; 202 std::optional<std::string> config_path;
201 std::string program_args; 203 std::string program_args;
204 std::optional<int> selected_user;
202 205
203 bool use_multiplayer = false; 206 bool use_multiplayer = false;
204 bool fullscreen = false; 207 bool fullscreen = false;
@@ -209,12 +212,14 @@ int main(int argc, char** argv) {
209 212
210 static struct option long_options[] = { 213 static struct option long_options[] = {
211 // clang-format off 214 // clang-format off
212 {"multiplayer", required_argument, 0, 'm'}, 215 {"config", required_argument, 0, 'c'},
213 {"fullscreen", no_argument, 0, 'f'}, 216 {"fullscreen", no_argument, 0, 'f'},
214 {"help", no_argument, 0, 'h'}, 217 {"help", no_argument, 0, 'h'},
215 {"version", no_argument, 0, 'v'}, 218 {"game", required_argument, 0, 'g'},
219 {"multiplayer", required_argument, 0, 'm'},
216 {"program", optional_argument, 0, 'p'}, 220 {"program", optional_argument, 0, 'p'},
217 {"config", required_argument, 0, 'c'}, 221 {"user", required_argument, 0, 'u'},
222 {"version", no_argument, 0, 'v'},
218 {0, 0, 0, 0}, 223 {0, 0, 0, 0},
219 // clang-format on 224 // clang-format on
220 }; 225 };
@@ -223,6 +228,21 @@ int main(int argc, char** argv) {
223 int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index); 228 int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index);
224 if (arg != -1) { 229 if (arg != -1) {
225 switch (static_cast<char>(arg)) { 230 switch (static_cast<char>(arg)) {
231 case 'c':
232 config_path = optarg;
233 break;
234 case 'f':
235 fullscreen = true;
236 LOG_INFO(Frontend, "Starting in fullscreen mode...");
237 break;
238 case 'h':
239 PrintHelp(argv[0]);
240 return 0;
241 case 'g': {
242 const std::string str_arg(optarg);
243 filepath = str_arg;
244 break;
245 }
226 case 'm': { 246 case 'm': {
227 use_multiplayer = true; 247 use_multiplayer = true;
228 const std::string str_arg(optarg); 248 const std::string str_arg(optarg);
@@ -255,23 +275,16 @@ int main(int argc, char** argv) {
255 } 275 }
256 break; 276 break;
257 } 277 }
258 case 'f': 278 case 'p':
259 fullscreen = true; 279 program_args = argv[optind];
260 LOG_INFO(Frontend, "Starting in fullscreen mode..."); 280 ++optind;
261 break; 281 break;
262 case 'h': 282 case 'u':
263 PrintHelp(argv[0]); 283 selected_user = atoi(optarg);
264 return 0; 284 return 0;
265 case 'v': 285 case 'v':
266 PrintVersion(); 286 PrintVersion();
267 return 0; 287 return 0;
268 case 'p':
269 program_args = argv[optind];
270 ++optind;
271 break;
272 case 'c':
273 config_path = optarg;
274 break;
275 } 288 }
276 } else { 289 } else {
277#ifdef _WIN32 290#ifdef _WIN32
@@ -295,6 +308,10 @@ int main(int argc, char** argv) {
295 Settings::values.program_args = program_args; 308 Settings::values.program_args = program_args;
296 } 309 }
297 310
311 if (selected_user.has_value()) {
312 Settings::values.current_user = std::clamp(*selected_user, 0, 7);
313 }
314
298#ifdef _WIN32 315#ifdef _WIN32
299 LocalFree(argv_w); 316 LocalFree(argv_w);
300#endif 317#endif