summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt44
-rw-r--r--src/core/arm/arm_interface.cpp141
-rw-r--r--src/core/arm/arm_interface.h3
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp93
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp98
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h4
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.h2
-rw-r--r--src/core/arm/symbols.cpp190
-rw-r--r--src/core/arm/symbols.h27
-rw-r--r--src/core/core.cpp1
-rw-r--r--src/core/file_sys/directory.h1
-rw-r--r--src/core/file_sys/patch_manager.cpp66
-rw-r--r--src/core/file_sys/program_metadata.h2
-rw-r--r--src/core/file_sys/registered_cache.cpp6
-rw-r--r--src/core/frontend/applets/mii_edit.cpp18
-rw-r--r--src/core/frontend/applets/mii_edit.h23
-rw-r--r--src/core/hid/emulated_console.cpp17
-rw-r--r--src/core/hid/emulated_console.h1
-rw-r--r--src/core/hid/emulated_controller.cpp377
-rw-r--r--src/core/hid/emulated_controller.h3
-rw-r--r--src/core/hid/emulated_devices.cpp33
-rw-r--r--src/core/hid/emulated_devices.h1
-rw-r--r--src/core/hle/ipc_helpers.h2
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp6
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp16
-rw-r--r--src/core/hle/kernel/hle_ipc.h17
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp101
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.h5
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp6
-rw-r--r--src/core/hle/kernel/k_auto_object.h7
-rw-r--r--src/core/hle/kernel/k_class_token.h2
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp15
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp3
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp8
-rw-r--r--src/core/hle/kernel/k_handle_table.h34
-rw-r--r--src/core/hle/kernel/k_memory_layout.h6
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp1
-rw-r--r--src/core/hle/kernel/k_page_buffer.cpp19
-rw-r--r--src/core/hle/kernel/k_page_buffer.h28
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp379
-rw-r--r--src/core/hle/kernel/k_page_table.h55
-rw-r--r--src/core/hle/kernel/k_port.cpp7
-rw-r--r--src/core/hle/kernel/k_process.cpp196
-rw-r--r--src/core/hle/kernel/k_process.h21
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp1
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h3
-rw-r--r--src/core/hle/kernel/k_server_port.cpp6
-rw-r--r--src/core/hle/kernel/k_server_port.h8
-rw-r--r--src/core/hle/kernel/k_server_session.cpp18
-rw-r--r--src/core/hle/kernel/k_slab_heap.h230
-rw-r--r--src/core/hle/kernel/k_thread.cpp14
-rw-r--r--src/core/hle/kernel/k_thread.h9
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp68
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h111
-rw-r--r--src/core/hle/kernel/kernel.cpp148
-rw-r--r--src/core/hle/kernel/kernel.h43
-rw-r--r--src/core/hle/kernel/physical_core.cpp1
-rw-r--r--src/core/hle/kernel/service_thread.cpp5
-rw-r--r--src/core/hle/kernel/slab_helpers.h2
-rw-r--r--src/core/hle/kernel/svc.cpp16
-rw-r--r--src/core/hle/kernel/svc_types.h2
-rw-r--r--src/core/hle/kernel/time_manager.cpp4
-rw-r--r--src/core/hle/service/acc/acc.cpp1
-rw-r--r--src/core/hle/service/acc/profile_manager.h1
-rw-r--r--src/core/hle/service/am/am.cpp13
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp139
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.h45
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit_types.h83
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp474
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.h35
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard_types.h72
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp8
-rw-r--r--src/core/hle/service/am/applets/applets.cpp15
-rw-r--r--src/core/hle/service/am/applets/applets.h9
-rw-r--r--src/core/hle/service/audio/audout_u.cpp7
-rw-r--r--src/core/hle/service/audio/audren_u.cpp3
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp2
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.cpp1
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.h1
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h4
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp34
-rw-r--r--src/core/hle/service/hid/controllers/npad.h102
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp1
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h3
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp4
-rw-r--r--src/core/hle/service/jit/jit.cpp332
-rw-r--r--src/core/hle/service/jit/jit.h20
-rw-r--r--src/core/hle/service/jit/jit_context.cpp424
-rw-r--r--src/core/hle/service/jit/jit_context.h65
-rw-r--r--src/core/hle/service/kernel_helpers.cpp11
-rw-r--r--src/core/hle/service/ldr/ldr.cpp31
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp3
-rw-r--r--src/core/hle/service/mii/mii_manager.h303
-rw-r--r--src/core/hle/service/mii/raw_data.h2
-rw-r--r--src/core/hle/service/mii/types.h307
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1
-rw-r--r--src/core/hle/service/nfp/nfp.h2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp1
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h11
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h10
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h3
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp14
-rw-r--r--src/core/hle/service/nvflinger/binder.h42
-rw-r--r--src/core/hle/service/nvflinger/buffer_item.h46
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.cpp59
-rw-r--r--src/core/hle/service/nvflinger/buffer_item_consumer.h28
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp206
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h154
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.cpp231
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h37
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp117
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.h79
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_defs.h21
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.cpp923
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h83
-rw-r--r--src/core/hle/service/nvflinger/buffer_slot.h39
-rw-r--r--src/core/hle/service/nvflinger/buffer_transform_flags.h25
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.cpp133
-rw-r--r--src/core/hle/service/nvflinger/consumer_base.h60
-rw-r--r--src/core/hle/service/nvflinger/consumer_listener.h26
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.cpp18
-rw-r--r--src/core/hle/service/nvflinger/graphic_buffer_producer.h76
-rw-r--r--src/core/hle/service/nvflinger/hos_binder_driver_server.cpp36
-rw-r--r--src/core/hle/service/nvflinger/hos_binder_driver_server.h37
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp85
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h26
-rw-r--r--src/core/hle/service/nvflinger/parcel.h172
-rw-r--r--src/core/hle/service/nvflinger/pixel_format.h21
-rw-r--r--src/core/hle/service/nvflinger/producer_listener.h16
-rw-r--r--src/core/hle/service/nvflinger/status.h28
-rw-r--r--src/core/hle/service/nvflinger/ui/fence.h32
-rw-r--r--src/core/hle/service/nvflinger/ui/graphic_buffer.h100
-rw-r--r--src/core/hle/service/nvflinger/window.h53
-rw-r--r--src/core/hle/service/service.cpp13
-rw-r--r--src/core/hle/service/service.h18
-rw-r--r--src/core/hle/service/sm/sm.cpp12
-rw-r--r--src/core/hle/service/sm/sm.h2
-rw-r--r--src/core/hle/service/sockets/bsd.cpp11
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp205
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h1
-rw-r--r--src/core/hle/service/sockets/sockets.h1
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp50
-rw-r--r--src/core/hle/service/vi/display/vi_display.h29
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp6
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h57
-rw-r--r--src/core/hle/service/vi/vi.cpp691
-rw-r--r--src/core/hle/service/vi/vi.h10
-rw-r--r--src/core/hle/service/vi/vi_m.cpp9
-rw-r--r--src/core/hle/service/vi/vi_m.h7
-rw-r--r--src/core/hle/service/vi/vi_s.cpp9
-rw-r--r--src/core/hle/service/vi/vi_s.h7
-rw-r--r--src/core/hle/service/vi/vi_u.cpp9
-rw-r--r--src/core/hle/service/vi/vi_u.h7
-rw-r--r--src/core/network/network.cpp4
-rw-r--r--src/core/network/sockets.h3
-rw-r--r--src/core/perf_stats.cpp10
-rw-r--r--src/core/reporter.cpp1
-rw-r--r--src/core/tools/freezer.cpp18
172 files changed, 6924 insertions, 2706 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5db6a1b3a..b681d21a7 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -13,6 +13,8 @@ add_library(core STATIC
13 arm/dynarmic/arm_exclusive_monitor.h 13 arm/dynarmic/arm_exclusive_monitor.h
14 arm/exclusive_monitor.cpp 14 arm/exclusive_monitor.cpp
15 arm/exclusive_monitor.h 15 arm/exclusive_monitor.h
16 arm/symbols.cpp
17 arm/symbols.h
16 constants.cpp 18 constants.cpp
17 constants.h 19 constants.h
18 core.cpp 20 core.cpp
@@ -122,6 +124,8 @@ add_library(core STATIC
122 frontend/applets/error.h 124 frontend/applets/error.h
123 frontend/applets/general_frontend.cpp 125 frontend/applets/general_frontend.cpp
124 frontend/applets/general_frontend.h 126 frontend/applets/general_frontend.h
127 frontend/applets/mii_edit.cpp
128 frontend/applets/mii_edit.h
125 frontend/applets/profile_select.cpp 129 frontend/applets/profile_select.cpp
126 frontend/applets/profile_select.h 130 frontend/applets/profile_select.h
127 frontend/applets/software_keyboard.cpp 131 frontend/applets/software_keyboard.cpp
@@ -207,6 +211,8 @@ add_library(core STATIC
207 hle/kernel/k_memory_region.h 211 hle/kernel/k_memory_region.h
208 hle/kernel/k_memory_region_type.h 212 hle/kernel/k_memory_region_type.h
209 hle/kernel/k_page_bitmap.h 213 hle/kernel/k_page_bitmap.h
214 hle/kernel/k_page_buffer.cpp
215 hle/kernel/k_page_buffer.h
210 hle/kernel/k_page_heap.cpp 216 hle/kernel/k_page_heap.cpp
211 hle/kernel/k_page_heap.h 217 hle/kernel/k_page_heap.h
212 hle/kernel/k_page_linked_list.h 218 hle/kernel/k_page_linked_list.h
@@ -244,6 +250,8 @@ add_library(core STATIC
244 hle/kernel/k_system_control.h 250 hle/kernel/k_system_control.h
245 hle/kernel/k_thread.cpp 251 hle/kernel/k_thread.cpp
246 hle/kernel/k_thread.h 252 hle/kernel/k_thread.h
253 hle/kernel/k_thread_local_page.cpp
254 hle/kernel/k_thread_local_page.h
247 hle/kernel/k_thread_queue.cpp 255 hle/kernel/k_thread_queue.cpp
248 hle/kernel/k_thread_queue.h 256 hle/kernel/k_thread_queue.h
249 hle/kernel/k_trace.h 257 hle/kernel/k_trace.h
@@ -300,6 +308,9 @@ add_library(core STATIC
300 hle/service/am/applets/applet_error.h 308 hle/service/am/applets/applet_error.h
301 hle/service/am/applets/applet_general_backend.cpp 309 hle/service/am/applets/applet_general_backend.cpp
302 hle/service/am/applets/applet_general_backend.h 310 hle/service/am/applets/applet_general_backend.h
311 hle/service/am/applets/applet_mii_edit.cpp
312 hle/service/am/applets/applet_mii_edit.h
313 hle/service/am/applets/applet_mii_edit_types.h
303 hle/service/am/applets/applet_profile_select.cpp 314 hle/service/am/applets/applet_profile_select.cpp
304 hle/service/am/applets/applet_profile_select.h 315 hle/service/am/applets/applet_profile_select.h
305 hle/service/am/applets/applet_software_keyboard.cpp 316 hle/service/am/applets/applet_software_keyboard.cpp
@@ -449,6 +460,10 @@ add_library(core STATIC
449 hle/service/hid/controllers/touchscreen.h 460 hle/service/hid/controllers/touchscreen.h
450 hle/service/hid/controllers/xpad.cpp 461 hle/service/hid/controllers/xpad.cpp
451 hle/service/hid/controllers/xpad.h 462 hle/service/hid/controllers/xpad.h
463 hle/service/jit/jit_context.cpp
464 hle/service/jit/jit_context.h
465 hle/service/jit/jit.cpp
466 hle/service/jit/jit.h
452 hle/service/lbl/lbl.cpp 467 hle/service/lbl/lbl.cpp
453 hle/service/lbl/lbl.h 468 hle/service/lbl/lbl.h
454 hle/service/ldn/errors.h 469 hle/service/ldn/errors.h
@@ -526,10 +541,35 @@ add_library(core STATIC
526 hle/service/nvdrv/nvmemp.h 541 hle/service/nvdrv/nvmemp.h
527 hle/service/nvdrv/syncpoint_manager.cpp 542 hle/service/nvdrv/syncpoint_manager.cpp
528 hle/service/nvdrv/syncpoint_manager.h 543 hle/service/nvdrv/syncpoint_manager.h
529 hle/service/nvflinger/buffer_queue.cpp 544 hle/service/nvflinger/binder.h
530 hle/service/nvflinger/buffer_queue.h 545 hle/service/nvflinger/buffer_item.h
546 hle/service/nvflinger/buffer_item_consumer.cpp
547 hle/service/nvflinger/buffer_item_consumer.h
548 hle/service/nvflinger/buffer_queue_consumer.cpp
549 hle/service/nvflinger/buffer_queue_consumer.h
550 hle/service/nvflinger/buffer_queue_core.cpp
551 hle/service/nvflinger/buffer_queue_core.h
552 hle/service/nvflinger/buffer_queue_defs.h
553 hle/service/nvflinger/buffer_queue_producer.cpp
554 hle/service/nvflinger/buffer_queue_producer.h
555 hle/service/nvflinger/buffer_slot.h
556 hle/service/nvflinger/buffer_transform_flags.h
557 hle/service/nvflinger/consumer_base.cpp
558 hle/service/nvflinger/consumer_base.h
559 hle/service/nvflinger/consumer_listener.h
560 hle/service/nvflinger/graphic_buffer_producer.cpp
561 hle/service/nvflinger/graphic_buffer_producer.h
562 hle/service/nvflinger/hos_binder_driver_server.cpp
563 hle/service/nvflinger/hos_binder_driver_server.h
531 hle/service/nvflinger/nvflinger.cpp 564 hle/service/nvflinger/nvflinger.cpp
532 hle/service/nvflinger/nvflinger.h 565 hle/service/nvflinger/nvflinger.h
566 hle/service/nvflinger/parcel.h
567 hle/service/nvflinger/pixel_format.h
568 hle/service/nvflinger/producer_listener.h
569 hle/service/nvflinger/status.h
570 hle/service/nvflinger/ui/fence.h
571 hle/service/nvflinger/ui/graphic_buffer.h
572 hle/service/nvflinger/window.h
533 hle/service/olsc/olsc.cpp 573 hle/service/olsc/olsc.cpp
534 hle/service/olsc/olsc.h 574 hle/service/olsc/olsc.h
535 hle/service/pcie/pcie.cpp 575 hle/service/pcie/pcie.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 0951e1976..08bf1201d 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -8,134 +8,13 @@
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/arm/symbols.h"
11#include "core/core.h" 12#include "core/core.h"
13#include "core/hle/kernel/k_process.h"
12#include "core/loader/loader.h" 14#include "core/loader/loader.h"
13#include "core/memory.h" 15#include "core/memory.h"
14 16
15namespace Core { 17namespace Core {
16namespace {
17
18constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
19constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
20constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
21constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
22
23enum class ELFSymbolType : u8 {
24 None = 0,
25 Object = 1,
26 Function = 2,
27 Section = 3,
28 File = 4,
29 Common = 5,
30 TLS = 6,
31};
32
33enum class ELFSymbolBinding : u8 {
34 Local = 0,
35 Global = 1,
36 Weak = 2,
37};
38
39enum class ELFSymbolVisibility : u8 {
40 Default = 0,
41 Internal = 1,
42 Hidden = 2,
43 Protected = 3,
44};
45
46struct ELFSymbol {
47 u32 name_index;
48 union {
49 u8 info;
50
51 BitField<0, 4, ELFSymbolType> type;
52 BitField<4, 4, ELFSymbolBinding> binding;
53 };
54 ELFSymbolVisibility visibility;
55 u16 sh_index;
56 u64 value;
57 u64 size;
58};
59static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
60
61using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
62
63Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
64 const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
65
66 if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
67 memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
68 return {};
69 }
70
71 const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
72
73 VAddr string_table_offset{};
74 VAddr symbol_table_offset{};
75 u64 symbol_entry_size{};
76
77 VAddr dynamic_index = dynamic_offset;
78 while (true) {
79 const u64 tag = memory.Read64(dynamic_index);
80 const u64 value = memory.Read64(dynamic_index + 0x8);
81 dynamic_index += 0x10;
82
83 if (tag == ELF_DYNAMIC_TAG_NULL) {
84 break;
85 }
86
87 if (tag == ELF_DYNAMIC_TAG_STRTAB) {
88 string_table_offset = value;
89 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
90 symbol_table_offset = value;
91 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
92 symbol_entry_size = value;
93 }
94 }
95
96 if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
97 return {};
98 }
99
100 const auto string_table_address = text_offset + string_table_offset;
101 const auto symbol_table_address = text_offset + symbol_table_offset;
102
103 Symbols out;
104
105 VAddr symbol_index = symbol_table_address;
106 while (symbol_index < string_table_address) {
107 ELFSymbol symbol{};
108 memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
109
110 VAddr string_offset = string_table_address + symbol.name_index;
111 std::string name;
112 for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
113 name += static_cast<char>(c);
114 }
115
116 symbol_index += symbol_entry_size;
117 out.push_back({symbol, name});
118 }
119
120 return out;
121}
122
123std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
124 const auto iter =
125 std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
126 const auto& symbol = pair.first;
127 const auto end_address = symbol.value + symbol.size;
128 return func_address >= symbol.value && func_address < end_address;
129 });
130
131 if (iter == symbols.end()) {
132 return std::nullopt;
133 }
134
135 return iter->second;
136}
137
138} // Anonymous namespace
139 18
140constexpr u64 SEGMENT_BASE = 0x7100000000ull; 19constexpr u64 SEGMENT_BASE = 0x7100000000ull;
141 20
@@ -169,9 +48,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
169 return {}; 48 return {};
170 } 49 }
171 50
172 std::map<std::string, Symbols> symbols; 51 std::map<std::string, Symbols::Symbols> symbols;
173 for (const auto& module : modules) { 52 for (const auto& module : modules) {
174 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); 53 symbols.insert_or_assign(module.second,
54 Symbols::GetSymbols(module.first, system.Memory(),
55 system.CurrentProcess()->Is64BitProcess()));
175 } 56 }
176 57
177 for (auto& entry : out) { 58 for (auto& entry : out) {
@@ -193,7 +74,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
193 74
194 const auto symbol_set = symbols.find(entry.module); 75 const auto symbol_set = symbols.find(entry.module);
195 if (symbol_set != symbols.end()) { 76 if (symbol_set != symbols.end()) {
196 const auto symbol = GetSymbolName(symbol_set->second, entry.offset); 77 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
197 if (symbol.has_value()) { 78 if (symbol.has_value()) {
198 // TODO(DarkLordZach): Add demangling of symbol names. 79 // TODO(DarkLordZach): Add demangling of symbol names.
199 entry.name = *symbol; 80 entry.name = *symbol;
@@ -225,9 +106,11 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
225 return {}; 106 return {};
226 } 107 }
227 108
228 std::map<std::string, Symbols> symbols; 109 std::map<std::string, Symbols::Symbols> symbols;
229 for (const auto& module : modules) { 110 for (const auto& module : modules) {
230 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); 111 symbols.insert_or_assign(module.second,
112 Symbols::GetSymbols(module.first, system.Memory(),
113 system.CurrentProcess()->Is64BitProcess()));
231 } 114 }
232 115
233 for (auto& entry : out) { 116 for (auto& entry : out) {
@@ -249,7 +132,7 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
249 132
250 const auto symbol_set = symbols.find(entry.module); 133 const auto symbol_set = symbols.find(entry.module);
251 if (symbol_set != symbols.end()) { 134 if (symbol_set != symbols.end()) {
252 const auto symbol = GetSymbolName(symbol_set->second, entry.offset); 135 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
253 if (symbol.has_value()) { 136 if (symbol.has_value()) {
254 // TODO(DarkLordZach): Add demangling of symbol names. 137 // TODO(DarkLordZach): Add demangling of symbol names.
255 entry.name = *symbol; 138 entry.name = *symbol;
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c60322442..dce2f4195 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -171,6 +171,9 @@ public:
171 /// Prepare core for thread reschedule (if needed to correctly handle state) 171 /// Prepare core for thread reschedule (if needed to correctly handle state)
172 virtual void PrepareReschedule() = 0; 172 virtual void PrepareReschedule() = 0;
173 173
174 /// Signal an interrupt and ask the core to halt as soon as possible.
175 virtual void SignalInterrupt() = 0;
176
174 struct BacktraceEntry { 177 struct BacktraceEntry {
175 std::string module; 178 std::string module;
176 u64 address; 179 u64 address;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index c1c843b8f..6f3d53dad 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -25,6 +25,9 @@ namespace Core {
25 25
26using namespace Common::Literals; 26using namespace Common::Literals;
27 27
28constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
29constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
30
28class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks { 31class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
29public: 32public:
30 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_) 33 explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
@@ -70,11 +73,13 @@ public:
70 } 73 }
71 74
72 void InterpreterFallback(u32 pc, std::size_t num_instructions) override { 75 void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
76 parent.LogBacktrace();
73 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc, 77 UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
74 MemoryReadCode(pc)); 78 MemoryReadCode(pc));
75 } 79 }
76 80
77 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { 81 void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
82 parent.LogBacktrace();
78 LOG_CRITICAL(Core_ARM, 83 LOG_CRITICAL(Core_ARM,
79 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", 84 "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
80 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); 85 exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
@@ -82,15 +87,13 @@ public:
82 } 87 }
83 88
84 void CallSVC(u32 swi) override { 89 void CallSVC(u32 swi) override {
85 parent.svc_called = true;
86 parent.svc_swi = swi; 90 parent.svc_swi = swi;
87 parent.jit->HaltExecution(); 91 parent.jit->HaltExecution(svc_call);
88 } 92 }
89 93
90 void AddTicks(u64 ticks) override { 94 void AddTicks(u64 ticks) override {
91 if (parent.uses_wall_clock) { 95 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
92 return; 96
93 }
94 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 97 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
95 // rough approximation of the amount of executed ticks in the system, it may be thrown off 98 // rough approximation of the amount of executed ticks in the system, it may be thrown off
96 // if not all cores are doing a similar amount of work. Instead of doing this, we should 99 // if not all cores are doing a similar amount of work. Instead of doing this, we should
@@ -106,12 +109,8 @@ public:
106 } 109 }
107 110
108 u64 GetTicksRemaining() override { 111 u64 GetTicksRemaining() override {
109 if (parent.uses_wall_clock) { 112 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
110 if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { 113
111 return minimum_run_cycles;
112 }
113 return 0U;
114 }
115 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); 114 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
116 } 115 }
117 116
@@ -146,10 +145,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
146 145
147 // Timing 146 // Timing
148 config.wall_clock_cntpct = uses_wall_clock; 147 config.wall_clock_cntpct = uses_wall_clock;
148 config.enable_cycle_counting = !uses_wall_clock;
149 149
150 // Code cache size 150 // Code cache size
151 config.code_cache_size = 128_MiB; 151 config.code_cache_size = 512_MiB;
152 config.far_code_offset = 100_MiB; 152 config.far_code_offset = 400_MiB;
153 153
154 // Safe optimizations 154 // Safe optimizations
155 if (Settings::values.cpu_debug_mode) { 155 if (Settings::values.cpu_debug_mode) {
@@ -186,35 +186,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
186 if (!Settings::values.cpuopt_recompile_exclusives) { 186 if (!Settings::values.cpuopt_recompile_exclusives) {
187 config.recompile_on_exclusive_fastmem_failure = false; 187 config.recompile_on_exclusive_fastmem_failure = false;
188 } 188 }
189 } 189 } else {
190 // Unsafe optimizations
191 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
192 config.unsafe_optimizations = true;
193 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
194 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
195 }
196 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
197 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
198 }
199 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
200 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
201 }
202 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
203 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
204 }
205 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
206 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
207 }
208 }
190 209
191 // Unsafe optimizations 210 // Curated optimizations
192 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 211 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
193 config.unsafe_optimizations = true; 212 config.unsafe_optimizations = true;
194 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
195 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
196 }
197 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
198 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
199 }
200 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
201 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
202 }
203 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
205 }
206 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
208 } 217 }
209 }
210 218
211 // Curated optimizations 219 // Paranoia mode for debugging optimizations
212 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 220 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
213 config.unsafe_optimizations = true; 221 config.unsafe_optimizations = false;
214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 222 config.optimizations = Dynarmic::no_optimizations;
215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 223 }
216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
218 } 224 }
219 225
220 return std::make_unique<Dynarmic::A32::Jit>(config); 226 return std::make_unique<Dynarmic::A32::Jit>(config);
@@ -222,13 +228,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
222 228
223void ARM_Dynarmic_32::Run() { 229void ARM_Dynarmic_32::Run() {
224 while (true) { 230 while (true) {
225 jit->Run(); 231 const auto hr = jit->Run();
226 if (!svc_called) { 232 if (Has(hr, svc_call)) {
227 break; 233 Kernel::Svc::Call(system, svc_swi);
228 } 234 }
229 svc_called = false; 235 if (Has(hr, break_loop) || !uses_wall_clock) {
230 Kernel::Svc::Call(system, svc_swi);
231 if (shutdown) {
232 break; 236 break;
233 } 237 }
234 } 238 }
@@ -314,8 +318,11 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
314} 318}
315 319
316void ARM_Dynarmic_32::PrepareReschedule() { 320void ARM_Dynarmic_32::PrepareReschedule() {
317 jit->HaltExecution(); 321 jit->HaltExecution(break_loop);
318 shutdown = true; 322}
323
324void ARM_Dynarmic_32::SignalInterrupt() {
325 jit->HaltExecution(break_loop);
319} 326}
320 327
321void ARM_Dynarmic_32::ClearInstructionCache() { 328void ARM_Dynarmic_32::ClearInstructionCache() {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 5d47b600d..3f68a4ff1 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -57,6 +57,7 @@ public:
57 void LoadContext(const ThreadContext64& ctx) override {} 57 void LoadContext(const ThreadContext64& ctx) override {}
58 58
59 void PrepareReschedule() override; 59 void PrepareReschedule() override;
60 void SignalInterrupt() override;
60 void ClearExclusiveState() override; 61 void ClearExclusiveState() override;
61 62
62 void ClearInstructionCache() override; 63 void ClearInstructionCache() override;
@@ -83,9 +84,6 @@ private:
83 84
84 // SVC callback 85 // SVC callback
85 u32 svc_swi{}; 86 u32 svc_swi{};
86 bool svc_called{};
87
88 bool shutdown{};
89}; 87};
90 88
91} // namespace Core 89} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index aa74fce4d..1fcb2b891 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -26,6 +26,9 @@ namespace Core {
26using Vector = Dynarmic::A64::Vector; 26using Vector = Dynarmic::A64::Vector;
27using namespace Common::Literals; 27using namespace Common::Literals;
28 28
29constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
30constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
31
29class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks { 32class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
30public: 33public:
31 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_) 34 explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
@@ -81,6 +84,7 @@ public:
81 } 84 }
82 85
83 void InterpreterFallback(u64 pc, std::size_t num_instructions) override { 86 void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
87 parent.LogBacktrace();
84 LOG_ERROR(Core_ARM, 88 LOG_ERROR(Core_ARM,
85 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc, 89 "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
86 num_instructions, MemoryReadCode(pc)); 90 num_instructions, MemoryReadCode(pc));
@@ -93,17 +97,19 @@ public:
93 static constexpr u64 ICACHE_LINE_SIZE = 64; 97 static constexpr u64 ICACHE_LINE_SIZE = 64;
94 98
95 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1); 99 const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
96 parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE); 100 parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
97 break; 101 break;
98 } 102 }
99 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU: 103 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
100 parent.ClearInstructionCache(); 104 parent.system.InvalidateCpuInstructionCaches();
101 break; 105 break;
102 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable: 106 case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
103 default: 107 default:
104 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op); 108 LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
105 break; 109 break;
106 } 110 }
111
112 parent.jit->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
107 } 113 }
108 114
109 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override { 115 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -116,21 +122,19 @@ public:
116 return; 122 return;
117 case Dynarmic::A64::Exception::Breakpoint: 123 case Dynarmic::A64::Exception::Breakpoint:
118 default: 124 default:
125 parent.LogBacktrace();
119 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", 126 ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
120 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc)); 127 static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
121 } 128 }
122 } 129 }
123 130
124 void CallSVC(u32 swi) override { 131 void CallSVC(u32 swi) override {
125 parent.svc_called = true;
126 parent.svc_swi = swi; 132 parent.svc_swi = swi;
127 parent.jit->HaltExecution(); 133 parent.jit->HaltExecution(svc_call);
128 } 134 }
129 135
130 void AddTicks(u64 ticks) override { 136 void AddTicks(u64 ticks) override {
131 if (parent.uses_wall_clock) { 137 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
132 return;
133 }
134 138
135 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 139 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
136 // rough approximation of the amount of executed ticks in the system, it may be thrown off 140 // rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -145,12 +149,8 @@ public:
145 } 149 }
146 150
147 u64 GetTicksRemaining() override { 151 u64 GetTicksRemaining() override {
148 if (parent.uses_wall_clock) { 152 ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
149 if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) { 153
150 return minimum_run_cycles;
151 }
152 return 0U;
153 }
154 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0); 154 return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
155 } 155 }
156 156
@@ -206,10 +206,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
206 206
207 // Timing 207 // Timing
208 config.wall_clock_cntpct = uses_wall_clock; 208 config.wall_clock_cntpct = uses_wall_clock;
209 config.enable_cycle_counting = !uses_wall_clock;
209 210
210 // Code cache size 211 // Code cache size
211 config.code_cache_size = 128_MiB; 212 config.code_cache_size = 512_MiB;
212 config.far_code_offset = 100_MiB; 213 config.far_code_offset = 400_MiB;
213 214
214 // Safe optimizations 215 // Safe optimizations
215 if (Settings::values.cpu_debug_mode) { 216 if (Settings::values.cpu_debug_mode) {
@@ -246,35 +247,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
246 if (!Settings::values.cpuopt_recompile_exclusives) { 247 if (!Settings::values.cpuopt_recompile_exclusives) {
247 config.recompile_on_exclusive_fastmem_failure = false; 248 config.recompile_on_exclusive_fastmem_failure = false;
248 } 249 }
249 } 250 } else {
251 // Unsafe optimizations
252 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
253 config.unsafe_optimizations = true;
254 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
255 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
256 }
257 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
258 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
259 }
260 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
261 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
262 }
263 if (Settings::values.cpuopt_unsafe_fastmem_check) {
264 config.fastmem_address_space_bits = 64;
265 }
266 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
267 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
268 }
269 }
250 270
251 // Unsafe optimizations 271 // Curated optimizations
252 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 272 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
253 config.unsafe_optimizations = true; 273 config.unsafe_optimizations = true;
254 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
255 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
256 }
257 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
258 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
259 }
260 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
261 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 275 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
262 }
263 if (Settings::values.cpuopt_unsafe_fastmem_check) {
264 config.fastmem_address_space_bits = 64; 276 config.fastmem_address_space_bits = 64;
265 }
266 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
267 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
268 } 278 }
269 }
270 279
271 // Curated optimizations 280 // Paranoia mode for debugging optimizations
272 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 281 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
273 config.unsafe_optimizations = true; 282 config.unsafe_optimizations = false;
274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 283 config.optimizations = Dynarmic::no_optimizations;
275 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 284 }
276 config.fastmem_address_space_bits = 64;
277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
278 } 285 }
279 286
280 return std::make_shared<Dynarmic::A64::Jit>(config); 287 return std::make_shared<Dynarmic::A64::Jit>(config);
@@ -282,13 +289,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
282 289
283void ARM_Dynarmic_64::Run() { 290void ARM_Dynarmic_64::Run() {
284 while (true) { 291 while (true) {
285 jit->Run(); 292 const auto hr = jit->Run();
286 if (!svc_called) { 293 if (Has(hr, svc_call)) {
287 break; 294 Kernel::Svc::Call(system, svc_swi);
288 } 295 }
289 svc_called = false; 296 if (Has(hr, break_loop) || !uses_wall_clock) {
290 Kernel::Svc::Call(system, svc_swi);
291 if (shutdown) {
292 break; 297 break;
293 } 298 }
294 } 299 }
@@ -379,8 +384,11 @@ void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
379} 384}
380 385
381void ARM_Dynarmic_64::PrepareReschedule() { 386void ARM_Dynarmic_64::PrepareReschedule() {
382 jit->HaltExecution(); 387 jit->HaltExecution(break_loop);
383 shutdown = true; 388}
389
390void ARM_Dynarmic_64::SignalInterrupt() {
391 jit->HaltExecution(break_loop);
384} 392}
385 393
386void ARM_Dynarmic_64::ClearInstructionCache() { 394void ARM_Dynarmic_64::ClearInstructionCache() {
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 0c4e46c64..58bc7fbec 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -51,6 +51,7 @@ public:
51 void LoadContext(const ThreadContext64& ctx) override; 51 void LoadContext(const ThreadContext64& ctx) override;
52 52
53 void PrepareReschedule() override; 53 void PrepareReschedule() override;
54 void SignalInterrupt() override;
54 void ClearExclusiveState() override; 55 void ClearExclusiveState() override;
55 56
56 void ClearInstructionCache() override; 57 void ClearInstructionCache() override;
@@ -77,9 +78,6 @@ private:
77 78
78 // SVC callback 79 // SVC callback
79 u32 svc_swi{}; 80 u32 svc_swi{};
80 bool svc_called{};
81
82 bool shutdown{};
83}; 81};
84 82
85} // namespace Core 83} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h
index 5a15b43ef..b82c77f76 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.h
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h
@@ -4,8 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <unordered_map>
8
9#include <dynarmic/interface/exclusive_monitor.h> 7#include <dynarmic/interface/exclusive_monitor.h>
10 8
11#include "common/common_types.h" 9#include "common/common_types.h"
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
new file mode 100644
index 000000000..26c44f0c7
--- /dev/null
+++ b/src/core/arm/symbols.cpp
@@ -0,0 +1,190 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/bit_field.h"
6#include "common/common_funcs.h"
7#include "core/arm/symbols.h"
8#include "core/core.h"
9#include "core/memory.h"
10
11namespace Core {
12namespace {
13
14constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
15constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
16constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
17constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
18
19enum class ELFSymbolType : u8 {
20 None = 0,
21 Object = 1,
22 Function = 2,
23 Section = 3,
24 File = 4,
25 Common = 5,
26 TLS = 6,
27};
28
29enum class ELFSymbolBinding : u8 {
30 Local = 0,
31 Global = 1,
32 Weak = 2,
33};
34
35enum class ELFSymbolVisibility : u8 {
36 Default = 0,
37 Internal = 1,
38 Hidden = 2,
39 Protected = 3,
40};
41
42struct ELF64Symbol {
43 u32 name_index;
44 union {
45 u8 info;
46
47 BitField<0, 4, ELFSymbolType> type;
48 BitField<4, 4, ELFSymbolBinding> binding;
49 };
50 ELFSymbolVisibility visibility;
51 u16 sh_index;
52 u64 value;
53 u64 size;
54};
55static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size.");
56
57struct ELF32Symbol {
58 u32 name_index;
59 u32 value;
60 u32 size;
61 union {
62 u8 info;
63
64 BitField<0, 4, ELFSymbolType> type;
65 BitField<4, 4, ELFSymbolBinding> binding;
66 };
67 ELFSymbolVisibility visibility;
68 u16 sh_index;
69};
70static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size.");
71
72} // Anonymous namespace
73
74namespace Symbols {
75
76template <typename Word, typename ELFSymbol, typename ByteReader>
77static Symbols GetSymbols(ByteReader ReadBytes) {
78 const auto Read8{[&](u64 index) {
79 u8 ret;
80 ReadBytes(&ret, index, sizeof(u8));
81 return ret;
82 }};
83
84 const auto Read32{[&](u64 index) {
85 u32 ret;
86 ReadBytes(&ret, index, sizeof(u32));
87 return ret;
88 }};
89
90 const auto ReadWord{[&](u64 index) {
91 Word ret;
92 ReadBytes(&ret, index, sizeof(Word));
93 return ret;
94 }};
95
96 const u32 mod_offset = Read32(4);
97
98 if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
99 return {};
100 }
101
102 VAddr string_table_offset{};
103 VAddr symbol_table_offset{};
104 u64 symbol_entry_size{};
105
106 const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
107
108 VAddr dynamic_index = dynamic_offset;
109 while (true) {
110 const Word tag = ReadWord(dynamic_index);
111 const Word value = ReadWord(dynamic_index + sizeof(Word));
112 dynamic_index += 2 * sizeof(Word);
113
114 if (tag == ELF_DYNAMIC_TAG_NULL) {
115 break;
116 }
117
118 if (tag == ELF_DYNAMIC_TAG_STRTAB) {
119 string_table_offset = value;
120 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
121 symbol_table_offset = value;
122 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
123 symbol_entry_size = value;
124 }
125 }
126
127 if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
128 return {};
129 }
130
131 Symbols out;
132
133 VAddr symbol_index = symbol_table_offset;
134 while (symbol_index < string_table_offset) {
135 ELFSymbol symbol{};
136 ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
137
138 VAddr string_offset = string_table_offset + symbol.name_index;
139 std::string name;
140 for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
141 name += static_cast<char>(c);
142 }
143
144 symbol_index += symbol_entry_size;
145 out[name] = std::make_pair(symbol.value, symbol.size);
146 }
147
148 return out;
149}
150
151Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
152 const auto ReadBytes{
153 [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
154
155 if (is_64) {
156 return GetSymbols<u64, ELF64Symbol>(ReadBytes);
157 } else {
158 return GetSymbols<u32, ELF32Symbol>(ReadBytes);
159 }
160}
161
162Symbols GetSymbols(std::span<const u8> data, bool is_64) {
163 const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
164 std::memcpy(ptr, data.data() + offset, size);
165 }};
166
167 if (is_64) {
168 return GetSymbols<u64, ELF64Symbol>(ReadBytes);
169 } else {
170 return GetSymbols<u32, ELF32Symbol>(ReadBytes);
171 }
172}
173
174std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
175 const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
176 const auto& [name, sym_info] = pair;
177 const auto& [start_address, size] = sym_info;
178 const auto end_address = start_address + size;
179 return addr >= start_address && addr < end_address;
180 });
181
182 if (iter == symbols.cend()) {
183 return std::nullopt;
184 }
185
186 return iter->first;
187}
188
189} // namespace Symbols
190} // namespace Core
diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h
new file mode 100644
index 000000000..99e6a9f8e
--- /dev/null
+++ b/src/core/arm/symbols.h
@@ -0,0 +1,27 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <optional>
9#include <span>
10#include <string>
11#include <utility>
12
13#include "common/common_types.h"
14
15namespace Core::Memory {
16class Memory;
17} // namespace Core::Memory
18
19namespace Core::Symbols {
20
21using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
22
23Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
24Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
25std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
26
27} // namespace Core::Symbols
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c60a784c3..b5e2bcae2 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -38,7 +38,6 @@
38#include "core/hle/service/apm/apm_controller.h" 38#include "core/hle/service/apm/apm_controller.h"
39#include "core/hle/service/filesystem/filesystem.h" 39#include "core/hle/service/filesystem/filesystem.h"
40#include "core/hle/service/glue/glue_manager.h" 40#include "core/hle/service/glue/glue_manager.h"
41#include "core/hle/service/hid/hid.h"
42#include "core/hle/service/service.h" 41#include "core/hle/service/service.h"
43#include "core/hle/service/sm/sm.h" 42#include "core/hle/service/sm/sm.h"
44#include "core/hle/service/time/time_manager.h" 43#include "core/hle/service/time/time_manager.h"
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 21c7aefc8..9a3b62117 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <iterator>
9#include "common/common_funcs.h" 8#include "common/common_funcs.h"
10#include "common/common_types.h" 9#include "common/common_types.h"
11 10
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index f19ac4607..73e724f3d 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -10,7 +10,10 @@
10#include "common/hex_util.h" 10#include "common/hex_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/settings.h" 12#include "common/settings.h"
13#ifndef _WIN32
13#include "common/string_util.h" 14#include "common/string_util.h"
15#endif
16
14#include "core/core.h" 17#include "core/core.h"
15#include "core/file_sys/common_funcs.h" 18#include "core/file_sys/common_funcs.h"
16#include "core/file_sys/content_archive.h" 19#include "core/file_sys/content_archive.h"
@@ -145,29 +148,33 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
145 148
146 // LayeredExeFS 149 // LayeredExeFS
147 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id); 150 const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
151 const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
152
153 std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
148 if (load_dir != nullptr && load_dir->GetSize() > 0) { 154 if (load_dir != nullptr && load_dir->GetSize() > 0) {
149 auto patch_dirs = load_dir->GetSubdirectories(); 155 const auto load_patch_dirs = load_dir->GetSubdirectories();
150 std::sort( 156 patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
151 patch_dirs.begin(), patch_dirs.end(), 157 }
152 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
153
154 std::vector<VirtualDir> layers;
155 layers.reserve(patch_dirs.size() + 1);
156 for (const auto& subdir : patch_dirs) {
157 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
158 continue;
159 158
160 auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs"); 159 std::sort(patch_dirs.begin(), patch_dirs.end(),
161 if (exefs_dir != nullptr) 160 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
162 layers.push_back(std::move(exefs_dir));
163 }
164 layers.push_back(exefs);
165 161
166 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); 162 std::vector<VirtualDir> layers;
167 if (layered != nullptr) { 163 layers.reserve(patch_dirs.size() + 1);
168 LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully"); 164 for (const auto& subdir : patch_dirs) {
169 exefs = std::move(layered); 165 if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
170 } 166 continue;
167
168 auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
169 if (exefs_dir != nullptr)
170 layers.push_back(std::move(exefs_dir));
171 }
172 layers.push_back(exefs);
173
174 auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
175 if (layered != nullptr) {
176 LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
177 exefs = std::move(layered);
171 } 178 }
172 179
173 if (Settings::values.dump_exefs) { 180 if (Settings::values.dump_exefs) {
@@ -533,11 +540,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
533 540
534 // SDMC mod directory (RomFS LayeredFS) 541 // SDMC mod directory (RomFS LayeredFS)
535 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id); 542 const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
536 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 && 543 if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) {
537 IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) { 544 std::string types;
538 const auto mod_disabled = 545 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
539 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end(); 546 AppendCommaIfNotEmpty(types, "LayeredExeFS");
540 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS"); 547 }
548 if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
549 AppendCommaIfNotEmpty(types, "LayeredFS");
550 }
551
552 if (!types.empty()) {
553 const auto mod_disabled =
554 std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
555 out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types);
556 }
541 } 557 }
542 558
543 // DLC 559 // DLC
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 1eee916be..32435e123 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -6,7 +6,9 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9
9#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_funcs.h"
10#include "common/common_types.h" 12#include "common/common_types.h"
11#include "common/swap.h" 13#include "common/swap.h"
12#include "core/file_sys/vfs_types.h" 14#include "core/file_sys/vfs_types.h"
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 7a646b5f1..4ada4a69b 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -387,15 +387,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
387 continue; 387 continue;
388 388
389 for (const auto& nca_dir : d2_dir->GetSubdirectories()) { 389 for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
390 if (!FollowsNcaIdFormat(nca_dir->GetName())) 390 if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
391 continue; 391 continue;
392 }
392 393
393 ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20))); 394 ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
394 } 395 }
395 396
396 for (const auto& nca_file : d2_dir->GetFiles()) { 397 for (const auto& nca_file : d2_dir->GetFiles()) {
397 if (!FollowsNcaIdFormat(nca_file->GetName())) 398 if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
398 continue; 399 continue;
400 }
399 401
400 ids.push_back( 402 ids.push_back(
401 Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20))); 403 Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));
diff --git a/src/core/frontend/applets/mii_edit.cpp b/src/core/frontend/applets/mii_edit.cpp
new file mode 100644
index 000000000..fadb5fb15
--- /dev/null
+++ b/src/core/frontend/applets/mii_edit.cpp
@@ -0,0 +1,18 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/frontend/applets/mii_edit.h"
7
8namespace Core::Frontend {
9
10MiiEditApplet::~MiiEditApplet() = default;
11
12void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const {
13 LOG_WARNING(Service_AM, "(STUBBED) called");
14
15 callback();
16}
17
18} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/mii_edit.h b/src/core/frontend/applets/mii_edit.h
new file mode 100644
index 000000000..cca0e931d
--- /dev/null
+++ b/src/core/frontend/applets/mii_edit.h
@@ -0,0 +1,23 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9namespace Core::Frontend {
10
11class MiiEditApplet {
12public:
13 virtual ~MiiEditApplet();
14
15 virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0;
16};
17
18class DefaultMiiEditApplet final : public MiiEditApplet {
19public:
20 void ShowMiiEdit(const std::function<void()>& callback) const override;
21};
22
23} // namespace Core::Frontend
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index eef0ff493..de565048b 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -132,7 +132,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
132} 132}
133 133
134void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) { 134void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
135 std::lock_guard lock{mutex}; 135 std::unique_lock lock{mutex};
136 auto& raw_status = console.motion_values.raw_status; 136 auto& raw_status = console.motion_values.raw_status;
137 auto& emulated = console.motion_values.emulated; 137 auto& emulated = console.motion_values.emulated;
138 138
@@ -151,6 +151,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
151 emulated.UpdateOrientation(raw_status.delta_timestamp); 151 emulated.UpdateOrientation(raw_status.delta_timestamp);
152 152
153 if (is_configuring) { 153 if (is_configuring) {
154 lock.unlock();
154 TriggerOnChange(ConsoleTriggerType::Motion); 155 TriggerOnChange(ConsoleTriggerType::Motion);
155 return; 156 return;
156 } 157 }
@@ -166,6 +167,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
166 // Find what is this value 167 // Find what is this value
167 motion.verticalization_error = 0.0f; 168 motion.verticalization_error = 0.0f;
168 169
170 lock.unlock();
169 TriggerOnChange(ConsoleTriggerType::Motion); 171 TriggerOnChange(ConsoleTriggerType::Motion);
170} 172}
171 173
@@ -173,11 +175,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
173 if (index >= console.touch_values.size()) { 175 if (index >= console.touch_values.size()) {
174 return; 176 return;
175 } 177 }
176 std::lock_guard lock{mutex}; 178 std::unique_lock lock{mutex};
177 179
178 console.touch_values[index] = TransformToTouch(callback); 180 console.touch_values[index] = TransformToTouch(callback);
179 181
180 if (is_configuring) { 182 if (is_configuring) {
183 lock.unlock();
181 TriggerOnChange(ConsoleTriggerType::Touch); 184 TriggerOnChange(ConsoleTriggerType::Touch);
182 return; 185 return;
183 } 186 }
@@ -189,26 +192,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
189 .pressed = console.touch_values[index].pressed.value, 192 .pressed = console.touch_values[index].pressed.value,
190 }; 193 };
191 194
195 lock.unlock();
192 TriggerOnChange(ConsoleTriggerType::Touch); 196 TriggerOnChange(ConsoleTriggerType::Touch);
193} 197}
194 198
195ConsoleMotionValues EmulatedConsole::GetMotionValues() const { 199ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
200 std::scoped_lock lock{mutex};
196 return console.motion_values; 201 return console.motion_values;
197} 202}
198 203
199TouchValues EmulatedConsole::GetTouchValues() const { 204TouchValues EmulatedConsole::GetTouchValues() const {
205 std::scoped_lock lock{mutex};
200 return console.touch_values; 206 return console.touch_values;
201} 207}
202 208
203ConsoleMotion EmulatedConsole::GetMotion() const { 209ConsoleMotion EmulatedConsole::GetMotion() const {
210 std::scoped_lock lock{mutex};
204 return console.motion_state; 211 return console.motion_state;
205} 212}
206 213
207TouchFingerState EmulatedConsole::GetTouch() const { 214TouchFingerState EmulatedConsole::GetTouch() const {
215 std::scoped_lock lock{mutex};
208 return console.touch_state; 216 return console.touch_state;
209} 217}
210 218
211void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) { 219void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
220 std::scoped_lock lock{callback_mutex};
212 for (const auto& poller_pair : callback_list) { 221 for (const auto& poller_pair : callback_list) {
213 const ConsoleUpdateCallback& poller = poller_pair.second; 222 const ConsoleUpdateCallback& poller = poller_pair.second;
214 if (poller.on_change) { 223 if (poller.on_change) {
@@ -218,13 +227,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
218} 227}
219 228
220int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) { 229int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
221 std::lock_guard lock{mutex}; 230 std::scoped_lock lock{callback_mutex};
222 callback_list.insert_or_assign(last_callback_key, update_callback); 231 callback_list.insert_or_assign(last_callback_key, update_callback);
223 return last_callback_key++; 232 return last_callback_key++;
224} 233}
225 234
226void EmulatedConsole::DeleteCallback(int key) { 235void EmulatedConsole::DeleteCallback(int key) {
227 std::lock_guard lock{mutex}; 236 std::scoped_lock lock{callback_mutex};
228 const auto& iterator = callback_list.find(key); 237 const auto& iterator = callback_list.find(key);
229 if (iterator == callback_list.end()) { 238 if (iterator == callback_list.end()) {
230 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 239 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 5eb170823..53677bdc5 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -183,6 +183,7 @@ private:
183 TouchDevices touch_devices; 183 TouchDevices touch_devices;
184 184
185 mutable std::mutex mutex; 185 mutable std::mutex mutex;
186 mutable std::mutex callback_mutex;
186 std::unordered_map<int, ConsoleUpdateCallback> callback_list; 187 std::unordered_map<int, ConsoleUpdateCallback> callback_list;
187 int last_callback_key = 0; 188 int last_callback_key = 0;
188 189
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 7e05666d6..c3f21066c 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -353,14 +353,17 @@ void EmulatedController::DisableConfiguration() {
353} 353}
354 354
355void EmulatedController::EnableSystemButtons() { 355void EmulatedController::EnableSystemButtons() {
356 std::scoped_lock lock{mutex};
356 system_buttons_enabled = true; 357 system_buttons_enabled = true;
357} 358}
358 359
359void EmulatedController::DisableSystemButtons() { 360void EmulatedController::DisableSystemButtons() {
361 std::scoped_lock lock{mutex};
360 system_buttons_enabled = false; 362 system_buttons_enabled = false;
361} 363}
362 364
363void EmulatedController::ResetSystemButtons() { 365void EmulatedController::ResetSystemButtons() {
366 std::scoped_lock lock{mutex};
364 controller.home_button_state.home.Assign(false); 367 controller.home_button_state.home.Assign(false);
365 controller.capture_button_state.capture.Assign(false); 368 controller.capture_button_state.capture.Assign(false);
366} 369}
@@ -494,139 +497,141 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
494 if (index >= controller.button_values.size()) { 497 if (index >= controller.button_values.size()) {
495 return; 498 return;
496 } 499 }
497 { 500 std::unique_lock lock{mutex};
498 std::lock_guard lock{mutex}; 501 bool value_changed = false;
499 bool value_changed = false; 502 const auto new_status = TransformToButton(callback);
500 const auto new_status = TransformToButton(callback); 503 auto& current_status = controller.button_values[index];
501 auto& current_status = controller.button_values[index];
502 504
503 // Only read button values that have the same uuid or are pressed once 505 // Only read button values that have the same uuid or are pressed once
504 if (current_status.uuid != uuid) { 506 if (current_status.uuid != uuid) {
505 if (!new_status.value) { 507 if (!new_status.value) {
506 return; 508 return;
507 }
508 } 509 }
510 }
509 511
510 current_status.toggle = new_status.toggle; 512 current_status.toggle = new_status.toggle;
511 current_status.uuid = uuid; 513 current_status.uuid = uuid;
512
513 // Update button status with current
514 if (!current_status.toggle) {
515 current_status.locked = false;
516 if (current_status.value != new_status.value) {
517 current_status.value = new_status.value;
518 value_changed = true;
519 }
520 } else {
521 // Toggle button and lock status
522 if (new_status.value && !current_status.locked) {
523 current_status.locked = true;
524 current_status.value = !current_status.value;
525 value_changed = true;
526 }
527 514
528 // Unlock button ready for next press 515 // Update button status with current
529 if (!new_status.value && current_status.locked) { 516 if (!current_status.toggle) {
530 current_status.locked = false; 517 current_status.locked = false;
531 } 518 if (current_status.value != new_status.value) {
519 current_status.value = new_status.value;
520 value_changed = true;
532 } 521 }
533 522 } else {
534 if (!value_changed) { 523 // Toggle button and lock status
535 return; 524 if (new_status.value && !current_status.locked) {
525 current_status.locked = true;
526 current_status.value = !current_status.value;
527 value_changed = true;
536 } 528 }
537 529
538 if (is_configuring) { 530 // Unlock button ready for next press
539 controller.npad_button_state.raw = NpadButton::None; 531 if (!new_status.value && current_status.locked) {
540 controller.debug_pad_button_state.raw = 0; 532 current_status.locked = false;
541 TriggerOnChange(ControllerTriggerType::Button, false);
542 return;
543 } 533 }
534 }
544 535
545 switch (index) { 536 if (!value_changed) {
546 case Settings::NativeButton::A: 537 return;
547 controller.npad_button_state.a.Assign(current_status.value); 538 }
548 controller.debug_pad_button_state.a.Assign(current_status.value); 539
549 break; 540 if (is_configuring) {
550 case Settings::NativeButton::B: 541 controller.npad_button_state.raw = NpadButton::None;
551 controller.npad_button_state.b.Assign(current_status.value); 542 controller.debug_pad_button_state.raw = 0;
552 controller.debug_pad_button_state.b.Assign(current_status.value); 543 lock.unlock();
553 break; 544 TriggerOnChange(ControllerTriggerType::Button, false);
554 case Settings::NativeButton::X: 545 return;
555 controller.npad_button_state.x.Assign(current_status.value); 546 }
556 controller.debug_pad_button_state.x.Assign(current_status.value); 547
557 break; 548 switch (index) {
558 case Settings::NativeButton::Y: 549 case Settings::NativeButton::A:
559 controller.npad_button_state.y.Assign(current_status.value); 550 controller.npad_button_state.a.Assign(current_status.value);
560 controller.debug_pad_button_state.y.Assign(current_status.value); 551 controller.debug_pad_button_state.a.Assign(current_status.value);
561 break; 552 break;
562 case Settings::NativeButton::LStick: 553 case Settings::NativeButton::B:
563 controller.npad_button_state.stick_l.Assign(current_status.value); 554 controller.npad_button_state.b.Assign(current_status.value);
564 break; 555 controller.debug_pad_button_state.b.Assign(current_status.value);
565 case Settings::NativeButton::RStick: 556 break;
566 controller.npad_button_state.stick_r.Assign(current_status.value); 557 case Settings::NativeButton::X:
567 break; 558 controller.npad_button_state.x.Assign(current_status.value);
568 case Settings::NativeButton::L: 559 controller.debug_pad_button_state.x.Assign(current_status.value);
569 controller.npad_button_state.l.Assign(current_status.value); 560 break;
570 controller.debug_pad_button_state.l.Assign(current_status.value); 561 case Settings::NativeButton::Y:
571 break; 562 controller.npad_button_state.y.Assign(current_status.value);
572 case Settings::NativeButton::R: 563 controller.debug_pad_button_state.y.Assign(current_status.value);
573 controller.npad_button_state.r.Assign(current_status.value); 564 break;
574 controller.debug_pad_button_state.r.Assign(current_status.value); 565 case Settings::NativeButton::LStick:
575 break; 566 controller.npad_button_state.stick_l.Assign(current_status.value);
576 case Settings::NativeButton::ZL: 567 break;
577 controller.npad_button_state.zl.Assign(current_status.value); 568 case Settings::NativeButton::RStick:
578 controller.debug_pad_button_state.zl.Assign(current_status.value); 569 controller.npad_button_state.stick_r.Assign(current_status.value);
579 break; 570 break;
580 case Settings::NativeButton::ZR: 571 case Settings::NativeButton::L:
581 controller.npad_button_state.zr.Assign(current_status.value); 572 controller.npad_button_state.l.Assign(current_status.value);
582 controller.debug_pad_button_state.zr.Assign(current_status.value); 573 controller.debug_pad_button_state.l.Assign(current_status.value);
583 break; 574 break;
584 case Settings::NativeButton::Plus: 575 case Settings::NativeButton::R:
585 controller.npad_button_state.plus.Assign(current_status.value); 576 controller.npad_button_state.r.Assign(current_status.value);
586 controller.debug_pad_button_state.plus.Assign(current_status.value); 577 controller.debug_pad_button_state.r.Assign(current_status.value);
587 break; 578 break;
588 case Settings::NativeButton::Minus: 579 case Settings::NativeButton::ZL:
589 controller.npad_button_state.minus.Assign(current_status.value); 580 controller.npad_button_state.zl.Assign(current_status.value);
590 controller.debug_pad_button_state.minus.Assign(current_status.value); 581 controller.debug_pad_button_state.zl.Assign(current_status.value);
591 break; 582 break;
592 case Settings::NativeButton::DLeft: 583 case Settings::NativeButton::ZR:
593 controller.npad_button_state.left.Assign(current_status.value); 584 controller.npad_button_state.zr.Assign(current_status.value);
594 controller.debug_pad_button_state.d_left.Assign(current_status.value); 585 controller.debug_pad_button_state.zr.Assign(current_status.value);
595 break; 586 break;
596 case Settings::NativeButton::DUp: 587 case Settings::NativeButton::Plus:
597 controller.npad_button_state.up.Assign(current_status.value); 588 controller.npad_button_state.plus.Assign(current_status.value);
598 controller.debug_pad_button_state.d_up.Assign(current_status.value); 589 controller.debug_pad_button_state.plus.Assign(current_status.value);
599 break; 590 break;
600 case Settings::NativeButton::DRight: 591 case Settings::NativeButton::Minus:
601 controller.npad_button_state.right.Assign(current_status.value); 592 controller.npad_button_state.minus.Assign(current_status.value);
602 controller.debug_pad_button_state.d_right.Assign(current_status.value); 593 controller.debug_pad_button_state.minus.Assign(current_status.value);
603 break; 594 break;
604 case Settings::NativeButton::DDown: 595 case Settings::NativeButton::DLeft:
605 controller.npad_button_state.down.Assign(current_status.value); 596 controller.npad_button_state.left.Assign(current_status.value);
606 controller.debug_pad_button_state.d_down.Assign(current_status.value); 597 controller.debug_pad_button_state.d_left.Assign(current_status.value);
607 break; 598 break;
608 case Settings::NativeButton::SL: 599 case Settings::NativeButton::DUp:
609 controller.npad_button_state.left_sl.Assign(current_status.value); 600 controller.npad_button_state.up.Assign(current_status.value);
610 controller.npad_button_state.right_sl.Assign(current_status.value); 601 controller.debug_pad_button_state.d_up.Assign(current_status.value);
611 break; 602 break;
612 case Settings::NativeButton::SR: 603 case Settings::NativeButton::DRight:
613 controller.npad_button_state.left_sr.Assign(current_status.value); 604 controller.npad_button_state.right.Assign(current_status.value);
614 controller.npad_button_state.right_sr.Assign(current_status.value); 605 controller.debug_pad_button_state.d_right.Assign(current_status.value);
615 break; 606 break;
616 case Settings::NativeButton::Home: 607 case Settings::NativeButton::DDown:
617 if (!system_buttons_enabled) { 608 controller.npad_button_state.down.Assign(current_status.value);
618 break; 609 controller.debug_pad_button_state.d_down.Assign(current_status.value);
619 } 610 break;
620 controller.home_button_state.home.Assign(current_status.value); 611 case Settings::NativeButton::SL:
612 controller.npad_button_state.left_sl.Assign(current_status.value);
613 controller.npad_button_state.right_sl.Assign(current_status.value);
614 break;
615 case Settings::NativeButton::SR:
616 controller.npad_button_state.left_sr.Assign(current_status.value);
617 controller.npad_button_state.right_sr.Assign(current_status.value);
618 break;
619 case Settings::NativeButton::Home:
620 if (!system_buttons_enabled) {
621 break; 621 break;
622 case Settings::NativeButton::Screenshot: 622 }
623 if (!system_buttons_enabled) { 623 controller.home_button_state.home.Assign(current_status.value);
624 break; 624 break;
625 } 625 case Settings::NativeButton::Screenshot:
626 controller.capture_button_state.capture.Assign(current_status.value); 626 if (!system_buttons_enabled) {
627 break; 627 break;
628 } 628 }
629 controller.capture_button_state.capture.Assign(current_status.value);
630 break;
629 } 631 }
632
633 lock.unlock();
634
630 if (!is_connected) { 635 if (!is_connected) {
631 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) { 636 if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
632 Connect(); 637 Connect();
@@ -643,7 +648,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
643 if (index >= controller.stick_values.size()) { 648 if (index >= controller.stick_values.size()) {
644 return; 649 return;
645 } 650 }
646 std::lock_guard lock{mutex}; 651 std::unique_lock lock{mutex};
647 const auto stick_value = TransformToStick(callback); 652 const auto stick_value = TransformToStick(callback);
648 653
649 // Only read stick values that have the same uuid or are over the threshold to avoid flapping 654 // Only read stick values that have the same uuid or are over the threshold to avoid flapping
@@ -659,6 +664,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
659 if (is_configuring) { 664 if (is_configuring) {
660 controller.analog_stick_state.left = {}; 665 controller.analog_stick_state.left = {};
661 controller.analog_stick_state.right = {}; 666 controller.analog_stick_state.right = {};
667 lock.unlock();
662 TriggerOnChange(ControllerTriggerType::Stick, false); 668 TriggerOnChange(ControllerTriggerType::Stick, false);
663 return; 669 return;
664 } 670 }
@@ -685,6 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
685 break; 691 break;
686 } 692 }
687 693
694 lock.unlock();
688 TriggerOnChange(ControllerTriggerType::Stick, true); 695 TriggerOnChange(ControllerTriggerType::Stick, true);
689} 696}
690 697
@@ -693,7 +700,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
693 if (index >= controller.trigger_values.size()) { 700 if (index >= controller.trigger_values.size()) {
694 return; 701 return;
695 } 702 }
696 std::lock_guard lock{mutex}; 703 std::unique_lock lock{mutex};
697 const auto trigger_value = TransformToTrigger(callback); 704 const auto trigger_value = TransformToTrigger(callback);
698 705
699 // Only read trigger values that have the same uuid or are pressed once 706 // Only read trigger values that have the same uuid or are pressed once
@@ -709,6 +716,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
709 if (is_configuring) { 716 if (is_configuring) {
710 controller.gc_trigger_state.left = 0; 717 controller.gc_trigger_state.left = 0;
711 controller.gc_trigger_state.right = 0; 718 controller.gc_trigger_state.right = 0;
719 lock.unlock();
712 TriggerOnChange(ControllerTriggerType::Trigger, false); 720 TriggerOnChange(ControllerTriggerType::Trigger, false);
713 return; 721 return;
714 } 722 }
@@ -727,6 +735,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
727 break; 735 break;
728 } 736 }
729 737
738 lock.unlock();
730 TriggerOnChange(ControllerTriggerType::Trigger, true); 739 TriggerOnChange(ControllerTriggerType::Trigger, true);
731} 740}
732 741
@@ -735,7 +744,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
735 if (index >= controller.motion_values.size()) { 744 if (index >= controller.motion_values.size()) {
736 return; 745 return;
737 } 746 }
738 std::lock_guard lock{mutex}; 747 std::unique_lock lock{mutex};
739 auto& raw_status = controller.motion_values[index].raw_status; 748 auto& raw_status = controller.motion_values[index].raw_status;
740 auto& emulated = controller.motion_values[index].emulated; 749 auto& emulated = controller.motion_values[index].emulated;
741 750
@@ -756,6 +765,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
756 force_update_motion = raw_status.force_update; 765 force_update_motion = raw_status.force_update;
757 766
758 if (is_configuring) { 767 if (is_configuring) {
768 lock.unlock();
759 TriggerOnChange(ControllerTriggerType::Motion, false); 769 TriggerOnChange(ControllerTriggerType::Motion, false);
760 return; 770 return;
761 } 771 }
@@ -767,6 +777,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
767 motion.orientation = emulated.GetOrientation(); 777 motion.orientation = emulated.GetOrientation();
768 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity); 778 motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
769 779
780 lock.unlock();
770 TriggerOnChange(ControllerTriggerType::Motion, true); 781 TriggerOnChange(ControllerTriggerType::Motion, true);
771} 782}
772 783
@@ -775,10 +786,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
775 if (index >= controller.battery_values.size()) { 786 if (index >= controller.battery_values.size()) {
776 return; 787 return;
777 } 788 }
778 std::lock_guard lock{mutex}; 789 std::unique_lock lock{mutex};
779 controller.battery_values[index] = TransformToBattery(callback); 790 controller.battery_values[index] = TransformToBattery(callback);
780 791
781 if (is_configuring) { 792 if (is_configuring) {
793 lock.unlock();
782 TriggerOnChange(ControllerTriggerType::Battery, false); 794 TriggerOnChange(ControllerTriggerType::Battery, false);
783 return; 795 return;
784 } 796 }
@@ -835,6 +847,8 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
835 }; 847 };
836 break; 848 break;
837 } 849 }
850
851 lock.unlock();
838 TriggerOnChange(ControllerTriggerType::Battery, true); 852 TriggerOnChange(ControllerTriggerType::Battery, true);
839} 853}
840 854
@@ -932,6 +946,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
932} 946}
933 947
934bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const { 948bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
949 std::scoped_lock lock{mutex};
935 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 950 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
936 switch (type) { 951 switch (type) {
937 case NpadStyleIndex::ProController: 952 case NpadStyleIndex::ProController:
@@ -947,6 +962,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
947} 962}
948 963
949bool EmulatedController::IsControllerSupported(bool use_temporary_value) const { 964bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
965 std::scoped_lock lock{mutex};
950 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type; 966 const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
951 switch (type) { 967 switch (type) {
952 case NpadStyleIndex::ProController: 968 case NpadStyleIndex::ProController:
@@ -982,40 +998,44 @@ void EmulatedController::Connect(bool use_temporary_value) {
982 LOG_ERROR(Service_HID, "Controller type {} is not supported", type); 998 LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
983 return; 999 return;
984 } 1000 }
985 {
986 std::lock_guard lock{mutex};
987 if (is_configuring) {
988 tmp_is_connected = true;
989 TriggerOnChange(ControllerTriggerType::Connected, false);
990 return;
991 }
992 1001
993 if (is_connected) { 1002 std::unique_lock lock{mutex};
994 return; 1003 if (is_configuring) {
995 } 1004 tmp_is_connected = true;
996 is_connected = true; 1005 lock.unlock();
1006 TriggerOnChange(ControllerTriggerType::Connected, false);
1007 return;
1008 }
1009
1010 if (is_connected) {
1011 return;
997 } 1012 }
1013 is_connected = true;
1014
1015 lock.unlock();
998 TriggerOnChange(ControllerTriggerType::Connected, true); 1016 TriggerOnChange(ControllerTriggerType::Connected, true);
999} 1017}
1000 1018
1001void EmulatedController::Disconnect() { 1019void EmulatedController::Disconnect() {
1002 { 1020 std::unique_lock lock{mutex};
1003 std::lock_guard lock{mutex}; 1021 if (is_configuring) {
1004 if (is_configuring) { 1022 tmp_is_connected = false;
1005 tmp_is_connected = false; 1023 lock.unlock();
1006 TriggerOnChange(ControllerTriggerType::Disconnected, false); 1024 TriggerOnChange(ControllerTriggerType::Disconnected, false);
1007 return; 1025 return;
1008 } 1026 }
1009 1027
1010 if (!is_connected) { 1028 if (!is_connected) {
1011 return; 1029 return;
1012 }
1013 is_connected = false;
1014 } 1030 }
1031 is_connected = false;
1032
1033 lock.unlock();
1015 TriggerOnChange(ControllerTriggerType::Disconnected, true); 1034 TriggerOnChange(ControllerTriggerType::Disconnected, true);
1016} 1035}
1017 1036
1018bool EmulatedController::IsConnected(bool get_temporary_value) const { 1037bool EmulatedController::IsConnected(bool get_temporary_value) const {
1038 std::scoped_lock lock{mutex};
1019 if (get_temporary_value && is_configuring) { 1039 if (get_temporary_value && is_configuring) {
1020 return tmp_is_connected; 1040 return tmp_is_connected;
1021 } 1041 }
@@ -1029,10 +1049,12 @@ bool EmulatedController::IsVibrationEnabled() const {
1029} 1049}
1030 1050
1031NpadIdType EmulatedController::GetNpadIdType() const { 1051NpadIdType EmulatedController::GetNpadIdType() const {
1052 std::scoped_lock lock{mutex};
1032 return npad_id_type; 1053 return npad_id_type;
1033} 1054}
1034 1055
1035NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const { 1056NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
1057 std::scoped_lock lock{mutex};
1036 if (get_temporary_value && is_configuring) { 1058 if (get_temporary_value && is_configuring) {
1037 return tmp_npad_type; 1059 return tmp_npad_type;
1038 } 1060 }
@@ -1040,27 +1062,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
1040} 1062}
1041 1063
1042void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) { 1064void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
1043 { 1065 std::unique_lock lock{mutex};
1044 std::lock_guard lock{mutex};
1045 1066
1046 if (is_configuring) { 1067 if (is_configuring) {
1047 if (tmp_npad_type == npad_type_) { 1068 if (tmp_npad_type == npad_type_) {
1048 return;
1049 }
1050 tmp_npad_type = npad_type_;
1051 TriggerOnChange(ControllerTriggerType::Type, false);
1052 return; 1069 return;
1053 } 1070 }
1071 tmp_npad_type = npad_type_;
1072 lock.unlock();
1073 TriggerOnChange(ControllerTriggerType::Type, false);
1074 return;
1075 }
1054 1076
1055 if (npad_type == npad_type_) { 1077 if (npad_type == npad_type_) {
1056 return; 1078 return;
1057 } 1079 }
1058 if (is_connected) { 1080 if (is_connected) {
1059 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", 1081 LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
1060 NpadIdTypeToIndex(npad_id_type)); 1082 NpadIdTypeToIndex(npad_id_type));
1061 }
1062 npad_type = npad_type_;
1063 } 1083 }
1084 npad_type = npad_type_;
1085
1086 lock.unlock();
1064 TriggerOnChange(ControllerTriggerType::Type, true); 1087 TriggerOnChange(ControllerTriggerType::Type, true);
1065} 1088}
1066 1089
@@ -1088,30 +1111,37 @@ LedPattern EmulatedController::GetLedPattern() const {
1088} 1111}
1089 1112
1090ButtonValues EmulatedController::GetButtonsValues() const { 1113ButtonValues EmulatedController::GetButtonsValues() const {
1114 std::scoped_lock lock{mutex};
1091 return controller.button_values; 1115 return controller.button_values;
1092} 1116}
1093 1117
1094SticksValues EmulatedController::GetSticksValues() const { 1118SticksValues EmulatedController::GetSticksValues() const {
1119 std::scoped_lock lock{mutex};
1095 return controller.stick_values; 1120 return controller.stick_values;
1096} 1121}
1097 1122
1098TriggerValues EmulatedController::GetTriggersValues() const { 1123TriggerValues EmulatedController::GetTriggersValues() const {
1124 std::scoped_lock lock{mutex};
1099 return controller.trigger_values; 1125 return controller.trigger_values;
1100} 1126}
1101 1127
1102ControllerMotionValues EmulatedController::GetMotionValues() const { 1128ControllerMotionValues EmulatedController::GetMotionValues() const {
1129 std::scoped_lock lock{mutex};
1103 return controller.motion_values; 1130 return controller.motion_values;
1104} 1131}
1105 1132
1106ColorValues EmulatedController::GetColorsValues() const { 1133ColorValues EmulatedController::GetColorsValues() const {
1134 std::scoped_lock lock{mutex};
1107 return controller.color_values; 1135 return controller.color_values;
1108} 1136}
1109 1137
1110BatteryValues EmulatedController::GetBatteryValues() const { 1138BatteryValues EmulatedController::GetBatteryValues() const {
1139 std::scoped_lock lock{mutex};
1111 return controller.battery_values; 1140 return controller.battery_values;
1112} 1141}
1113 1142
1114HomeButtonState EmulatedController::GetHomeButtons() const { 1143HomeButtonState EmulatedController::GetHomeButtons() const {
1144 std::scoped_lock lock{mutex};
1115 if (is_configuring) { 1145 if (is_configuring) {
1116 return {}; 1146 return {};
1117 } 1147 }
@@ -1119,6 +1149,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
1119} 1149}
1120 1150
1121CaptureButtonState EmulatedController::GetCaptureButtons() const { 1151CaptureButtonState EmulatedController::GetCaptureButtons() const {
1152 std::scoped_lock lock{mutex};
1122 if (is_configuring) { 1153 if (is_configuring) {
1123 return {}; 1154 return {};
1124 } 1155 }
@@ -1126,6 +1157,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
1126} 1157}
1127 1158
1128NpadButtonState EmulatedController::GetNpadButtons() const { 1159NpadButtonState EmulatedController::GetNpadButtons() const {
1160 std::scoped_lock lock{mutex};
1129 if (is_configuring) { 1161 if (is_configuring) {
1130 return {}; 1162 return {};
1131 } 1163 }
@@ -1133,6 +1165,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
1133} 1165}
1134 1166
1135DebugPadButton EmulatedController::GetDebugPadButtons() const { 1167DebugPadButton EmulatedController::GetDebugPadButtons() const {
1168 std::scoped_lock lock{mutex};
1136 if (is_configuring) { 1169 if (is_configuring) {
1137 return {}; 1170 return {};
1138 } 1171 }
@@ -1140,20 +1173,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
1140} 1173}
1141 1174
1142AnalogSticks EmulatedController::GetSticks() const { 1175AnalogSticks EmulatedController::GetSticks() const {
1176 std::unique_lock lock{mutex};
1177
1143 if (is_configuring) { 1178 if (is_configuring) {
1144 return {}; 1179 return {};
1145 } 1180 }
1181
1146 // Some drivers like stick from buttons need constant refreshing 1182 // Some drivers like stick from buttons need constant refreshing
1147 for (auto& device : stick_devices) { 1183 for (auto& device : stick_devices) {
1148 if (!device) { 1184 if (!device) {
1149 continue; 1185 continue;
1150 } 1186 }
1187 lock.unlock();
1151 device->SoftUpdate(); 1188 device->SoftUpdate();
1189 lock.lock();
1152 } 1190 }
1191
1153 return controller.analog_stick_state; 1192 return controller.analog_stick_state;
1154} 1193}
1155 1194
1156NpadGcTriggerState EmulatedController::GetTriggers() const { 1195NpadGcTriggerState EmulatedController::GetTriggers() const {
1196 std::scoped_lock lock{mutex};
1157 if (is_configuring) { 1197 if (is_configuring) {
1158 return {}; 1198 return {};
1159 } 1199 }
@@ -1161,26 +1201,35 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
1161} 1201}
1162 1202
1163MotionState EmulatedController::GetMotions() const { 1203MotionState EmulatedController::GetMotions() const {
1204 std::unique_lock lock{mutex};
1205
1206 // Some drivers like mouse motion need constant refreshing
1164 if (force_update_motion) { 1207 if (force_update_motion) {
1165 for (auto& device : motion_devices) { 1208 for (auto& device : motion_devices) {
1166 if (!device) { 1209 if (!device) {
1167 continue; 1210 continue;
1168 } 1211 }
1212 lock.unlock();
1169 device->ForceUpdate(); 1213 device->ForceUpdate();
1214 lock.lock();
1170 } 1215 }
1171 } 1216 }
1217
1172 return controller.motion_state; 1218 return controller.motion_state;
1173} 1219}
1174 1220
1175ControllerColors EmulatedController::GetColors() const { 1221ControllerColors EmulatedController::GetColors() const {
1222 std::scoped_lock lock{mutex};
1176 return controller.colors_state; 1223 return controller.colors_state;
1177} 1224}
1178 1225
1179BatteryLevelState EmulatedController::GetBattery() const { 1226BatteryLevelState EmulatedController::GetBattery() const {
1227 std::scoped_lock lock{mutex};
1180 return controller.battery_state; 1228 return controller.battery_state;
1181} 1229}
1182 1230
1183void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) { 1231void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
1232 std::scoped_lock lock{callback_mutex};
1184 for (const auto& poller_pair : callback_list) { 1233 for (const auto& poller_pair : callback_list) {
1185 const ControllerUpdateCallback& poller = poller_pair.second; 1234 const ControllerUpdateCallback& poller = poller_pair.second;
1186 if (!is_npad_service_update && poller.is_npad_service) { 1235 if (!is_npad_service_update && poller.is_npad_service) {
@@ -1193,13 +1242,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
1193} 1242}
1194 1243
1195int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) { 1244int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
1196 std::lock_guard lock{mutex}; 1245 std::scoped_lock lock{callback_mutex};
1197 callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); 1246 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
1198 return last_callback_key++; 1247 return last_callback_key++;
1199} 1248}
1200 1249
1201void EmulatedController::DeleteCallback(int key) { 1250void EmulatedController::DeleteCallback(int key) {
1202 std::lock_guard lock{mutex}; 1251 std::scoped_lock lock{callback_mutex};
1203 const auto& iterator = callback_list.find(key); 1252 const auto& iterator = callback_list.find(key);
1204 if (iterator == callback_list.end()) { 1253 if (iterator == callback_list.end()) {
1205 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 1254 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index aa52f9572..1e224685d 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -400,7 +400,7 @@ private:
400 */ 400 */
401 void TriggerOnChange(ControllerTriggerType type, bool is_service_update); 401 void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
402 402
403 NpadIdType npad_id_type; 403 const NpadIdType npad_id_type;
404 NpadStyleIndex npad_type{NpadStyleIndex::None}; 404 NpadStyleIndex npad_type{NpadStyleIndex::None};
405 NpadStyleTag supported_style_tag{NpadStyleSet::All}; 405 NpadStyleTag supported_style_tag{NpadStyleSet::All};
406 bool is_connected{false}; 406 bool is_connected{false};
@@ -434,6 +434,7 @@ private:
434 StickDevices tas_stick_devices; 434 StickDevices tas_stick_devices;
435 435
436 mutable std::mutex mutex; 436 mutable std::mutex mutex;
437 mutable std::mutex callback_mutex;
437 std::unordered_map<int, ControllerUpdateCallback> callback_list; 438 std::unordered_map<int, ControllerUpdateCallback> callback_list;
438 int last_callback_key = 0; 439 int last_callback_key = 0;
439 440
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index 708480f2d..cc0dcd931 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -169,7 +169,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
169 if (index >= device_status.keyboard_values.size()) { 169 if (index >= device_status.keyboard_values.size()) {
170 return; 170 return;
171 } 171 }
172 std::lock_guard lock{mutex}; 172 std::unique_lock lock{mutex};
173 bool value_changed = false; 173 bool value_changed = false;
174 const auto new_status = TransformToButton(callback); 174 const auto new_status = TransformToButton(callback);
175 auto& current_status = device_status.keyboard_values[index]; 175 auto& current_status = device_status.keyboard_values[index];
@@ -201,6 +201,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
201 } 201 }
202 202
203 if (is_configuring) { 203 if (is_configuring) {
204 lock.unlock();
204 TriggerOnChange(DeviceTriggerType::Keyboard); 205 TriggerOnChange(DeviceTriggerType::Keyboard);
205 return; 206 return;
206 } 207 }
@@ -208,6 +209,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
208 // Index should be converted from NativeKeyboard to KeyboardKeyIndex 209 // Index should be converted from NativeKeyboard to KeyboardKeyIndex
209 UpdateKey(index, current_status.value); 210 UpdateKey(index, current_status.value);
210 211
212 lock.unlock();
211 TriggerOnChange(DeviceTriggerType::Keyboard); 213 TriggerOnChange(DeviceTriggerType::Keyboard);
212} 214}
213 215
@@ -227,7 +229,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
227 if (index >= device_status.keyboard_moddifier_values.size()) { 229 if (index >= device_status.keyboard_moddifier_values.size()) {
228 return; 230 return;
229 } 231 }
230 std::lock_guard lock{mutex}; 232 std::unique_lock lock{mutex};
231 bool value_changed = false; 233 bool value_changed = false;
232 const auto new_status = TransformToButton(callback); 234 const auto new_status = TransformToButton(callback);
233 auto& current_status = device_status.keyboard_moddifier_values[index]; 235 auto& current_status = device_status.keyboard_moddifier_values[index];
@@ -259,6 +261,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
259 } 261 }
260 262
261 if (is_configuring) { 263 if (is_configuring) {
264 lock.unlock();
262 TriggerOnChange(DeviceTriggerType::KeyboardModdifier); 265 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
263 return; 266 return;
264 } 267 }
@@ -289,6 +292,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
289 break; 292 break;
290 } 293 }
291 294
295 lock.unlock();
292 TriggerOnChange(DeviceTriggerType::KeyboardModdifier); 296 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
293} 297}
294 298
@@ -297,7 +301,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
297 if (index >= device_status.mouse_button_values.size()) { 301 if (index >= device_status.mouse_button_values.size()) {
298 return; 302 return;
299 } 303 }
300 std::lock_guard lock{mutex}; 304 std::unique_lock lock{mutex};
301 bool value_changed = false; 305 bool value_changed = false;
302 const auto new_status = TransformToButton(callback); 306 const auto new_status = TransformToButton(callback);
303 auto& current_status = device_status.mouse_button_values[index]; 307 auto& current_status = device_status.mouse_button_values[index];
@@ -329,6 +333,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
329 } 333 }
330 334
331 if (is_configuring) { 335 if (is_configuring) {
336 lock.unlock();
332 TriggerOnChange(DeviceTriggerType::Mouse); 337 TriggerOnChange(DeviceTriggerType::Mouse);
333 return; 338 return;
334 } 339 }
@@ -351,6 +356,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
351 break; 356 break;
352 } 357 }
353 358
359 lock.unlock();
354 TriggerOnChange(DeviceTriggerType::Mouse); 360 TriggerOnChange(DeviceTriggerType::Mouse);
355} 361}
356 362
@@ -359,13 +365,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
359 if (index >= device_status.mouse_analog_values.size()) { 365 if (index >= device_status.mouse_analog_values.size()) {
360 return; 366 return;
361 } 367 }
362 std::lock_guard lock{mutex}; 368 std::unique_lock lock{mutex};
363 const auto analog_value = TransformToAnalog(callback); 369 const auto analog_value = TransformToAnalog(callback);
364 370
365 device_status.mouse_analog_values[index] = analog_value; 371 device_status.mouse_analog_values[index] = analog_value;
366 372
367 if (is_configuring) { 373 if (is_configuring) {
368 device_status.mouse_position_state = {}; 374 device_status.mouse_position_state = {};
375 lock.unlock();
369 TriggerOnChange(DeviceTriggerType::Mouse); 376 TriggerOnChange(DeviceTriggerType::Mouse);
370 return; 377 return;
371 } 378 }
@@ -379,17 +386,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
379 break; 386 break;
380 } 387 }
381 388
389 lock.unlock();
382 TriggerOnChange(DeviceTriggerType::Mouse); 390 TriggerOnChange(DeviceTriggerType::Mouse);
383} 391}
384 392
385void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) { 393void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
386 std::lock_guard lock{mutex}; 394 std::unique_lock lock{mutex};
387 const auto touch_value = TransformToTouch(callback); 395 const auto touch_value = TransformToTouch(callback);
388 396
389 device_status.mouse_stick_value = touch_value; 397 device_status.mouse_stick_value = touch_value;
390 398
391 if (is_configuring) { 399 if (is_configuring) {
392 device_status.mouse_position_state = {}; 400 device_status.mouse_position_state = {};
401 lock.unlock();
393 TriggerOnChange(DeviceTriggerType::Mouse); 402 TriggerOnChange(DeviceTriggerType::Mouse);
394 return; 403 return;
395 } 404 }
@@ -397,42 +406,52 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
397 device_status.mouse_position_state.x = touch_value.x.value; 406 device_status.mouse_position_state.x = touch_value.x.value;
398 device_status.mouse_position_state.y = touch_value.y.value; 407 device_status.mouse_position_state.y = touch_value.y.value;
399 408
409 lock.unlock();
400 TriggerOnChange(DeviceTriggerType::Mouse); 410 TriggerOnChange(DeviceTriggerType::Mouse);
401} 411}
402 412
403KeyboardValues EmulatedDevices::GetKeyboardValues() const { 413KeyboardValues EmulatedDevices::GetKeyboardValues() const {
414 std::scoped_lock lock{mutex};
404 return device_status.keyboard_values; 415 return device_status.keyboard_values;
405} 416}
406 417
407KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { 418KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
419 std::scoped_lock lock{mutex};
408 return device_status.keyboard_moddifier_values; 420 return device_status.keyboard_moddifier_values;
409} 421}
410 422
411MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { 423MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
424 std::scoped_lock lock{mutex};
412 return device_status.mouse_button_values; 425 return device_status.mouse_button_values;
413} 426}
414 427
415KeyboardKey EmulatedDevices::GetKeyboard() const { 428KeyboardKey EmulatedDevices::GetKeyboard() const {
429 std::scoped_lock lock{mutex};
416 return device_status.keyboard_state; 430 return device_status.keyboard_state;
417} 431}
418 432
419KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { 433KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
434 std::scoped_lock lock{mutex};
420 return device_status.keyboard_moddifier_state; 435 return device_status.keyboard_moddifier_state;
421} 436}
422 437
423MouseButton EmulatedDevices::GetMouseButtons() const { 438MouseButton EmulatedDevices::GetMouseButtons() const {
439 std::scoped_lock lock{mutex};
424 return device_status.mouse_button_state; 440 return device_status.mouse_button_state;
425} 441}
426 442
427MousePosition EmulatedDevices::GetMousePosition() const { 443MousePosition EmulatedDevices::GetMousePosition() const {
444 std::scoped_lock lock{mutex};
428 return device_status.mouse_position_state; 445 return device_status.mouse_position_state;
429} 446}
430 447
431AnalogStickState EmulatedDevices::GetMouseWheel() const { 448AnalogStickState EmulatedDevices::GetMouseWheel() const {
449 std::scoped_lock lock{mutex};
432 return device_status.mouse_wheel_state; 450 return device_status.mouse_wheel_state;
433} 451}
434 452
435void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { 453void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
454 std::scoped_lock lock{callback_mutex};
436 for (const auto& poller_pair : callback_list) { 455 for (const auto& poller_pair : callback_list) {
437 const InterfaceUpdateCallback& poller = poller_pair.second; 456 const InterfaceUpdateCallback& poller = poller_pair.second;
438 if (poller.on_change) { 457 if (poller.on_change) {
@@ -442,13 +461,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
442} 461}
443 462
444int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { 463int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
445 std::lock_guard lock{mutex}; 464 std::scoped_lock lock{callback_mutex};
446 callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); 465 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
447 return last_callback_key++; 466 return last_callback_key++;
448} 467}
449 468
450void EmulatedDevices::DeleteCallback(int key) { 469void EmulatedDevices::DeleteCallback(int key) {
451 std::lock_guard lock{mutex}; 470 std::scoped_lock lock{callback_mutex};
452 const auto& iterator = callback_list.find(key); 471 const auto& iterator = callback_list.find(key);
453 if (iterator == callback_list.end()) { 472 if (iterator == callback_list.end()) {
454 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 473 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key);
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 790d3b411..73e9f0293 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -200,6 +200,7 @@ private:
200 MouseStickDevice mouse_stick_device; 200 MouseStickDevice mouse_stick_device;
201 201
202 mutable std::mutex mutex; 202 mutable std::mutex mutex;
203 mutable std::mutex callback_mutex;
203 std::unordered_map<int, InterfaceUpdateCallback> callback_list; 204 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
204 int last_callback_key = 0; 205 int last_callback_key = 0;
205 206
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 026257115..3c4e45fcd 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -385,7 +385,7 @@ public:
385 T PopRaw(); 385 T PopRaw();
386 386
387 template <class T> 387 template <class T>
388 std::shared_ptr<T> PopIpcInterface() { 388 std::weak_ptr<T> PopIpcInterface() {
389 ASSERT(context->Session()->IsDomain()); 389 ASSERT(context->Session()->IsDomain());
390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0); 390 ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
391 return context->GetDomainHandler<T>(Pop<u32>() - 1); 391 return context->GetDomainHandler<T>(Pop<u32>() - 1);
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
index 8027bec00..7765e7848 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp
@@ -148,9 +148,9 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
148} // Anonymous namespace 148} // Anonymous namespace
149 149
150u64 KSystemControl::GenerateRandomU64() { 150u64 KSystemControl::GenerateRandomU64() {
151 static std::random_device device; 151 std::random_device device;
152 static std::mt19937 gen(device()); 152 std::mt19937 gen(device());
153 static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); 153 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
154 return distribution(gen); 154 return distribution(gen);
155} 155}
156 156
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e19544c54..5828ac923 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -17,7 +17,6 @@
17#include "core/hle/kernel/k_auto_object.h" 17#include "core/hle/kernel/k_auto_object.h"
18#include "core/hle/kernel/k_handle_table.h" 18#include "core/hle/kernel/k_handle_table.h"
19#include "core/hle/kernel/k_process.h" 19#include "core/hle/kernel/k_process.h"
20#include "core/hle/kernel/k_readable_event.h"
21#include "core/hle/kernel/k_server_session.h" 20#include "core/hle/kernel/k_server_session.h"
22#include "core/hle/kernel/k_thread.h" 21#include "core/hle/kernel/k_thread.h"
23#include "core/hle/kernel/kernel.h" 22#include "core/hle/kernel/kernel.h"
@@ -25,8 +24,15 @@
25 24
26namespace Kernel { 25namespace Kernel {
27 26
28SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_) 27SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
29 : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {} 28 ServiceThreadType thread_type)
29 : kernel{kernel_} {
30 if (thread_type == ServiceThreadType::CreateNew) {
31 service_thread = kernel.CreateServiceThread(service_name_);
32 } else {
33 service_thread = kernel.GetDefaultServiceThread();
34 }
35}
30 36
31SessionRequestHandler::~SessionRequestHandler() { 37SessionRequestHandler::~SessionRequestHandler() {
32 kernel.ReleaseServiceThread(service_thread); 38 kernel.ReleaseServiceThread(service_thread);
@@ -45,7 +51,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
45 LOG_CRITICAL(IPC, "object_id {} is too big!", object_id); 51 LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
46 return false; 52 return false;
47 } 53 }
48 return DomainHandler(object_id - 1) != nullptr; 54 return !DomainHandler(object_id - 1).expired();
49 } else { 55 } else {
50 return session_handler != nullptr; 56 return session_handler != nullptr;
51 } 57 }
@@ -55,7 +61,7 @@ void SessionRequestHandler::ClientConnected(KServerSession* session) {
55 session->ClientConnected(shared_from_this()); 61 session->ClientConnected(shared_from_this());
56 62
57 // Ensure our server session is tracked globally. 63 // Ensure our server session is tracked globally.
58 kernel.RegisterServerSession(session); 64 kernel.RegisterServerObject(session);
59} 65}
60 66
61void SessionRequestHandler::ClientDisconnected(KServerSession* session) { 67void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 754b41ff6..640146137 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -33,6 +33,11 @@ namespace Service {
33class ServiceFrameworkBase; 33class ServiceFrameworkBase;
34} 34}
35 35
36enum class ServiceThreadType {
37 Default,
38 CreateNew,
39};
40
36namespace Kernel { 41namespace Kernel {
37 42
38class Domain; 43class Domain;
@@ -57,7 +62,8 @@ enum class ThreadWakeupReason;
57 */ 62 */
58class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { 63class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
59public: 64public:
60 SessionRequestHandler(KernelCore& kernel, const char* service_name_); 65 SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
66 ServiceThreadType thread_type);
61 virtual ~SessionRequestHandler(); 67 virtual ~SessionRequestHandler();
62 68
63 /** 69 /**
@@ -94,6 +100,7 @@ protected:
94 std::weak_ptr<ServiceThread> service_thread; 100 std::weak_ptr<ServiceThread> service_thread;
95}; 101};
96 102
103using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
97using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>; 104using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
98 105
99/** 106/**
@@ -139,7 +146,7 @@ public:
139 } 146 }
140 } 147 }
141 148
142 SessionRequestHandlerPtr DomainHandler(std::size_t index) const { 149 SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const {
143 ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index); 150 ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index);
144 return domain_handlers.at(index); 151 return domain_handlers.at(index);
145 } 152 }
@@ -328,10 +335,10 @@ public:
328 335
329 template <typename T> 336 template <typename T>
330 std::shared_ptr<T> GetDomainHandler(std::size_t index) const { 337 std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
331 return std::static_pointer_cast<T>(manager->DomainHandler(index)); 338 return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock());
332 } 339 }
333 340
334 void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) { 341 void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
335 manager = std::move(manager_); 342 manager = std::move(manager_);
336 } 343 }
337 344
@@ -374,7 +381,7 @@ private:
374 u32 handles_offset{}; 381 u32 handles_offset{};
375 u32 domain_offset{}; 382 u32 domain_offset{};
376 383
377 std::shared_ptr<SessionRequestManager> manager; 384 std::weak_ptr<SessionRequestManager> manager;
378 385
379 KernelCore& kernel; 386 KernelCore& kernel;
380 Core::Memory::Memory& memory; 387 Core::Memory::Memory& memory;
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 36fc0944a..b0f773ee0 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -7,19 +7,23 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/device_memory.h"
10#include "core/hardware_properties.h" 11#include "core/hardware_properties.h"
11#include "core/hle/kernel/init/init_slab_setup.h" 12#include "core/hle/kernel/init/init_slab_setup.h"
12#include "core/hle/kernel/k_code_memory.h" 13#include "core/hle/kernel/k_code_memory.h"
13#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
14#include "core/hle/kernel/k_memory_layout.h" 15#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 16#include "core/hle/kernel/k_memory_manager.h"
17#include "core/hle/kernel/k_page_buffer.h"
16#include "core/hle/kernel/k_port.h" 18#include "core/hle/kernel/k_port.h"
17#include "core/hle/kernel/k_process.h" 19#include "core/hle/kernel/k_process.h"
18#include "core/hle/kernel/k_resource_limit.h" 20#include "core/hle/kernel/k_resource_limit.h"
19#include "core/hle/kernel/k_session.h" 21#include "core/hle/kernel/k_session.h"
20#include "core/hle/kernel/k_shared_memory.h" 22#include "core/hle/kernel/k_shared_memory.h"
23#include "core/hle/kernel/k_shared_memory_info.h"
21#include "core/hle/kernel/k_system_control.h" 24#include "core/hle/kernel/k_system_control.h"
22#include "core/hle/kernel/k_thread.h" 25#include "core/hle/kernel/k_thread.h"
26#include "core/hle/kernel/k_thread_local_page.h"
23#include "core/hle/kernel/k_transfer_memory.h" 27#include "core/hle/kernel/k_transfer_memory.h"
24 28
25namespace Kernel::Init { 29namespace Kernel::Init {
@@ -32,9 +36,13 @@ namespace Kernel::Init {
32 HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \ 36 HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
33 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \ 37 HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
34 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \ 38 HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
39 HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
35 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \ 40 HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
36 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \ 41 HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
37 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \ 42 HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
43 HANDLER(KThreadLocalPage, \
44 (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
45 ##__VA_ARGS__) \
38 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__) 46 HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
39 47
40namespace { 48namespace {
@@ -50,38 +58,46 @@ enum KSlabType : u32 {
50// Constexpr counts. 58// Constexpr counts.
51constexpr size_t SlabCountKProcess = 80; 59constexpr size_t SlabCountKProcess = 80;
52constexpr size_t SlabCountKThread = 800; 60constexpr size_t SlabCountKThread = 800;
53constexpr size_t SlabCountKEvent = 700; 61constexpr size_t SlabCountKEvent = 900;
54constexpr size_t SlabCountKInterruptEvent = 100; 62constexpr size_t SlabCountKInterruptEvent = 100;
55constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew. 63constexpr size_t SlabCountKPort = 384;
56constexpr size_t SlabCountKSharedMemory = 80; 64constexpr size_t SlabCountKSharedMemory = 80;
57constexpr size_t SlabCountKTransferMemory = 200; 65constexpr size_t SlabCountKTransferMemory = 200;
58constexpr size_t SlabCountKCodeMemory = 10; 66constexpr size_t SlabCountKCodeMemory = 10;
59constexpr size_t SlabCountKDeviceAddressSpace = 300; 67constexpr size_t SlabCountKDeviceAddressSpace = 300;
60constexpr size_t SlabCountKSession = 933; 68constexpr size_t SlabCountKSession = 1133;
61constexpr size_t SlabCountKLightSession = 100; 69constexpr size_t SlabCountKLightSession = 100;
62constexpr size_t SlabCountKObjectName = 7; 70constexpr size_t SlabCountKObjectName = 7;
63constexpr size_t SlabCountKResourceLimit = 5; 71constexpr size_t SlabCountKResourceLimit = 5;
64constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES; 72constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
65constexpr size_t SlabCountKAlpha = 1; 73constexpr size_t SlabCountKIoPool = 1;
66constexpr size_t SlabCountKBeta = 6; 74constexpr size_t SlabCountKIoRegion = 6;
67 75
68constexpr size_t SlabCountExtraKThread = 160; 76constexpr size_t SlabCountExtraKThread = 160;
69 77
78/// Helper function to translate from the slab virtual address to the reserved location in physical
79/// memory.
80static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) {
81 slab_addr -= memory_layout.GetSlabRegionAddress();
82 return slab_addr + Core::DramMemoryMap::SlabHeapBase;
83}
84
70template <typename T> 85template <typename T>
71VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address, 86VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address,
72 size_t num_objects) { 87 size_t num_objects) {
73 // TODO(bunnei): This is just a place holder. We should initialize the appropriate KSlabHeap for
74 // kernel object type T with the backing kernel memory pointer once we emulate kernel memory.
75 88
76 const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*)); 89 const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
77 VAddr start = Common::AlignUp(address, alignof(T)); 90 VAddr start = Common::AlignUp(address, alignof(T));
78 91
79 // This is intentionally empty. Once KSlabHeap is fully implemented, we can replace this with 92 // This should use the virtual memory address passed in, but currently, we do not setup the
80 // the pointer to emulated memory to pass along. Until then, KSlabHeap will just allocate/free 93 // kernel virtual memory layout. Instead, we simply map these at a region of physical memory
81 // host memory. 94 // that we reserve for the slab heaps.
82 void* backing_kernel_memory{}; 95 // TODO(bunnei): Fix this once we support the kernel virtual memory layout.
83 96
84 if (size > 0) { 97 if (size > 0) {
98 void* backing_kernel_memory{
99 system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))};
100
85 const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1); 101 const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
86 ASSERT(region != nullptr); 102 ASSERT(region != nullptr);
87 ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab)); 103 ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
@@ -91,6 +107,12 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
91 return start + size; 107 return start + size;
92} 108}
93 109
110size_t CalculateSlabHeapGapSize() {
111 constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
112 static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
113 return KernelSlabHeapGapSize;
114}
115
94} // namespace 116} // namespace
95 117
96KSlabResourceCounts KSlabResourceCounts::CreateDefault() { 118KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
@@ -109,8 +131,8 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
109 .num_KObjectName = SlabCountKObjectName, 131 .num_KObjectName = SlabCountKObjectName,
110 .num_KResourceLimit = SlabCountKResourceLimit, 132 .num_KResourceLimit = SlabCountKResourceLimit,
111 .num_KDebug = SlabCountKDebug, 133 .num_KDebug = SlabCountKDebug,
112 .num_KAlpha = SlabCountKAlpha, 134 .num_KIoPool = SlabCountKIoPool,
113 .num_KBeta = SlabCountKBeta, 135 .num_KIoRegion = SlabCountKIoRegion,
114 }; 136 };
115} 137}
116 138
@@ -136,11 +158,34 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
136#undef ADD_SLAB_SIZE 158#undef ADD_SLAB_SIZE
137 159
138 // Add the reserved size. 160 // Add the reserved size.
139 size += KernelSlabHeapGapsSize; 161 size += CalculateSlabHeapGapSize();
140 162
141 return size; 163 return size;
142} 164}
143 165
166void InitializeKPageBufferSlabHeap(Core::System& system) {
167 auto& kernel = system.Kernel();
168
169 const auto& counts = kernel.SlabResourceCounts();
170 const size_t num_pages =
171 counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
172 const size_t slab_size = num_pages * PageSize;
173
174 // Reserve memory from the system resource limit.
175 ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
176
177 // Allocate memory for the slab.
178 constexpr auto AllocateOption = KMemoryManager::EncodeOption(
179 KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
180 const PAddr slab_address =
181 kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
182 ASSERT(slab_address != 0);
183
184 // Initialize the slabheap.
185 KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address),
186 slab_size);
187}
188
144void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { 189void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
145 auto& kernel = system.Kernel(); 190 auto& kernel = system.Kernel();
146 191
@@ -160,13 +205,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
160 } 205 }
161 206
162 // Create an array to represent the gaps between the slabs. 207 // Create an array to represent the gaps between the slabs.
163 const size_t total_gap_size = KernelSlabHeapGapsSize; 208 const size_t total_gap_size = CalculateSlabHeapGapSize();
164 std::array<size_t, slab_types.size()> slab_gaps; 209 std::array<size_t, slab_types.size()> slab_gaps;
165 for (size_t i = 0; i < slab_gaps.size(); i++) { 210 for (auto& slab_gap : slab_gaps) {
166 // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange 211 // Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
167 // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we 212 // is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
168 // will include it ourselves. 213 // will include it ourselves.
169 slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size); 214 slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size);
170 } 215 }
171 216
172 // Sort the array, so that we can treat differences between values as offsets to the starts of 217 // Sort the array, so that we can treat differences between values as offsets to the starts of
@@ -177,13 +222,21 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
177 } 222 }
178 } 223 }
179 224
180 for (size_t i = 0; i < slab_types.size(); i++) { 225 // Track the gaps, so that we can free them to the unused slab tree.
226 VAddr gap_start = address;
227 size_t gap_size = 0;
228
229 for (size_t i = 0; i < slab_gaps.size(); i++) {
181 // Add the random gap to the address. 230 // Add the random gap to the address.
182 address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1]; 231 const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
232 address += cur_gap;
233 gap_size += cur_gap;
183 234
184#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \ 235#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \
185 case KSlabType_##NAME: \ 236 case KSlabType_##NAME: \
186 address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \ 237 if (COUNT > 0) { \
238 address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
239 } \
187 break; 240 break;
188 241
189 // Initialize the slabheap. 242 // Initialize the slabheap.
@@ -192,7 +245,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
192 FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP) 245 FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
193 // If we somehow get an invalid type, abort. 246 // If we somehow get an invalid type, abort.
194 default: 247 default:
195 UNREACHABLE(); 248 UNREACHABLE_MSG("Unknown slab type: {}", slab_types[i]);
249 }
250
251 // If we've hit the end of a gap, free it.
252 if (gap_start + gap_size != address) {
253 gap_start = address;
254 gap_size = 0;
196 } 255 }
197 } 256 }
198} 257}
diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h
index a8f7e0918..f54b67d02 100644
--- a/src/core/hle/kernel/init/init_slab_setup.h
+++ b/src/core/hle/kernel/init/init_slab_setup.h
@@ -32,12 +32,13 @@ struct KSlabResourceCounts {
32 size_t num_KObjectName; 32 size_t num_KObjectName;
33 size_t num_KResourceLimit; 33 size_t num_KResourceLimit;
34 size_t num_KDebug; 34 size_t num_KDebug;
35 size_t num_KAlpha; 35 size_t num_KIoPool;
36 size_t num_KBeta; 36 size_t num_KIoRegion;
37}; 37};
38 38
39void InitializeSlabResourceCounts(KernelCore& kernel); 39void InitializeSlabResourceCounts(KernelCore& kernel);
40size_t CalculateTotalSlabHeapSize(const KernelCore& kernel); 40size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
41void InitializeKPageBufferSlabHeap(Core::System& system);
41void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout); 42void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
42 43
43} // namespace Kernel::Init 44} // namespace Kernel::Init
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 1d1f5e5f8..8cdd0490f 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -115,7 +115,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
115 { 115 {
116 KScopedSchedulerLock sl(kernel); 116 KScopedSchedulerLock sl(kernel);
117 117
118 auto it = thread_tree.nfind_light({addr, -1}); 118 auto it = thread_tree.nfind_key({addr, -1});
119 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 119 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
120 (it->GetAddressArbiterKey() == addr)) { 120 (it->GetAddressArbiterKey() == addr)) {
121 // End the thread's wait. 121 // End the thread's wait.
@@ -148,7 +148,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
148 return ResultInvalidState; 148 return ResultInvalidState;
149 } 149 }
150 150
151 auto it = thread_tree.nfind_light({addr, -1}); 151 auto it = thread_tree.nfind_key({addr, -1});
152 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 152 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
153 (it->GetAddressArbiterKey() == addr)) { 153 (it->GetAddressArbiterKey() == addr)) {
154 // End the thread's wait. 154 // End the thread's wait.
@@ -171,7 +171,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
171 { 171 {
172 [[maybe_unused]] const KScopedSchedulerLock sl(kernel); 172 [[maybe_unused]] const KScopedSchedulerLock sl(kernel);
173 173
174 auto it = thread_tree.nfind_light({addr, -1}); 174 auto it = thread_tree.nfind_key({addr, -1});
175 // Determine the updated value. 175 // Determine the updated value.
176 s32 new_value{}; 176 s32 new_value{};
177 if (count <= 0) { 177 if (count <= 0) {
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 05779f2d5..423e8d8f5 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -89,9 +89,7 @@ public:
89 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) { 89 explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
90 RegisterWithKernel(); 90 RegisterWithKernel();
91 } 91 }
92 virtual ~KAutoObject() { 92 virtual ~KAutoObject() = default;
93 UnregisterWithKernel();
94 }
95 93
96 static KAutoObject* Create(KAutoObject* ptr); 94 static KAutoObject* Create(KAutoObject* ptr);
97 95
@@ -163,11 +161,12 @@ public:
163 do { 161 do {
164 ASSERT(cur_ref_count > 0); 162 ASSERT(cur_ref_count > 0);
165 } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1, 163 } while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
166 std::memory_order_relaxed)); 164 std::memory_order_acq_rel));
167 165
168 // If ref count hits zero, destroy the object. 166 // If ref count hits zero, destroy the object.
169 if (cur_ref_count - 1 == 0) { 167 if (cur_ref_count - 1 == 0) {
170 this->Destroy(); 168 this->Destroy();
169 this->UnregisterWithKernel();
171 } 170 }
172 } 171 }
173 172
diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h
index 980010150..93fc8786d 100644
--- a/src/core/hle/kernel/k_class_token.h
+++ b/src/core/hle/kernel/k_class_token.h
@@ -4,8 +4,6 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
8
9#include "common/bit_util.h" 7#include "common/bit_util.h"
10#include "common/common_types.h" 8#include "common/common_types.h"
11 9
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 0b225e8e0..09eaf004c 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -5,7 +5,6 @@
5#include "common/alignment.h" 5#include "common/alignment.h"
6#include "common/common_types.h" 6#include "common/common_types.h"
7#include "core/device_memory.h" 7#include "core/device_memory.h"
8#include "core/hle/kernel/k_auto_object.h"
9#include "core/hle/kernel/k_code_memory.h" 8#include "core/hle/kernel/k_code_memory.h"
10#include "core/hle/kernel/k_light_lock.h" 9#include "core/hle/kernel/k_light_lock.h"
11#include "core/hle/kernel/k_memory_block.h" 10#include "core/hle/kernel/k_memory_block.h"
@@ -29,15 +28,21 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
29 auto& page_table = m_owner->PageTable(); 28 auto& page_table = m_owner->PageTable();
30 29
31 // Construct the page group. 30 // Construct the page group.
32 m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize)); 31 m_page_group =
32 KPageLinkedList(page_table.GetPhysicalAddr(addr), Common::DivideUp(size, PageSize));
33 33
34 // Lock the memory. 34 // Lock the memory.
35 R_TRY(page_table.LockForCodeMemory(addr, size)) 35 R_TRY(page_table.LockForCodeMemory(addr, size))
36 36
37 // Clear the memory. 37 // Clear the memory.
38 for (const auto& block : m_page_group.Nodes()) { 38 //
39 std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize()); 39 // FIXME: this ends up clobbering address ranges outside the scope of the mapping within
40 } 40 // guest memory, and is not specifically required if the guest program is correctly
41 // written, so disable until this is further investigated.
42 //
43 // for (const auto& block : m_page_group.Nodes()) {
44 // std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
45 // }
41 46
42 // Set remaining tracking members. 47 // Set remaining tracking members.
43 m_address = addr; 48 m_address = addr;
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index aadcc297a..aa29922d0 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -9,7 +9,6 @@
9#include "core/hle/kernel/k_process.h" 9#include "core/hle/kernel/k_process.h"
10#include "core/hle/kernel/k_scheduler.h" 10#include "core/hle/kernel/k_scheduler.h"
11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" 11#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
12#include "core/hle/kernel/k_synchronization_object.h"
13#include "core/hle/kernel/k_thread.h" 12#include "core/hle/kernel/k_thread.h"
14#include "core/hle/kernel/k_thread_queue.h" 13#include "core/hle/kernel/k_thread_queue.h"
15#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
@@ -244,7 +243,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
244 { 243 {
245 KScopedSchedulerLock sl(kernel); 244 KScopedSchedulerLock sl(kernel);
246 245
247 auto it = thread_tree.nfind_light({cv_key, -1}); 246 auto it = thread_tree.nfind_key({cv_key, -1});
248 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && 247 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
249 (it->GetConditionVariableKey() == cv_key)) { 248 (it->GetConditionVariableKey() == cv_key)) {
250 KThread* target_thread = std::addressof(*it); 249 KThread* target_thread = std::addressof(*it);
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index cf95f0852..db7512ee7 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -63,7 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
63 return true; 63 return true;
64} 64}
65 65
66ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { 66ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
67 KScopedDisableDispatch dd(kernel); 67 KScopedDisableDispatch dd(kernel);
68 KScopedSpinLock lk(m_lock); 68 KScopedSpinLock lk(m_lock);
69 69
@@ -75,7 +75,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
75 const auto linear_id = this->AllocateLinearId(); 75 const auto linear_id = this->AllocateLinearId();
76 const auto index = this->AllocateEntry(); 76 const auto index = this->AllocateEntry();
77 77
78 m_entry_infos[index].info = {.linear_id = linear_id, .type = type}; 78 m_entry_infos[index].linear_id = linear_id;
79 m_objects[index] = obj; 79 m_objects[index] = obj;
80 80
81 obj->Open(); 81 obj->Open();
@@ -116,7 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
116 } 116 }
117} 117}
118 118
119void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { 119void KHandleTable::Register(Handle handle, KAutoObject* obj) {
120 KScopedDisableDispatch dd(kernel); 120 KScopedDisableDispatch dd(kernel);
121 KScopedSpinLock lk(m_lock); 121 KScopedSpinLock lk(m_lock);
122 122
@@ -132,7 +132,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
132 // Set the entry. 132 // Set the entry.
133 ASSERT(m_objects[index] == nullptr); 133 ASSERT(m_objects[index] == nullptr);
134 134
135 m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type}; 135 m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
136 m_objects[index] = obj; 136 m_objects[index] = obj;
137 137
138 obj->Open(); 138 obj->Open();
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 87004a0f9..dd27689b6 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -42,7 +42,7 @@ public:
42 m_free_head_index = -1; 42 m_free_head_index = -1;
43 43
44 // Free all entries. 44 // Free all entries.
45 for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) { 45 for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
46 m_objects[i] = nullptr; 46 m_objects[i] = nullptr;
47 m_entry_infos[i].next_free_index = i - 1; 47 m_entry_infos[i].next_free_index = i - 1;
48 m_free_head_index = i; 48 m_free_head_index = i;
@@ -104,17 +104,8 @@ public:
104 ResultCode Reserve(Handle* out_handle); 104 ResultCode Reserve(Handle* out_handle);
105 void Unreserve(Handle handle); 105 void Unreserve(Handle handle);
106 106
107 template <typename T> 107 ResultCode Add(Handle* out_handle, KAutoObject* obj);
108 ResultCode Add(Handle* out_handle, T* obj) { 108 void Register(Handle handle, KAutoObject* obj);
109 static_assert(std::is_base_of_v<KAutoObject, T>);
110 return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken());
111 }
112
113 template <typename T>
114 void Register(Handle handle, T* obj) {
115 static_assert(std::is_base_of_v<KAutoObject, T>);
116 return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
117 }
118 109
119 template <typename T> 110 template <typename T>
120 bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const { 111 bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
@@ -160,9 +151,6 @@ public:
160 } 151 }
161 152
162private: 153private:
163 ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type);
164 void Register(Handle handle, KAutoObject* obj, u16 type);
165
166 s32 AllocateEntry() { 154 s32 AllocateEntry() {
167 ASSERT(m_count < m_table_size); 155 ASSERT(m_count < m_table_size);
168 156
@@ -179,7 +167,7 @@ private:
179 ASSERT(m_count > 0); 167 ASSERT(m_count > 0);
180 168
181 m_objects[index] = nullptr; 169 m_objects[index] = nullptr;
182 m_entry_infos[index].next_free_index = m_free_head_index; 170 m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
183 171
184 m_free_head_index = index; 172 m_free_head_index = index;
185 173
@@ -278,19 +266,13 @@ private:
278 } 266 }
279 267
280 union EntryInfo { 268 union EntryInfo {
281 struct { 269 u16 linear_id;
282 u16 linear_id; 270 s16 next_free_index;
283 u16 type;
284 } info;
285 s32 next_free_index;
286 271
287 constexpr u16 GetLinearId() const { 272 constexpr u16 GetLinearId() const {
288 return info.linear_id; 273 return linear_id;
289 }
290 constexpr u16 GetType() const {
291 return info.type;
292 } 274 }
293 constexpr s32 GetNextFreeIndex() const { 275 constexpr s16 GetNextFreeIndex() const {
294 return next_free_index; 276 return next_free_index;
295 } 277 }
296 }; 278 };
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index bcddb0d62..0858827b6 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -57,11 +57,11 @@ constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemor
57constexpr std::size_t KernelInitialPageHeapSize = 128_KiB; 57constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
58 58
59constexpr std::size_t KernelSlabHeapDataSize = 5_MiB; 59constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
60constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB; 60constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
61constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; 61constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
62 62
63// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. 63// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
64constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB; 64constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
65 65
66constexpr std::size_t KernelResourceSize = 66constexpr std::size_t KernelResourceSize =
67 KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; 67 KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index a2f18f643..17a1b76c7 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -15,7 +15,6 @@
15#include "core/hle/kernel/k_page_linked_list.h" 15#include "core/hle/kernel/k_page_linked_list.h"
16#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/svc_results.h" 17#include "core/hle/kernel/svc_results.h"
18#include "core/memory.h"
19 18
20namespace Kernel { 19namespace Kernel {
21 20
diff --git a/src/core/hle/kernel/k_page_buffer.cpp b/src/core/hle/kernel/k_page_buffer.cpp
new file mode 100644
index 000000000..f7df4a9a8
--- /dev/null
+++ b/src/core/hle/kernel/k_page_buffer.cpp
@@ -0,0 +1,19 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/alignment.h"
6#include "common/assert.h"
7#include "core/core.h"
8#include "core/device_memory.h"
9#include "core/hle/kernel/k_page_buffer.h"
10#include "core/hle/kernel/memory_types.h"
11
12namespace Kernel {
13
14KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) {
15 ASSERT(Common::IsAligned(phys_addr, PageSize));
16 return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr));
17}
18
19} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_buffer.h b/src/core/hle/kernel/k_page_buffer.h
new file mode 100644
index 000000000..1d11a4e27
--- /dev/null
+++ b/src/core/hle/kernel/k_page_buffer.h
@@ -0,0 +1,28 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10#include "core/hle/kernel/memory_types.h"
11#include "core/hle/kernel/slab_helpers.h"
12
13namespace Kernel {
14
15class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
16public:
17 KPageBuffer() = default;
18
19 static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
20
21private:
22 [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
23};
24
25static_assert(sizeof(KPageBuffer) == PageSize);
26static_assert(alignof(KPageBuffer) == PageSize);
27
28} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 0e2ae582a..869228322 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -89,6 +89,10 @@ public:
89 return ResultSuccess; 89 return ResultSuccess;
90 } 90 }
91 91
92 bool Empty() const {
93 return nodes.empty();
94 }
95
92private: 96private:
93 std::list<Node> nodes; 97 std::list<Node> nodes;
94}; 98};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 0602de1f7..47ea3c89c 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -346,7 +346,8 @@ ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::
346 return ResultSuccess; 346 return ResultSuccess;
347} 347}
348 348
349ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) { 349ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
350 ICacheInvalidationStrategy icache_invalidation_strategy) {
350 // Validate the mapping request. 351 // Validate the mapping request.
351 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), 352 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
352 ResultInvalidMemoryRegion); 353 ResultInvalidMemoryRegion);
@@ -396,7 +397,11 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std
396 bool reprotected_pages = false; 397 bool reprotected_pages = false;
397 SCOPE_EXIT({ 398 SCOPE_EXIT({
398 if (reprotected_pages && any_code_pages) { 399 if (reprotected_pages && any_code_pages) {
399 system.InvalidateCpuInstructionCacheRange(dst_address, size); 400 if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
401 system.InvalidateCpuInstructionCacheRange(dst_address, size);
402 } else {
403 system.InvalidateCpuInstructionCaches();
404 }
400 } 405 }
401 }); 406 });
402 407
@@ -424,6 +429,120 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std
424 return ResultSuccess; 429 return ResultSuccess;
425} 430}
426 431
432VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
433 std::size_t num_pages, std::size_t alignment, std::size_t offset,
434 std::size_t guard_pages) {
435 VAddr address = 0;
436
437 if (num_pages <= region_num_pages) {
438 if (this->IsAslrEnabled()) {
439 // Try to directly find a free area up to 8 times.
440 for (std::size_t i = 0; i < 8; i++) {
441 const std::size_t random_offset =
442 KSystemControl::GenerateRandomRange(
443 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
444 alignment;
445 const VAddr candidate =
446 Common::AlignDown((region_start + random_offset), alignment) + offset;
447
448 KMemoryInfo info = this->QueryInfoImpl(candidate);
449
450 if (info.state != KMemoryState::Free) {
451 continue;
452 }
453 if (region_start > candidate) {
454 continue;
455 }
456 if (info.GetAddress() + guard_pages * PageSize > candidate) {
457 continue;
458 }
459
460 const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
461 if (candidate_end > info.GetLastAddress()) {
462 continue;
463 }
464 if (candidate_end > region_start + region_num_pages * PageSize - 1) {
465 continue;
466 }
467
468 address = candidate;
469 break;
470 }
471 // Fall back to finding the first free area with a random offset.
472 if (address == 0) {
473 // NOTE: Nintendo does not account for guard pages here.
474 // This may theoretically cause an offset to be chosen that cannot be mapped. We
475 // will account for guard pages.
476 const std::size_t offset_pages = KSystemControl::GenerateRandomRange(
477 0, region_num_pages - num_pages - guard_pages);
478 address = block_manager->FindFreeArea(region_start + offset_pages * PageSize,
479 region_num_pages - offset_pages, num_pages,
480 alignment, offset, guard_pages);
481 }
482 }
483
484 // Find the first free area.
485 if (address == 0) {
486 address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages,
487 alignment, offset, guard_pages);
488 }
489 }
490
491 return address;
492}
493
494ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
495 ASSERT(this->IsLockedByCurrentThread());
496
497 const size_t size = num_pages * PageSize;
498
499 // We're making a new group, not adding to an existing one.
500 R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
501
502 // Begin traversal.
503 Common::PageTable::TraversalContext context;
504 Common::PageTable::TraversalEntry next_entry;
505 R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
506
507 // Prepare tracking variables.
508 PAddr cur_addr = next_entry.phys_addr;
509 size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
510 size_t tot_size = cur_size;
511
512 // Iterate, adding to group as we go.
513 const auto& memory_layout = system.Kernel().MemoryLayout();
514 while (tot_size < size) {
515 R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
516 ResultInvalidCurrentMemory);
517
518 if (next_entry.phys_addr != (cur_addr + cur_size)) {
519 const size_t cur_pages = cur_size / PageSize;
520
521 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
522 R_TRY(pg.AddBlock(cur_addr, cur_pages));
523
524 cur_addr = next_entry.phys_addr;
525 cur_size = next_entry.block_size;
526 } else {
527 cur_size += next_entry.block_size;
528 }
529
530 tot_size += next_entry.block_size;
531 }
532
533 // Ensure we add the right amount for the last block.
534 if (tot_size > size) {
535 cur_size -= (tot_size - size);
536 }
537
538 // Add the last block.
539 const size_t cur_pages = cur_size / PageSize;
540 R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
541 R_TRY(pg.AddBlock(cur_addr, cur_pages));
542
543 return ResultSuccess;
544}
545
427ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, 546ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
428 KPageTable& src_page_table, VAddr src_addr) { 547 KPageTable& src_page_table, VAddr src_addr) {
429 KScopedLightLock lk(general_lock); 548 KScopedLightLock lk(general_lock);
@@ -449,6 +568,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
449 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, 568 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
450 KMemoryAttribute::None); 569 KMemoryAttribute::None);
451 570
571 system.InvalidateCpuInstructionCaches();
572
452 return ResultSuccess; 573 return ResultSuccess;
453} 574}
454 575
@@ -1055,6 +1176,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
1055 return ResultSuccess; 1176 return ResultSuccess;
1056} 1177}
1057 1178
1179ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
1180 PAddr phys_addr, bool is_pa_valid, VAddr region_start,
1181 std::size_t region_num_pages, KMemoryState state,
1182 KMemoryPermission perm) {
1183 ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
1184
1185 // Ensure this is a valid map request.
1186 R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
1187 ResultInvalidCurrentMemory);
1188 R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
1189
1190 // Lock the table.
1191 KScopedLightLock lk(general_lock);
1192
1193 // Find a random address to map at.
1194 VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
1195 this->GetNumGuardPages());
1196 R_UNLESS(addr != 0, ResultOutOfMemory);
1197 ASSERT(Common::IsAligned(addr, alignment));
1198 ASSERT(this->CanContain(addr, num_pages * PageSize, state));
1199 ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
1200 KMemoryPermission::None, KMemoryPermission::None,
1201 KMemoryAttribute::None, KMemoryAttribute::None)
1202 .IsSuccess());
1203
1204 // Perform mapping operation.
1205 if (is_pa_valid) {
1206 R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
1207 } else {
1208 UNIMPLEMENTED();
1209 }
1210
1211 // Update the blocks.
1212 block_manager->Update(addr, num_pages, state, perm);
1213
1214 // We successfully mapped the pages.
1215 *out_addr = addr;
1216 return ResultSuccess;
1217}
1218
1058ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { 1219ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
1059 ASSERT(this->IsLockedByCurrentThread()); 1220 ASSERT(this->IsLockedByCurrentThread());
1060 1221
@@ -1097,6 +1258,55 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
1097 return ResultSuccess; 1258 return ResultSuccess;
1098} 1259}
1099 1260
1261ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
1262 // Check that the unmap is in range.
1263 const std::size_t size = num_pages * PageSize;
1264 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1265
1266 // Lock the table.
1267 KScopedLightLock lk(general_lock);
1268
1269 // Check the memory state.
1270 std::size_t num_allocator_blocks{};
1271 R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
1272 KMemoryState::All, state, KMemoryPermission::None,
1273 KMemoryPermission::None, KMemoryAttribute::All,
1274 KMemoryAttribute::None));
1275
1276 // Perform the unmap.
1277 R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
1278
1279 // Update the blocks.
1280 block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None);
1281
1282 return ResultSuccess;
1283}
1284
1285ResultCode KPageTable::MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
1286 KMemoryState state_mask, KMemoryState state,
1287 KMemoryPermission perm_mask, KMemoryPermission perm,
1288 KMemoryAttribute attr_mask, KMemoryAttribute attr) {
1289 // Ensure that the page group isn't null.
1290 ASSERT(out != nullptr);
1291
1292 // Make sure that the region we're mapping is valid for the table.
1293 const size_t size = num_pages * PageSize;
1294 R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
1295
1296 // Lock the table.
1297 KScopedLightLock lk(general_lock);
1298
1299 // Check if state allows us to create the group.
1300 R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
1301 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
1302 attr_mask, attr));
1303
1304 // Create a new page group for the region.
1305 R_TRY(this->MakePageGroup(*out, address, num_pages));
1306
1307 return ResultSuccess;
1308}
1309
1100ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1310ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1101 Svc::MemoryPermission svc_perm) { 1311 Svc::MemoryPermission svc_perm) {
1102 const size_t num_pages = size / PageSize; 1312 const size_t num_pages = size / PageSize;
@@ -1479,57 +1689,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1479} 1689}
1480 1690
1481ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1691ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1482 KScopedLightLock lk(general_lock); 1692 return this->LockMemoryAndOpen(
1483 1693 nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1484 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; 1694 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1485 1695 KMemoryAttribute::All, KMemoryAttribute::None,
1486 KMemoryPermission old_perm{}; 1696 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1487 1697 KMemoryPermission::KernelReadWrite),
1488 if (const ResultCode result{CheckMemoryState( 1698 KMemoryAttribute::Locked);
1489 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1490 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
1491 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
1492 result.IsError()) {
1493 return result;
1494 }
1495
1496 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1497
1498 block_manager->UpdateLock(
1499 addr, size / PageSize,
1500 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1501 block->ShareToDevice(permission);
1502 },
1503 new_perm);
1504
1505 return ResultSuccess;
1506} 1699}
1507 1700
1508ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1701ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1509 KScopedLightLock lk(general_lock); 1702 return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
1510 1703 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
1511 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; 1704 KMemoryPermission::None, KMemoryAttribute::All,
1512 1705 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
1513 KMemoryPermission old_perm{}; 1706 KMemoryAttribute::Locked, nullptr);
1514
1515 if (const ResultCode result{CheckMemoryState(
1516 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1517 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
1518 KMemoryAttribute::All, KMemoryAttribute::Locked)};
1519 result.IsError()) {
1520 return result;
1521 }
1522
1523 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1524
1525 block_manager->UpdateLock(
1526 addr, size / PageSize,
1527 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1528 block->UnshareToDevice(permission);
1529 },
1530 new_perm);
1531
1532 return ResultSuccess;
1533} 1707}
1534 1708
1535ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1709ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1865,4 +2039,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
1865 return ResultSuccess; 2039 return ResultSuccess;
1866} 2040}
1867 2041
2042ResultCode KPageTable::LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr,
2043 size_t size, KMemoryState state_mask, KMemoryState state,
2044 KMemoryPermission perm_mask, KMemoryPermission perm,
2045 KMemoryAttribute attr_mask, KMemoryAttribute attr,
2046 KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
2047 // Validate basic preconditions.
2048 ASSERT((lock_attr & attr) == KMemoryAttribute::None);
2049 ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
2050 KMemoryAttribute::None);
2051
2052 // Validate the lock request.
2053 const size_t num_pages = size / PageSize;
2054 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2055
2056 // Lock the table.
2057 KScopedLightLock lk(general_lock);
2058
2059 // Check that the output page group is empty, if it exists.
2060 if (out_pg) {
2061 ASSERT(out_pg->GetNumPages() == 0);
2062 }
2063
2064 // Check the state.
2065 KMemoryState old_state{};
2066 KMemoryPermission old_perm{};
2067 KMemoryAttribute old_attr{};
2068 size_t num_allocator_blocks{};
2069 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2070 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2071 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2072 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2073 attr_mask, attr));
2074
2075 // Get the physical address, if we're supposed to.
2076 if (out_paddr != nullptr) {
2077 ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
2078 }
2079
2080 // Make the page group, if we're supposed to.
2081 if (out_pg != nullptr) {
2082 R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
2083 }
2084
2085 // Decide on new perm and attr.
2086 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2087 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
2088
2089 // Update permission, if we need to.
2090 if (new_perm != old_perm) {
2091 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2092 }
2093
2094 // Apply the memory block updates.
2095 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2096
2097 return ResultSuccess;
2098}
2099
2100ResultCode KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
2101 KMemoryState state, KMemoryPermission perm_mask,
2102 KMemoryPermission perm, KMemoryAttribute attr_mask,
2103 KMemoryAttribute attr, KMemoryPermission new_perm,
2104 KMemoryAttribute lock_attr, const KPageLinkedList* pg) {
2105 // Validate basic preconditions.
2106 ASSERT((attr_mask & lock_attr) == lock_attr);
2107 ASSERT((attr & lock_attr) == lock_attr);
2108
2109 // Validate the unlock request.
2110 const size_t num_pages = size / PageSize;
2111 R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
2112
2113 // Lock the table.
2114 KScopedLightLock lk(general_lock);
2115
2116 // Check the state.
2117 KMemoryState old_state{};
2118 KMemoryPermission old_perm{};
2119 KMemoryAttribute old_attr{};
2120 size_t num_allocator_blocks{};
2121 R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
2122 std::addressof(old_attr), std::addressof(num_allocator_blocks),
2123 addr, size, state_mask | KMemoryState::FlagReferenceCounted,
2124 state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
2125 attr_mask, attr));
2126
2127 // Check the page group.
2128 if (pg != nullptr) {
2129 UNIMPLEMENTED_MSG("PageGroup support is unimplemented!");
2130 }
2131
2132 // Decide on new perm and attr.
2133 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
2134 KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
2135
2136 // Update permission, if we need to.
2137 if (new_perm != old_perm) {
2138 R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
2139 }
2140
2141 // Apply the memory block updates.
2142 block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
2143
2144 return ResultSuccess;
2145}
2146
1868} // namespace Kernel 2147} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index e99abe36a..dd6022975 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -12,6 +12,7 @@
12#include "core/file_sys/program_metadata.h" 12#include "core/file_sys/program_metadata.h"
13#include "core/hle/kernel/k_light_lock.h" 13#include "core/hle/kernel/k_light_lock.h"
14#include "core/hle/kernel/k_memory_block.h" 14#include "core/hle/kernel/k_memory_block.h"
15#include "core/hle/kernel/k_memory_layout.h"
15#include "core/hle/kernel/k_memory_manager.h" 16#include "core/hle/kernel/k_memory_manager.h"
16#include "core/hle/result.h" 17#include "core/hle/result.h"
17 18
@@ -25,6 +26,8 @@ class KMemoryBlockManager;
25 26
26class KPageTable final { 27class KPageTable final {
27public: 28public:
29 enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
30
28 YUZU_NON_COPYABLE(KPageTable); 31 YUZU_NON_COPYABLE(KPageTable);
29 YUZU_NON_MOVEABLE(KPageTable); 32 YUZU_NON_MOVEABLE(KPageTable);
30 33
@@ -37,7 +40,8 @@ public:
37 ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state, 40 ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
38 KMemoryPermission perm); 41 KMemoryPermission perm);
39 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); 42 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
40 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size); 43 ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
44 ICacheInvalidationStrategy icache_invalidation_strategy);
41 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table, 45 ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
42 VAddr src_addr); 46 VAddr src_addr);
43 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); 47 ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
@@ -46,7 +50,14 @@ public:
46 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); 50 ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
47 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state, 51 ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
48 KMemoryPermission perm); 52 KMemoryPermission perm);
53 ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
54 PAddr phys_addr, KMemoryState state, KMemoryPermission perm) {
55 return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
56 this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
57 state, perm);
58 }
49 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state); 59 ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
60 ResultCode UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
50 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size, 61 ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
51 Svc::MemoryPermission svc_perm); 62 Svc::MemoryPermission svc_perm);
52 KMemoryInfo QueryInfo(VAddr addr); 63 KMemoryInfo QueryInfo(VAddr addr);
@@ -64,6 +75,10 @@ public:
64 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size); 75 ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
65 ResultCode LockForCodeMemory(VAddr addr, std::size_t size); 76 ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
66 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size); 77 ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
78 ResultCode MakeAndOpenPageGroup(KPageLinkedList* out, VAddr address, size_t num_pages,
79 KMemoryState state_mask, KMemoryState state,
80 KMemoryPermission perm_mask, KMemoryPermission perm,
81 KMemoryAttribute attr_mask, KMemoryAttribute attr);
67 82
68 Common::PageTable& PageTableImpl() { 83 Common::PageTable& PageTableImpl() {
69 return page_table_impl; 84 return page_table_impl;
@@ -91,6 +106,9 @@ private:
91 ResultCode InitializeMemoryLayout(VAddr start, VAddr end); 106 ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
92 ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list, 107 ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
93 KMemoryPermission perm); 108 KMemoryPermission perm);
109 ResultCode MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
110 PAddr phys_addr, bool is_pa_valid, VAddr region_start,
111 std::size_t region_num_pages, KMemoryState state, KMemoryPermission perm);
94 ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list); 112 ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
95 bool IsRegionMapped(VAddr address, u64 size); 113 bool IsRegionMapped(VAddr address, u64 size);
96 bool IsRegionContiguous(VAddr addr, u64 size) const; 114 bool IsRegionContiguous(VAddr addr, u64 size) const;
@@ -105,6 +123,9 @@ private:
105 VAddr GetRegionAddress(KMemoryState state) const; 123 VAddr GetRegionAddress(KMemoryState state) const;
106 std::size_t GetRegionSize(KMemoryState state) const; 124 std::size_t GetRegionSize(KMemoryState state) const;
107 125
126 VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
127 std::size_t alignment, std::size_t offset, std::size_t guard_pages);
128
108 ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, 129 ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
109 std::size_t size, KMemoryState state_mask, 130 std::size_t size, KMemoryState state_mask,
110 KMemoryState state, KMemoryPermission perm_mask, 131 KMemoryState state, KMemoryPermission perm_mask,
@@ -137,7 +158,7 @@ private:
137 return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size, 158 return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
138 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr); 159 state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
139 } 160 }
140 ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask, 161 ResultCode CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
141 KMemoryState state, KMemoryPermission perm_mask, 162 KMemoryState state, KMemoryPermission perm_mask,
142 KMemoryPermission perm, KMemoryAttribute attr_mask, 163 KMemoryPermission perm, KMemoryAttribute attr_mask,
143 KMemoryAttribute attr, 164 KMemoryAttribute attr,
@@ -146,10 +167,37 @@ private:
146 attr_mask, attr, ignore_attr); 167 attr_mask, attr, ignore_attr);
147 } 168 }
148 169
170 ResultCode LockMemoryAndOpen(KPageLinkedList* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
171 KMemoryState state_mask, KMemoryState state,
172 KMemoryPermission perm_mask, KMemoryPermission perm,
173 KMemoryAttribute attr_mask, KMemoryAttribute attr,
174 KMemoryPermission new_perm, KMemoryAttribute lock_attr);
175 ResultCode UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
176 KMemoryPermission perm_mask, KMemoryPermission perm,
177 KMemoryAttribute attr_mask, KMemoryAttribute attr,
178 KMemoryPermission new_perm, KMemoryAttribute lock_attr,
179 const KPageLinkedList* pg);
180
181 ResultCode MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages);
182
149 bool IsLockedByCurrentThread() const { 183 bool IsLockedByCurrentThread() const {
150 return general_lock.IsLockedByCurrentThread(); 184 return general_lock.IsLockedByCurrentThread();
151 } 185 }
152 186
187 bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
188 ASSERT(this->IsLockedByCurrentThread());
189
190 return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
191 }
192
193 bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
194 ASSERT(this->IsLockedByCurrentThread());
195
196 *out = GetPhysicalAddr(virt_addr);
197
198 return *out != 0;
199 }
200
153 mutable KLightLock general_lock; 201 mutable KLightLock general_lock;
154 mutable KLightLock map_physical_memory_lock; 202 mutable KLightLock map_physical_memory_lock;
155 203
@@ -210,7 +258,7 @@ public:
210 constexpr VAddr GetAliasCodeRegionSize() const { 258 constexpr VAddr GetAliasCodeRegionSize() const {
211 return alias_code_region_end - alias_code_region_start; 259 return alias_code_region_end - alias_code_region_start;
212 } 260 }
213 size_t GetNormalMemorySize() { 261 std::size_t GetNormalMemorySize() {
214 KScopedLightLock lk(general_lock); 262 KScopedLightLock lk(general_lock);
215 return GetHeapSize() + mapped_physical_memory_size; 263 return GetHeapSize() + mapped_physical_memory_size;
216 } 264 }
@@ -309,6 +357,7 @@ private:
309 bool is_aslr_enabled{}; 357 bool is_aslr_enabled{};
310 358
311 u32 heap_fill_value{}; 359 u32 heap_fill_value{};
360 const KMemoryRegion* cached_physical_heap_region{};
312 361
313 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; 362 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
314 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront}; 363 KMemoryManager::Direction allocation_option{KMemoryManager::Direction::FromFront};
diff --git a/src/core/hle/kernel/k_port.cpp b/src/core/hle/kernel/k_port.cpp
index a8ba09c4a..ceb98709f 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -57,7 +57,12 @@ ResultCode KPort::EnqueueSession(KServerSession* session) {
57 R_UNLESS(state == State::Normal, ResultPortClosed); 57 R_UNLESS(state == State::Normal, ResultPortClosed);
58 58
59 server.EnqueueSession(session); 59 server.EnqueueSession(session);
60 server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession()); 60
61 if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
62 session_ptr->ClientConnected(server.AcceptSession());
63 } else {
64 UNREACHABLE();
65 }
61 66
62 return ResultSuccess; 67 return ResultSuccess;
63} 68}
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 9233261cd..490e31fc7 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -13,7 +13,6 @@
13#include "common/scope_exit.h" 13#include "common/scope_exit.h"
14#include "common/settings.h" 14#include "common/settings.h"
15#include "core/core.h" 15#include "core/core.h"
16#include "core/device_memory.h"
17#include "core/file_sys/program_metadata.h" 16#include "core/file_sys/program_metadata.h"
18#include "core/hle/kernel/code_set.h" 17#include "core/hle/kernel/code_set.h"
19#include "core/hle/kernel/k_memory_block_manager.h" 18#include "core/hle/kernel/k_memory_block_manager.h"
@@ -24,7 +23,6 @@
24#include "core/hle/kernel/k_scoped_resource_reservation.h" 23#include "core/hle/kernel/k_scoped_resource_reservation.h"
25#include "core/hle/kernel/k_shared_memory.h" 24#include "core/hle/kernel/k_shared_memory.h"
26#include "core/hle/kernel/k_shared_memory_info.h" 25#include "core/hle/kernel/k_shared_memory_info.h"
27#include "core/hle/kernel/k_slab_heap.h"
28#include "core/hle/kernel/k_thread.h" 26#include "core/hle/kernel/k_thread.h"
29#include "core/hle/kernel/kernel.h" 27#include "core/hle/kernel/kernel.h"
30#include "core/hle/kernel/svc_results.h" 28#include "core/hle/kernel/svc_results.h"
@@ -70,58 +68,6 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
70} 68}
71} // Anonymous namespace 69} // Anonymous namespace
72 70
73// Represents a page used for thread-local storage.
74//
75// Each TLS page contains slots that may be used by processes and threads.
76// Every process and thread is created with a slot in some arbitrary page
77// (whichever page happens to have an available slot).
78class TLSPage {
79public:
80 static constexpr std::size_t num_slot_entries =
81 Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE;
82
83 explicit TLSPage(VAddr address) : base_address{address} {}
84
85 bool HasAvailableSlots() const {
86 return !is_slot_used.all();
87 }
88
89 VAddr GetBaseAddress() const {
90 return base_address;
91 }
92
93 std::optional<VAddr> ReserveSlot() {
94 for (std::size_t i = 0; i < is_slot_used.size(); i++) {
95 if (is_slot_used[i]) {
96 continue;
97 }
98
99 is_slot_used[i] = true;
100 return base_address + (i * Core::Memory::TLS_ENTRY_SIZE);
101 }
102
103 return std::nullopt;
104 }
105
106 void ReleaseSlot(VAddr address) {
107 // Ensure that all given addresses are consistent with how TLS pages
108 // are intended to be used when releasing slots.
109 ASSERT(IsWithinPage(address));
110 ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0);
111
112 const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE;
113 is_slot_used[index] = false;
114 }
115
116private:
117 bool IsWithinPage(VAddr address) const {
118 return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE;
119 }
120
121 VAddr base_address;
122 std::bitset<num_slot_entries> is_slot_used;
123};
124
125ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name, 71ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
126 ProcessType type, KResourceLimit* res_limit) { 72 ProcessType type, KResourceLimit* res_limit) {
127 auto& kernel = system.Kernel(); 73 auto& kernel = system.Kernel();
@@ -404,7 +350,7 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
404 } 350 }
405 351
406 // Create TLS region 352 // Create TLS region
407 tls_region_address = CreateTLSRegion(); 353 R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address)));
408 memory_reservation.Commit(); 354 memory_reservation.Commit();
409 355
410 return handle_table.Initialize(capabilities.GetHandleTableSize()); 356 return handle_table.Initialize(capabilities.GetHandleTableSize());
@@ -444,7 +390,7 @@ void KProcess::PrepareForTermination() {
444 390
445 stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList()); 391 stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
446 392
447 FreeTLSRegion(tls_region_address); 393 this->DeleteThreadLocalRegion(tls_region_address);
448 tls_region_address = 0; 394 tls_region_address = 0;
449 395
450 if (resource_limit) { 396 if (resource_limit) {
@@ -456,9 +402,6 @@ void KProcess::PrepareForTermination() {
456} 402}
457 403
458void KProcess::Finalize() { 404void KProcess::Finalize() {
459 // Finalize the handle table and close any open handles.
460 handle_table.Finalize();
461
462 // Free all shared memory infos. 405 // Free all shared memory infos.
463 { 406 {
464 auto it = shared_memory_list.begin(); 407 auto it = shared_memory_list.begin();
@@ -483,67 +426,110 @@ void KProcess::Finalize() {
483 resource_limit = nullptr; 426 resource_limit = nullptr;
484 } 427 }
485 428
429 // Finalize the page table.
430 page_table.reset();
431
486 // Perform inherited finalization. 432 // Perform inherited finalization.
487 KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize(); 433 KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
488} 434}
489 435
490/** 436ResultCode KProcess::CreateThreadLocalRegion(VAddr* out) {
491 * Attempts to find a TLS page that contains a free slot for 437 KThreadLocalPage* tlp = nullptr;
492 * use by a thread. 438 VAddr tlr = 0;
493 *
494 * @returns If a page with an available slot is found, then an iterator
495 * pointing to the page is returned. Otherwise the end iterator
496 * is returned instead.
497 */
498static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
499 return std::find_if(tls_pages.begin(), tls_pages.end(),
500 [](const auto& page) { return page.HasAvailableSlots(); });
501}
502 439
503VAddr KProcess::CreateTLSRegion() { 440 // See if we can get a region from a partially used TLP.
504 KScopedSchedulerLock lock(kernel); 441 {
505 if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; 442 KScopedSchedulerLock sl{kernel};
506 tls_page_iter != tls_pages.cend()) {
507 return *tls_page_iter->ReserveSlot();
508 }
509 443
510 Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; 444 if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) {
511 ASSERT(tls_page_ptr); 445 tlr = it->Reserve();
446 ASSERT(tlr != 0);
512 447
513 const VAddr start{page_table->GetKernelMapRegionStart()}; 448 if (it->IsAllUsed()) {
514 const VAddr size{page_table->GetKernelMapRegionEnd() - start}; 449 tlp = std::addressof(*it);
515 const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; 450 partially_used_tlp_tree.erase(it);
516 const VAddr tls_page_addr{page_table 451 fully_used_tlp_tree.insert(*tlp);
517 ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize, 452 }
518 KMemoryState::ThreadLocal,
519 KMemoryPermission::UserReadWrite,
520 tls_map_addr)
521 .ValueOr(0)};
522 453
523 ASSERT(tls_page_addr); 454 *out = tlr;
455 return ResultSuccess;
456 }
457 }
458
459 // Allocate a new page.
460 tlp = KThreadLocalPage::Allocate(kernel);
461 R_UNLESS(tlp != nullptr, ResultOutOfMemory);
462 auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); });
463
464 // Initialize the new page.
465 R_TRY(tlp->Initialize(kernel, this));
524 466
525 std::memset(tls_page_ptr, 0, PageSize); 467 // Reserve a TLR.
526 tls_pages.emplace_back(tls_page_addr); 468 tlr = tlp->Reserve();
469 ASSERT(tlr != 0);
527 470
528 const auto reserve_result{tls_pages.back().ReserveSlot()}; 471 // Insert into our tree.
529 ASSERT(reserve_result.has_value()); 472 {
473 KScopedSchedulerLock sl{kernel};
474 if (tlp->IsAllUsed()) {
475 fully_used_tlp_tree.insert(*tlp);
476 } else {
477 partially_used_tlp_tree.insert(*tlp);
478 }
479 }
530 480
531 return *reserve_result; 481 // We succeeded!
482 tlp_guard.Cancel();
483 *out = tlr;
484 return ResultSuccess;
532} 485}
533 486
534void KProcess::FreeTLSRegion(VAddr tls_address) { 487ResultCode KProcess::DeleteThreadLocalRegion(VAddr addr) {
535 KScopedSchedulerLock lock(kernel); 488 KThreadLocalPage* page_to_free = nullptr;
536 const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
537 auto iter =
538 std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
539 return page.GetBaseAddress() == aligned_address;
540 });
541 489
542 // Something has gone very wrong if we're freeing a region 490 // Release the region.
543 // with no actual page available. 491 {
544 ASSERT(iter != tls_pages.cend()); 492 KScopedSchedulerLock sl{kernel};
493
494 // Try to find the page in the partially used list.
495 auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
496 if (it == partially_used_tlp_tree.end()) {
497 // If we don't find it, it has to be in the fully used list.
498 it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
499 R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress);
500
501 // Release the region.
502 it->Release(addr);
503
504 // Move the page out of the fully used list.
505 KThreadLocalPage* tlp = std::addressof(*it);
506 fully_used_tlp_tree.erase(it);
507 if (tlp->IsAllFree()) {
508 page_to_free = tlp;
509 } else {
510 partially_used_tlp_tree.insert(*tlp);
511 }
512 } else {
513 // Release the region.
514 it->Release(addr);
515
516 // Handle the all-free case.
517 KThreadLocalPage* tlp = std::addressof(*it);
518 if (tlp->IsAllFree()) {
519 partially_used_tlp_tree.erase(it);
520 page_to_free = tlp;
521 }
522 }
523 }
524
525 // If we should free the page it was in, do so.
526 if (page_to_free != nullptr) {
527 page_to_free->Finalize();
545 528
546 iter->ReleaseSlot(tls_address); 529 KThreadLocalPage::Free(kernel, page_to_free);
530 }
531
532 return ResultSuccess;
547} 533}
548 534
549void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) { 535void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index cf1b67428..9f171e3da 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -8,13 +8,13 @@
8#include <cstddef> 8#include <cstddef>
9#include <list> 9#include <list>
10#include <string> 10#include <string>
11#include <vector>
12#include "common/common_types.h" 11#include "common/common_types.h"
13#include "core/hle/kernel/k_address_arbiter.h" 12#include "core/hle/kernel/k_address_arbiter.h"
14#include "core/hle/kernel/k_auto_object.h" 13#include "core/hle/kernel/k_auto_object.h"
15#include "core/hle/kernel/k_condition_variable.h" 14#include "core/hle/kernel/k_condition_variable.h"
16#include "core/hle/kernel/k_handle_table.h" 15#include "core/hle/kernel/k_handle_table.h"
17#include "core/hle/kernel/k_synchronization_object.h" 16#include "core/hle/kernel/k_synchronization_object.h"
17#include "core/hle/kernel/k_thread_local_page.h"
18#include "core/hle/kernel/k_worker_task.h" 18#include "core/hle/kernel/k_worker_task.h"
19#include "core/hle/kernel/process_capability.h" 19#include "core/hle/kernel/process_capability.h"
20#include "core/hle/kernel/slab_helpers.h" 20#include "core/hle/kernel/slab_helpers.h"
@@ -362,10 +362,10 @@ public:
362 // Thread-local storage management 362 // Thread-local storage management
363 363
364 // Marks the next available region as used and returns the address of the slot. 364 // Marks the next available region as used and returns the address of the slot.
365 [[nodiscard]] VAddr CreateTLSRegion(); 365 [[nodiscard]] ResultCode CreateThreadLocalRegion(VAddr* out);
366 366
367 // Frees a used TLS slot identified by the given address 367 // Frees a used TLS slot identified by the given address
368 void FreeTLSRegion(VAddr tls_address); 368 ResultCode DeleteThreadLocalRegion(VAddr addr);
369 369
370private: 370private:
371 void PinThread(s32 core_id, KThread* thread) { 371 void PinThread(s32 core_id, KThread* thread) {
@@ -413,13 +413,6 @@ private:
413 /// The ideal CPU core for this process, threads are scheduled on this core by default. 413 /// The ideal CPU core for this process, threads are scheduled on this core by default.
414 u8 ideal_core = 0; 414 u8 ideal_core = 0;
415 415
416 /// The Thread Local Storage area is allocated as processes create threads,
417 /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
418 /// holds the TLS for a specific thread. This vector contains which parts are in use for each
419 /// page as a bitmask.
420 /// This vector will grow as more pages are allocated for new threads.
421 std::vector<TLSPage> tls_pages;
422
423 /// Contains the parsed process capability descriptors. 416 /// Contains the parsed process capability descriptors.
424 ProcessCapabilities capabilities; 417 ProcessCapabilities capabilities;
425 418
@@ -429,7 +422,7 @@ private:
429 bool is_64bit_process = true; 422 bool is_64bit_process = true;
430 423
431 /// Total running time for the process in ticks. 424 /// Total running time for the process in ticks.
432 u64 total_process_running_time_ticks = 0; 425 std::atomic<u64> total_process_running_time_ticks = 0;
433 426
434 /// Per-process handle table for storing created object handles in. 427 /// Per-process handle table for storing created object handles in.
435 KHandleTable handle_table; 428 KHandleTable handle_table;
@@ -482,6 +475,12 @@ private:
482 KThread* exception_thread{}; 475 KThread* exception_thread{};
483 476
484 KLightLock state_lock; 477 KLightLock state_lock;
478
479 using TLPTree =
480 Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
481 using TLPIterator = TLPTree::iterator;
482 TLPTree fully_used_tlp_tree;
483 TLPTree partially_used_tlp_tree;
485}; 484};
486 485
487} // namespace Kernel 486} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index c96520828..6c0bb1672 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -22,7 +22,6 @@
22#include "core/hle/kernel/k_thread.h" 22#include "core/hle/kernel/k_thread.h"
23#include "core/hle/kernel/kernel.h" 23#include "core/hle/kernel/kernel.h"
24#include "core/hle/kernel/physical_core.h" 24#include "core/hle/kernel/physical_core.h"
25#include "core/hle/kernel/time_manager.h"
26 25
27namespace Kernel { 26namespace Kernel {
28 27
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 93c47f1b1..016e0a818 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include "common/assert.h" 8#include "common/assert.h"
8#include "core/hle/kernel/k_spin_lock.h" 9#include "core/hle/kernel/k_spin_lock.h"
9#include "core/hle/kernel/k_thread.h" 10#include "core/hle/kernel/k_thread.h"
@@ -75,7 +76,7 @@ private:
75 KernelCore& kernel; 76 KernelCore& kernel;
76 KAlignedSpinLock spin_lock{}; 77 KAlignedSpinLock spin_lock{};
77 s32 lock_count{}; 78 s32 lock_count{};
78 KThread* owner_thread{}; 79 std::atomic<KThread*> owner_thread{};
79}; 80};
80 81
81} // namespace Kernel 82} // namespace Kernel
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index 433fc98e1..e66c0c992 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -62,6 +62,12 @@ void KServerPort::Destroy() {
62 62
63 // Close our reference to our parent. 63 // Close our reference to our parent.
64 parent->Close(); 64 parent->Close();
65
66 // Release host emulation members.
67 session_handler.reset();
68
69 // Ensure that the global list tracking server objects does not hold on to a reference.
70 kernel.UnregisterServerObject(this);
65} 71}
66 72
67bool KServerPort::IsSignaled() const { 73bool KServerPort::IsSignaled() const {
diff --git a/src/core/hle/kernel/k_server_port.h b/src/core/hle/kernel/k_server_port.h
index 6302d5e61..2185736be 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -30,11 +30,11 @@ public:
30 30
31 /// Whether or not this server port has an HLE handler available. 31 /// Whether or not this server port has an HLE handler available.
32 bool HasSessionRequestHandler() const { 32 bool HasSessionRequestHandler() const {
33 return session_handler != nullptr; 33 return !session_handler.expired();
34 } 34 }
35 35
36 /// Gets the HLE handler for this port. 36 /// Gets the HLE handler for this port.
37 SessionRequestHandlerPtr GetSessionRequestHandler() const { 37 SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
38 return session_handler; 38 return session_handler;
39 } 39 }
40 40
@@ -42,7 +42,7 @@ public:
42 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port 42 * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
43 * will inherit a reference to this handler. 43 * will inherit a reference to this handler.
44 */ 44 */
45 void SetSessionHandler(SessionRequestHandlerPtr&& handler) { 45 void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
46 session_handler = std::move(handler); 46 session_handler = std::move(handler);
47 } 47 }
48 48
@@ -66,7 +66,7 @@ private:
66 void CleanupSessions(); 66 void CleanupSessions();
67 67
68 SessionList session_list; 68 SessionList session_list;
69 SessionRequestHandlerPtr session_handler; 69 SessionRequestHandlerWeakPtr session_handler;
70 KPort* parent{}; 70 KPort* parent{};
71}; 71};
72 72
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 4d94eb9cf..7ac2ef254 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -27,10 +27,7 @@ namespace Kernel {
27 27
28KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {} 28KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
29 29
30KServerSession::~KServerSession() { 30KServerSession::~KServerSession() = default;
31 // Ensure that the global list tracking server sessions does not hold on to a reference.
32 kernel.UnregisterServerSession(this);
33}
34 31
35void KServerSession::Initialize(KSession* parent_session_, std::string&& name_, 32void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
36 std::shared_ptr<SessionRequestManager> manager_) { 33 std::shared_ptr<SessionRequestManager> manager_) {
@@ -49,6 +46,12 @@ void KServerSession::Destroy() {
49 parent->OnServerClosed(); 46 parent->OnServerClosed();
50 47
51 parent->Close(); 48 parent->Close();
49
50 // Release host emulation members.
51 manager.reset();
52
53 // Ensure that the global list tracking server objects does not hold on to a reference.
54 kernel.UnregisterServerObject(this);
52} 55}
53 56
54void KServerSession::OnClientClosed() { 57void KServerSession::OnClientClosed() {
@@ -98,7 +101,12 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
98 UNREACHABLE(); 101 UNREACHABLE();
99 return ResultSuccess; // Ignore error if asserts are off 102 return ResultSuccess; // Ignore error if asserts are off
100 } 103 }
101 return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context); 104 if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
105 return strong_ptr->HandleSyncRequest(*this, context);
106 } else {
107 UNREACHABLE();
108 return ResultSuccess;
109 }
102 110
103 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { 111 case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
104 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id); 112 LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h
index 05c0bec9c..5690cc757 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -16,39 +16,34 @@ class KernelCore;
16 16
17namespace impl { 17namespace impl {
18 18
19class KSlabHeapImpl final { 19class KSlabHeapImpl {
20public:
21 YUZU_NON_COPYABLE(KSlabHeapImpl); 20 YUZU_NON_COPYABLE(KSlabHeapImpl);
22 YUZU_NON_MOVEABLE(KSlabHeapImpl); 21 YUZU_NON_MOVEABLE(KSlabHeapImpl);
23 22
23public:
24 struct Node { 24 struct Node {
25 Node* next{}; 25 Node* next{};
26 }; 26 };
27 27
28public:
28 constexpr KSlabHeapImpl() = default; 29 constexpr KSlabHeapImpl() = default;
29 constexpr ~KSlabHeapImpl() = default;
30 30
31 void Initialize(std::size_t size) { 31 void Initialize() {
32 ASSERT(head == nullptr); 32 ASSERT(m_head == nullptr);
33 obj_size = size;
34 }
35
36 constexpr std::size_t GetObjectSize() const {
37 return obj_size;
38 } 33 }
39 34
40 Node* GetHead() const { 35 Node* GetHead() const {
41 return head; 36 return m_head;
42 } 37 }
43 38
44 void* Allocate() { 39 void* Allocate() {
45 Node* ret = head.load(); 40 Node* ret = m_head.load();
46 41
47 do { 42 do {
48 if (ret == nullptr) { 43 if (ret == nullptr) {
49 break; 44 break;
50 } 45 }
51 } while (!head.compare_exchange_weak(ret, ret->next)); 46 } while (!m_head.compare_exchange_weak(ret, ret->next));
52 47
53 return ret; 48 return ret;
54 } 49 }
@@ -56,170 +51,157 @@ public:
56 void Free(void* obj) { 51 void Free(void* obj) {
57 Node* node = static_cast<Node*>(obj); 52 Node* node = static_cast<Node*>(obj);
58 53
59 Node* cur_head = head.load(); 54 Node* cur_head = m_head.load();
60 do { 55 do {
61 node->next = cur_head; 56 node->next = cur_head;
62 } while (!head.compare_exchange_weak(cur_head, node)); 57 } while (!m_head.compare_exchange_weak(cur_head, node));
63 } 58 }
64 59
65private: 60private:
66 std::atomic<Node*> head{}; 61 std::atomic<Node*> m_head{};
67 std::size_t obj_size{};
68}; 62};
69 63
70} // namespace impl 64} // namespace impl
71 65
72class KSlabHeapBase { 66template <bool SupportDynamicExpansion>
73public: 67class KSlabHeapBase : protected impl::KSlabHeapImpl {
74 YUZU_NON_COPYABLE(KSlabHeapBase); 68 YUZU_NON_COPYABLE(KSlabHeapBase);
75 YUZU_NON_MOVEABLE(KSlabHeapBase); 69 YUZU_NON_MOVEABLE(KSlabHeapBase);
76 70
77 constexpr KSlabHeapBase() = default; 71private:
78 constexpr ~KSlabHeapBase() = default; 72 size_t m_obj_size{};
73 uintptr_t m_peak{};
74 uintptr_t m_start{};
75 uintptr_t m_end{};
79 76
80 constexpr bool Contains(uintptr_t addr) const { 77private:
81 return start <= addr && addr < end; 78 void UpdatePeakImpl(uintptr_t obj) {
82 } 79 static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
80 std::atomic_ref<uintptr_t> peak_ref(m_peak);
83 81
84 constexpr std::size_t GetSlabHeapSize() const { 82 const uintptr_t alloc_peak = obj + this->GetObjectSize();
85 return (end - start) / GetObjectSize(); 83 uintptr_t cur_peak = m_peak;
84 do {
85 if (alloc_peak <= cur_peak) {
86 break;
87 }
88 } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
86 } 89 }
87 90
88 constexpr std::size_t GetObjectSize() const { 91public:
89 return impl.GetObjectSize(); 92 constexpr KSlabHeapBase() = default;
90 }
91 93
92 constexpr uintptr_t GetSlabHeapAddress() const { 94 bool Contains(uintptr_t address) const {
93 return start; 95 return m_start <= address && address < m_end;
94 } 96 }
95 97
96 std::size_t GetObjectIndexImpl(const void* obj) const { 98 void Initialize(size_t obj_size, void* memory, size_t memory_size) {
97 return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); 99 // Ensure we don't initialize a slab using null memory.
100 ASSERT(memory != nullptr);
101
102 // Set our object size.
103 m_obj_size = obj_size;
104
105 // Initialize the base allocator.
106 KSlabHeapImpl::Initialize();
107
108 // Set our tracking variables.
109 const size_t num_obj = (memory_size / obj_size);
110 m_start = reinterpret_cast<uintptr_t>(memory);
111 m_end = m_start + num_obj * obj_size;
112 m_peak = m_start;
113
114 // Free the objects.
115 u8* cur = reinterpret_cast<u8*>(m_end);
116
117 for (size_t i = 0; i < num_obj; i++) {
118 cur -= obj_size;
119 KSlabHeapImpl::Free(cur);
120 }
98 } 121 }
99 122
100 std::size_t GetPeakIndex() const { 123 size_t GetSlabHeapSize() const {
101 return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); 124 return (m_end - m_start) / this->GetObjectSize();
102 } 125 }
103 126
104 void* AllocateImpl() { 127 size_t GetObjectSize() const {
105 return impl.Allocate(); 128 return m_obj_size;
106 } 129 }
107 130
108 void FreeImpl(void* obj) { 131 void* Allocate() {
109 // Don't allow freeing an object that wasn't allocated from this heap 132 void* obj = KSlabHeapImpl::Allocate();
110 ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
111 133
112 impl.Free(obj); 134 return obj;
113 } 135 }
114 136
115 void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { 137 void Free(void* obj) {
116 // Ensure we don't initialize a slab using null memory 138 // Don't allow freeing an object that wasn't allocated from this heap.
117 ASSERT(memory != nullptr); 139 const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
118 140 ASSERT(contained);
119 // Initialize the base allocator 141 KSlabHeapImpl::Free(obj);
120 impl.Initialize(obj_size); 142 }
121 143
122 // Set our tracking variables 144 size_t GetObjectIndex(const void* obj) const {
123 const std::size_t num_obj = (memory_size / obj_size); 145 if constexpr (SupportDynamicExpansion) {
124 start = reinterpret_cast<uintptr_t>(memory); 146 if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) {
125 end = start + num_obj * obj_size; 147 return std::numeric_limits<size_t>::max();
126 peak = start; 148 }
149 }
127 150
128 // Free the objects 151 return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
129 u8* cur = reinterpret_cast<u8*>(end); 152 }
130 153
131 for (std::size_t i{}; i < num_obj; i++) { 154 size_t GetPeakIndex() const {
132 cur -= obj_size; 155 return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
133 impl.Free(cur);
134 }
135 } 156 }
136 157
137private: 158 uintptr_t GetSlabHeapAddress() const {
138 using Impl = impl::KSlabHeapImpl; 159 return m_start;
160 }
139 161
140 Impl impl; 162 size_t GetNumRemaining() const {
141 uintptr_t peak{}; 163 // Only calculate the number of remaining objects under debug configuration.
142 uintptr_t start{}; 164 return 0;
143 uintptr_t end{}; 165 }
144}; 166};
145 167
146template <typename T> 168template <typename T>
147class KSlabHeap final : public KSlabHeapBase { 169class KSlabHeap final : public KSlabHeapBase<false> {
148public: 170private:
149 enum class AllocationType { 171 using BaseHeap = KSlabHeapBase<false>;
150 Host,
151 Guest,
152 };
153 172
154 explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host) 173public:
155 : KSlabHeapBase(), allocation_type{allocation_type_} {} 174 constexpr KSlabHeap() = default;
156 175
157 void Initialize(void* memory, std::size_t memory_size) { 176 void Initialize(void* memory, size_t memory_size) {
158 if (allocation_type == AllocationType::Guest) { 177 BaseHeap::Initialize(sizeof(T), memory, memory_size);
159 InitializeImpl(sizeof(T), memory, memory_size);
160 }
161 } 178 }
162 179
163 T* Allocate() { 180 T* Allocate() {
164 switch (allocation_type) { 181 T* obj = static_cast<T*>(BaseHeap::Allocate());
165 case AllocationType::Host:
166 // Fallback for cases where we do not yet support allocating guest memory from the slab
167 // heap, such as for kernel memory regions.
168 return new T;
169
170 case AllocationType::Guest:
171 T* obj = static_cast<T*>(AllocateImpl());
172 if (obj != nullptr) {
173 new (obj) T();
174 }
175 return obj;
176 }
177 182
178 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type); 183 if (obj != nullptr) [[likely]] {
179 return nullptr; 184 std::construct_at(obj);
185 }
186 return obj;
180 } 187 }
181 188
182 T* AllocateWithKernel(KernelCore& kernel) { 189 T* Allocate(KernelCore& kernel) {
183 switch (allocation_type) { 190 T* obj = static_cast<T*>(BaseHeap::Allocate());
184 case AllocationType::Host:
185 // Fallback for cases where we do not yet support allocating guest memory from the slab
186 // heap, such as for kernel memory regions.
187 return new T(kernel);
188 191
189 case AllocationType::Guest: 192 if (obj != nullptr) [[likely]] {
190 T* obj = static_cast<T*>(AllocateImpl()); 193 std::construct_at(obj, kernel);
191 if (obj != nullptr) {
192 new (obj) T(kernel);
193 }
194 return obj;
195 } 194 }
196 195 return obj;
197 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
198 return nullptr;
199 } 196 }
200 197
201 void Free(T* obj) { 198 void Free(T* obj) {
202 switch (allocation_type) { 199 BaseHeap::Free(obj);
203 case AllocationType::Host:
204 // Fallback for cases where we do not yet support allocating guest memory from the slab
205 // heap, such as for kernel memory regions.
206 delete obj;
207 return;
208
209 case AllocationType::Guest:
210 FreeImpl(obj);
211 return;
212 }
213
214 UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
215 } 200 }
216 201
217 constexpr std::size_t GetObjectIndex(const T* obj) const { 202 size_t GetObjectIndex(const T* obj) const {
218 return GetObjectIndexImpl(obj); 203 return BaseHeap::GetObjectIndex(obj);
219 } 204 }
220
221private:
222 const AllocationType allocation_type;
223}; 205};
224 206
225} // namespace Kernel 207} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index de3ffe0c7..d3bb1c871 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -14,9 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/fiber.h" 15#include "common/fiber.h"
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/scope_exit.h"
18#include "common/settings.h" 17#include "common/settings.h"
19#include "common/thread_queue_list.h"
20#include "core/core.h" 18#include "core/core.h"
21#include "core/cpu_manager.h" 19#include "core/cpu_manager.h"
22#include "core/hardware_properties.h" 20#include "core/hardware_properties.h"
@@ -33,7 +31,6 @@
33#include "core/hle/kernel/k_worker_task_manager.h" 31#include "core/hle/kernel/k_worker_task_manager.h"
34#include "core/hle/kernel/kernel.h" 32#include "core/hle/kernel/kernel.h"
35#include "core/hle/kernel/svc_results.h" 33#include "core/hle/kernel/svc_results.h"
36#include "core/hle/kernel/time_manager.h"
37#include "core/hle/result.h" 34#include "core/hle/result.h"
38#include "core/memory.h" 35#include "core/memory.h"
39 36
@@ -210,7 +207,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
210 if (owner != nullptr) { 207 if (owner != nullptr) {
211 // Setup the TLS, if needed. 208 // Setup the TLS, if needed.
212 if (type == ThreadType::User) { 209 if (type == ThreadType::User) {
213 tls_address = owner->CreateTLSRegion(); 210 R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address)));
214 } 211 }
215 212
216 parent = owner; 213 parent = owner;
@@ -305,7 +302,7 @@ void KThread::Finalize() {
305 302
306 // If the thread has a local region, delete it. 303 // If the thread has a local region, delete it.
307 if (tls_address != 0) { 304 if (tls_address != 0) {
308 parent->FreeTLSRegion(tls_address); 305 ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess());
309 } 306 }
310 307
311 // Release any waiters. 308 // Release any waiters.
@@ -326,6 +323,9 @@ void KThread::Finalize() {
326 } 323 }
327 } 324 }
328 325
326 // Release host emulation members.
327 host_context.reset();
328
329 // Perform inherited finalization. 329 // Perform inherited finalization.
330 KSynchronizationObject::Finalize(); 330 KSynchronizationObject::Finalize();
331} 331}
@@ -723,7 +723,7 @@ void KThread::UpdateState() {
723 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 723 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
724 724
725 // Set our suspend flags in state. 725 // Set our suspend flags in state.
726 const auto old_state = thread_state; 726 const ThreadState old_state = thread_state;
727 const auto new_state = 727 const auto new_state =
728 static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask); 728 static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
729 thread_state = new_state; 729 thread_state = new_state;
@@ -738,7 +738,7 @@ void KThread::Continue() {
738 ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 738 ASSERT(kernel.GlobalSchedulerContext().IsLocked());
739 739
740 // Clear our suspend flags in state. 740 // Clear our suspend flags in state.
741 const auto old_state = thread_state; 741 const ThreadState old_state = thread_state;
742 thread_state = old_state & ThreadState::Mask; 742 thread_state = old_state & ThreadState::Mask;
743 743
744 // Note the state change in scheduler. 744 // Note the state change in scheduler.
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index d058db62c..d0fd85130 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
8#include <span> 9#include <span>
9#include <string> 10#include <string>
10#include <utility> 11#include <utility>
@@ -656,7 +657,7 @@ private:
656 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); 657 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
657 658
658 struct ConditionVariableComparator { 659 struct ConditionVariableComparator {
659 struct LightCompareType { 660 struct RedBlackKeyType {
660 u64 cv_key{}; 661 u64 cv_key{};
661 s32 priority{}; 662 s32 priority{};
662 663
@@ -672,8 +673,8 @@ private:
672 template <typename T> 673 template <typename T>
673 requires( 674 requires(
674 std::same_as<T, KThread> || 675 std::same_as<T, KThread> ||
675 std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, 676 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
676 const KThread& rhs) { 677 const KThread& rhs) {
677 const u64 l_key = lhs.GetConditionVariableKey(); 678 const u64 l_key = lhs.GetConditionVariableKey();
678 const u64 r_key = rhs.GetConditionVariableKey(); 679 const u64 r_key = rhs.GetConditionVariableKey();
679 680
@@ -751,7 +752,7 @@ private:
751 KAffinityMask original_physical_affinity_mask{}; 752 KAffinityMask original_physical_affinity_mask{};
752 s32 original_physical_ideal_core_id{}; 753 s32 original_physical_ideal_core_id{};
753 s32 num_core_migration_disables{}; 754 s32 num_core_migration_disables{};
754 ThreadState thread_state{}; 755 std::atomic<ThreadState> thread_state{};
755 std::atomic<bool> termination_requested{}; 756 std::atomic<bool> termination_requested{};
756 bool wait_cancelled{}; 757 bool wait_cancelled{};
757 bool cancellable{}; 758 bool cancellable{};
diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp
new file mode 100644
index 000000000..3fb277eba
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -0,0 +1,68 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/scope_exit.h"
6#include "core/core.h"
7
8#include "core/hle/kernel/k_memory_block.h"
9#include "core/hle/kernel/k_page_buffer.h"
10#include "core/hle/kernel/k_page_table.h"
11#include "core/hle/kernel/k_process.h"
12#include "core/hle/kernel/k_thread_local_page.h"
13#include "core/hle/kernel/kernel.h"
14
15namespace Kernel {
16
17ResultCode KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
18 // Set that this process owns us.
19 m_owner = process;
20 m_kernel = &kernel;
21
22 // Allocate a new page.
23 KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
24 R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
25 auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); });
26
27 // Map the address in.
28 const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
29 R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr,
30 KMemoryState::ThreadLocal,
31 KMemoryPermission::UserReadWrite));
32
33 // We succeeded.
34 page_buf_guard.Cancel();
35
36 return ResultSuccess;
37}
38
39ResultCode KThreadLocalPage::Finalize() {
40 // Get the physical address of the page.
41 const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr);
42 ASSERT(phys_addr);
43
44 // Unmap the page.
45 R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
46
47 // Free the page.
48 KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr));
49
50 return ResultSuccess;
51}
52
53VAddr KThreadLocalPage::Reserve() {
54 for (size_t i = 0; i < m_is_region_free.size(); i++) {
55 if (m_is_region_free[i]) {
56 m_is_region_free[i] = false;
57 return this->GetRegionAddress(i);
58 }
59 }
60
61 return 0;
62}
63
64void KThreadLocalPage::Release(VAddr addr) {
65 m_is_region_free[this->GetRegionIndex(addr)] = true;
66}
67
68} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
new file mode 100644
index 000000000..74b565a71
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -0,0 +1,111 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <array>
9
10#include "common/alignment.h"
11#include "common/assert.h"
12#include "common/common_types.h"
13#include "common/intrusive_red_black_tree.h"
14#include "core/hle/kernel/memory_types.h"
15#include "core/hle/kernel/slab_helpers.h"
16#include "core/hle/result.h"
17
18namespace Kernel {
19
20class KernelCore;
21class KProcess;
22
23class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>,
24 public KSlabAllocated<KThreadLocalPage> {
25public:
26 static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize;
27 static_assert(RegionsPerPage > 0);
28
29public:
30 constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) {
31 m_is_region_free.fill(true);
32 }
33
34 constexpr VAddr GetAddress() const {
35 return m_virt_addr;
36 }
37
38 ResultCode Initialize(KernelCore& kernel, KProcess* process);
39 ResultCode Finalize();
40
41 VAddr Reserve();
42 void Release(VAddr addr);
43
44 bool IsAllUsed() const {
45 return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
46 [](bool is_free) { return !is_free; });
47 }
48
49 bool IsAllFree() const {
50 return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
51 [](bool is_free) { return is_free; });
52 }
53
54 bool IsAnyUsed() const {
55 return !this->IsAllFree();
56 }
57
58 bool IsAnyFree() const {
59 return !this->IsAllUsed();
60 }
61
62public:
63 using RedBlackKeyType = VAddr;
64
65 static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) {
66 return v;
67 }
68 static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) {
69 return v.GetAddress();
70 }
71
72 template <typename T>
73 requires(std::same_as<T, KThreadLocalPage> ||
74 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
75 const KThreadLocalPage&
76 rhs) {
77 const VAddr lval = GetRedBlackKey(lhs);
78 const VAddr rval = GetRedBlackKey(rhs);
79
80 if (lval < rval) {
81 return -1;
82 } else if (lval == rval) {
83 return 0;
84 } else {
85 return 1;
86 }
87 }
88
89private:
90 constexpr VAddr GetRegionAddress(size_t i) const {
91 return this->GetAddress() + i * Svc::ThreadLocalRegionSize;
92 }
93
94 constexpr bool Contains(VAddr addr) const {
95 return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize;
96 }
97
98 constexpr size_t GetRegionIndex(VAddr addr) const {
99 ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize));
100 ASSERT(this->Contains(addr));
101 return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize;
102 }
103
104private:
105 VAddr m_virt_addr{};
106 KProcess* m_owner{};
107 KernelCore* m_kernel{};
108 std::array<bool, RegionsPerPage> m_is_region_free{};
109};
110
111} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 71bd466cf..d840d44e6 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -22,9 +22,7 @@
22#include "core/arm/exclusive_monitor.h" 22#include "core/arm/exclusive_monitor.h"
23#include "core/core.h" 23#include "core/core.h"
24#include "core/core_timing.h" 24#include "core/core_timing.h"
25#include "core/core_timing_util.h"
26#include "core/cpu_manager.h" 25#include "core/cpu_manager.h"
27#include "core/device_memory.h"
28#include "core/hardware_properties.h" 26#include "core/hardware_properties.h"
29#include "core/hle/kernel/init/init_slab_setup.h" 27#include "core/hle/kernel/init/init_slab_setup.h"
30#include "core/hle/kernel/k_client_port.h" 28#include "core/hle/kernel/k_client_port.h"
@@ -35,7 +33,6 @@
35#include "core/hle/kernel/k_resource_limit.h" 33#include "core/hle/kernel/k_resource_limit.h"
36#include "core/hle/kernel/k_scheduler.h" 34#include "core/hle/kernel/k_scheduler.h"
37#include "core/hle/kernel/k_shared_memory.h" 35#include "core/hle/kernel/k_shared_memory.h"
38#include "core/hle/kernel/k_slab_heap.h"
39#include "core/hle/kernel/k_thread.h" 36#include "core/hle/kernel/k_thread.h"
40#include "core/hle/kernel/k_worker_task_manager.h" 37#include "core/hle/kernel/k_worker_task_manager.h"
41#include "core/hle/kernel/kernel.h" 38#include "core/hle/kernel/kernel.h"
@@ -52,7 +49,7 @@ namespace Kernel {
52 49
53struct KernelCore::Impl { 50struct KernelCore::Impl {
54 explicit Impl(Core::System& system_, KernelCore& kernel_) 51 explicit Impl(Core::System& system_, KernelCore& kernel_)
55 : time_manager{system_}, object_list_container{kernel_}, 52 : time_manager{system_},
56 service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {} 53 service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
57 54
58 void SetMulticore(bool is_multi) { 55 void SetMulticore(bool is_multi) {
@@ -60,9 +57,11 @@ struct KernelCore::Impl {
60 } 57 }
61 58
62 void Initialize(KernelCore& kernel) { 59 void Initialize(KernelCore& kernel) {
60 global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
63 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 61 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
64 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel); 62 global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
65 global_handle_table->Initialize(KHandleTable::MaxTableSize); 63 global_handle_table->Initialize(KHandleTable::MaxTableSize);
64 default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
66 65
67 is_phantom_mode_for_singlecore = false; 66 is_phantom_mode_for_singlecore = false;
68 67
@@ -76,7 +75,7 @@ struct KernelCore::Impl {
76 // Initialize kernel memory and resources. 75 // Initialize kernel memory and resources.
77 InitializeSystemResourceLimit(kernel, system.CoreTiming()); 76 InitializeSystemResourceLimit(kernel, system.CoreTiming());
78 InitializeMemoryLayout(); 77 InitializeMemoryLayout();
79 InitializePageSlab(); 78 Init::InitializeKPageBufferSlabHeap(system);
80 InitializeSchedulers(); 79 InitializeSchedulers();
81 InitializeSuspendThreads(); 80 InitializeSuspendThreads();
82 InitializePreemption(kernel); 81 InitializePreemption(kernel);
@@ -86,7 +85,7 @@ struct KernelCore::Impl {
86 85
87 void InitializeCores() { 86 void InitializeCores() {
88 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 87 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
89 cores[core_id].Initialize(current_process->Is64BitProcess()); 88 cores[core_id].Initialize((*current_process).Is64BitProcess());
90 system.Memory().SetCurrentPageTable(*current_process, core_id); 89 system.Memory().SetCurrentPageTable(*current_process, core_id);
91 } 90 }
92 } 91 }
@@ -97,30 +96,17 @@ struct KernelCore::Impl {
97 96
98 process_list.clear(); 97 process_list.clear();
99 98
100 // Close all open server ports. 99 // Close all open server sessions and ports.
101 std::unordered_set<KServerPort*> server_ports_; 100 std::unordered_set<KAutoObject*> server_objects_;
102 { 101 {
103 std::lock_guard lk(server_ports_lock); 102 std::scoped_lock lk(server_objects_lock);
104 server_ports_ = server_ports; 103 server_objects_ = server_objects;
105 server_ports.clear(); 104 server_objects.clear();
106 } 105 }
107 for (auto* server_port : server_ports_) { 106 for (auto* server_object : server_objects_) {
108 server_port->Close(); 107 server_object->Close();
109 }
110 // Close all open server sessions.
111 std::unordered_set<KServerSession*> server_sessions_;
112 {
113 std::lock_guard lk(server_sessions_lock);
114 server_sessions_ = server_sessions;
115 server_sessions.clear();
116 }
117 for (auto* server_session : server_sessions_) {
118 server_session->Close();
119 } 108 }
120 109
121 // Ensure that the object list container is finalized and properly shutdown.
122 object_list_container.Finalize();
123
124 // Ensures all service threads gracefully shutdown. 110 // Ensures all service threads gracefully shutdown.
125 ClearServiceThreads(); 111 ClearServiceThreads();
126 112
@@ -171,7 +157,7 @@ struct KernelCore::Impl {
171 157
172 // Close kernel objects that were not freed on shutdown 158 // Close kernel objects that were not freed on shutdown
173 { 159 {
174 std::lock_guard lk(registered_in_use_objects_lock); 160 std::scoped_lock lk{registered_in_use_objects_lock};
175 if (registered_in_use_objects.size()) { 161 if (registered_in_use_objects.size()) {
176 for (auto& object : registered_in_use_objects) { 162 for (auto& object : registered_in_use_objects) {
177 object->Close(); 163 object->Close();
@@ -182,23 +168,27 @@ struct KernelCore::Impl {
182 168
183 // Shutdown all processes. 169 // Shutdown all processes.
184 if (current_process) { 170 if (current_process) {
185 current_process->Finalize(); 171 (*current_process).Finalize();
186 // current_process->Close(); 172 // current_process->Close();
187 // TODO: The current process should be destroyed based on accurate ref counting after 173 // TODO: The current process should be destroyed based on accurate ref counting after
188 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak. 174 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
189 current_process->Destroy(); 175 (*current_process).Destroy();
190 current_process = nullptr; 176 current_process = nullptr;
191 } 177 }
192 178
193 // Track kernel objects that were not freed on shutdown 179 // Track kernel objects that were not freed on shutdown
194 { 180 {
195 std::lock_guard lk(registered_objects_lock); 181 std::scoped_lock lk{registered_objects_lock};
196 if (registered_objects.size()) { 182 if (registered_objects.size()) {
197 LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", 183 LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
198 registered_objects.size()); 184 registered_objects.size());
199 registered_objects.clear(); 185 registered_objects.clear();
200 } 186 }
201 } 187 }
188
189 // Ensure that the object list container is finalized and properly shutdown.
190 global_object_list_container->Finalize();
191 global_object_list_container.reset();
202 } 192 }
203 193
204 void InitializePhysicalCores() { 194 void InitializePhysicalCores() {
@@ -291,15 +281,16 @@ struct KernelCore::Impl {
291 281
292 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 282 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
293 KThread* GetHostDummyThread() { 283 KThread* GetHostDummyThread() {
294 auto make_thread = [this]() { 284 auto initialize = [this](KThread* thread) {
295 KThread* thread = KThread::Create(system.Kernel());
296 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); 285 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
297 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 286 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
298 return thread; 287 return thread;
299 }; 288 };
300 289
301 thread_local KThread* saved_thread = make_thread(); 290 thread_local auto raw_thread = KThread(system.Kernel());
302 return saved_thread; 291 thread_local auto thread = initialize(&raw_thread);
292
293 return thread;
303 } 294 }
304 295
305 /// Registers a CPU core thread by allocating a host thread ID for it 296 /// Registers a CPU core thread by allocating a host thread ID for it
@@ -660,22 +651,6 @@ struct KernelCore::Impl {
660 time_phys_addr, time_size, "Time:SharedMemory"); 651 time_phys_addr, time_size, "Time:SharedMemory");
661 } 652 }
662 653
663 void InitializePageSlab() {
664 // Allocate slab heaps
665 user_slab_heap_pages =
666 std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest);
667
668 // TODO(ameerj): This should be derived, not hardcoded within the kernel
669 constexpr u64 user_slab_heap_size{0x3de000};
670 // Reserve slab heaps
671 ASSERT(
672 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
673 // Initialize slab heap
674 user_slab_heap_pages->Initialize(
675 system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
676 user_slab_heap_size);
677 }
678
679 KClientPort* CreateNamedServicePort(std::string name) { 654 KClientPort* CreateNamedServicePort(std::string name) {
680 auto search = service_interface_factory.find(name); 655 auto search = service_interface_factory.find(name);
681 if (search == service_interface_factory.end()) { 656 if (search == service_interface_factory.end()) {
@@ -684,13 +659,20 @@ struct KernelCore::Impl {
684 } 659 }
685 660
686 KClientPort* port = &search->second(system.ServiceManager(), system); 661 KClientPort* port = &search->second(system.ServiceManager(), system);
687 { 662 RegisterServerObject(&port->GetParent()->GetServerPort());
688 std::lock_guard lk(server_ports_lock);
689 server_ports.insert(&port->GetParent()->GetServerPort());
690 }
691 return port; 663 return port;
692 } 664 }
693 665
666 void RegisterServerObject(KAutoObject* server_object) {
667 std::scoped_lock lk(server_objects_lock);
668 server_objects.insert(server_object);
669 }
670
671 void UnregisterServerObject(KAutoObject* server_object) {
672 std::scoped_lock lk(server_objects_lock);
673 server_objects.erase(server_object);
674 }
675
694 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, 676 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
695 const std::string& name) { 677 const std::string& name) {
696 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); 678 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
@@ -703,6 +685,12 @@ struct KernelCore::Impl {
703 685
704 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 686 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
705 if (auto strong_ptr = service_thread.lock()) { 687 if (auto strong_ptr = service_thread.lock()) {
688 if (strong_ptr == default_service_thread.lock()) {
689 // Nothing to do here, the service is using default_service_thread, which will be
690 // released on shutdown.
691 return;
692 }
693
706 service_threads_manager.QueueWork( 694 service_threads_manager.QueueWork(
707 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); 695 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
708 } 696 }
@@ -712,8 +700,7 @@ struct KernelCore::Impl {
712 service_threads_manager.QueueWork([this]() { service_threads.clear(); }); 700 service_threads_manager.QueueWork([this]() { service_threads.clear(); });
713 } 701 }
714 702
715 std::mutex server_ports_lock; 703 std::mutex server_objects_lock;
716 std::mutex server_sessions_lock;
717 std::mutex registered_objects_lock; 704 std::mutex registered_objects_lock;
718 std::mutex registered_in_use_objects_lock; 705 std::mutex registered_in_use_objects_lock;
719 706
@@ -724,7 +711,7 @@ struct KernelCore::Impl {
724 711
725 // Lists all processes that exist in the current session. 712 // Lists all processes that exist in the current session.
726 std::vector<KProcess*> process_list; 713 std::vector<KProcess*> process_list;
727 KProcess* current_process{}; 714 std::atomic<KProcess*> current_process{};
728 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 715 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
729 Kernel::TimeManager time_manager; 716 Kernel::TimeManager time_manager;
730 717
@@ -737,14 +724,13 @@ struct KernelCore::Impl {
737 // stores all the objects in place. 724 // stores all the objects in place.
738 std::unique_ptr<KHandleTable> global_handle_table; 725 std::unique_ptr<KHandleTable> global_handle_table;
739 726
740 KAutoObjectWithListContainer object_list_container; 727 std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
741 728
742 /// Map of named ports managed by the kernel, which can be retrieved using 729 /// Map of named ports managed by the kernel, which can be retrieved using
743 /// the ConnectToPort SVC. 730 /// the ConnectToPort SVC.
744 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; 731 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
745 NamedPortTable named_ports; 732 NamedPortTable named_ports;
746 std::unordered_set<KServerPort*> server_ports; 733 std::unordered_set<KAutoObject*> server_objects;
747 std::unordered_set<KServerSession*> server_sessions;
748 std::unordered_set<KAutoObject*> registered_objects; 734 std::unordered_set<KAutoObject*> registered_objects;
749 std::unordered_set<KAutoObject*> registered_in_use_objects; 735 std::unordered_set<KAutoObject*> registered_in_use_objects;
750 736
@@ -756,7 +742,6 @@ struct KernelCore::Impl {
756 742
757 // Kernel memory management 743 // Kernel memory management
758 std::unique_ptr<KMemoryManager> memory_manager; 744 std::unique_ptr<KMemoryManager> memory_manager;
759 std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
760 745
761 // Shared memory for services 746 // Shared memory for services
762 Kernel::KSharedMemory* hid_shared_mem{}; 747 Kernel::KSharedMemory* hid_shared_mem{};
@@ -768,7 +753,8 @@ struct KernelCore::Impl {
768 std::unique_ptr<KMemoryLayout> memory_layout; 753 std::unique_ptr<KMemoryLayout> memory_layout;
769 754
770 // Threads used for services 755 // Threads used for services
771 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; 756 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
757 std::weak_ptr<ServiceThread> default_service_thread;
772 Common::ThreadWorker service_threads_manager; 758 Common::ThreadWorker service_threads_manager;
773 759
774 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; 760 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
@@ -915,11 +901,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
915} 901}
916 902
917KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { 903KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
918 return impl->object_list_container; 904 return *impl->global_object_list_container;
919} 905}
920 906
921const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { 907const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
922 return impl->object_list_container; 908 return *impl->global_object_list_container;
923} 909}
924 910
925void KernelCore::InvalidateAllInstructionCaches() { 911void KernelCore::InvalidateAllInstructionCaches() {
@@ -949,33 +935,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
949 return impl->CreateNamedServicePort(std::move(name)); 935 return impl->CreateNamedServicePort(std::move(name));
950} 936}
951 937
952void KernelCore::RegisterServerSession(KServerSession* server_session) { 938void KernelCore::RegisterServerObject(KAutoObject* server_object) {
953 std::lock_guard lk(impl->server_sessions_lock); 939 impl->RegisterServerObject(server_object);
954 impl->server_sessions.insert(server_session);
955} 940}
956 941
957void KernelCore::UnregisterServerSession(KServerSession* server_session) { 942void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
958 std::lock_guard lk(impl->server_sessions_lock); 943 impl->UnregisterServerObject(server_object);
959 impl->server_sessions.erase(server_session);
960} 944}
961 945
962void KernelCore::RegisterKernelObject(KAutoObject* object) { 946void KernelCore::RegisterKernelObject(KAutoObject* object) {
963 std::lock_guard lk(impl->registered_objects_lock); 947 std::scoped_lock lk{impl->registered_objects_lock};
964 impl->registered_objects.insert(object); 948 impl->registered_objects.insert(object);
965} 949}
966 950
967void KernelCore::UnregisterKernelObject(KAutoObject* object) { 951void KernelCore::UnregisterKernelObject(KAutoObject* object) {
968 std::lock_guard lk(impl->registered_objects_lock); 952 std::scoped_lock lk{impl->registered_objects_lock};
969 impl->registered_objects.erase(object); 953 impl->registered_objects.erase(object);
970} 954}
971 955
972void KernelCore::RegisterInUseObject(KAutoObject* object) { 956void KernelCore::RegisterInUseObject(KAutoObject* object) {
973 std::lock_guard lk(impl->registered_in_use_objects_lock); 957 std::scoped_lock lk{impl->registered_in_use_objects_lock};
974 impl->registered_in_use_objects.insert(object); 958 impl->registered_in_use_objects.insert(object);
975} 959}
976 960
977void KernelCore::UnregisterInUseObject(KAutoObject* object) { 961void KernelCore::UnregisterInUseObject(KAutoObject* object) {
978 std::lock_guard lk(impl->registered_in_use_objects_lock); 962 std::scoped_lock lk{impl->registered_in_use_objects_lock};
979 impl->registered_in_use_objects.erase(object); 963 impl->registered_in_use_objects.erase(object);
980} 964}
981 965
@@ -1031,14 +1015,6 @@ const KMemoryManager& KernelCore::MemoryManager() const {
1031 return *impl->memory_manager; 1015 return *impl->memory_manager;
1032} 1016}
1033 1017
1034KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
1035 return *impl->user_slab_heap_pages;
1036}
1037
1038const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
1039 return *impl->user_slab_heap_pages;
1040}
1041
1042Kernel::KSharedMemory& KernelCore::GetHidSharedMem() { 1018Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
1043 return *impl->hid_shared_mem; 1019 return *impl->hid_shared_mem;
1044} 1020}
@@ -1112,6 +1088,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
1112 return impl->CreateServiceThread(*this, name); 1088 return impl->CreateServiceThread(*this, name);
1113} 1089}
1114 1090
1091std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
1092 return impl->default_service_thread;
1093}
1094
1115void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 1095void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
1116 impl->ReleaseServiceThread(service_thread); 1096 impl->ReleaseServiceThread(service_thread);
1117} 1097}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c1254b18d..d709c368b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -14,7 +14,6 @@
14#include "core/hardware_properties.h" 14#include "core/hardware_properties.h"
15#include "core/hle/kernel/k_auto_object.h" 15#include "core/hle/kernel/k_auto_object.h"
16#include "core/hle/kernel/k_slab_heap.h" 16#include "core/hle/kernel/k_slab_heap.h"
17#include "core/hle/kernel/memory_types.h"
18#include "core/hle/kernel/svc_common.h" 17#include "core/hle/kernel/svc_common.h"
19 18
20namespace Core { 19namespace Core {
@@ -43,6 +42,7 @@ class KHandleTable;
43class KLinkedListNode; 42class KLinkedListNode;
44class KMemoryLayout; 43class KMemoryLayout;
45class KMemoryManager; 44class KMemoryManager;
45class KPageBuffer;
46class KPort; 46class KPort;
47class KProcess; 47class KProcess;
48class KResourceLimit; 48class KResourceLimit;
@@ -52,6 +52,7 @@ class KSession;
52class KSharedMemory; 52class KSharedMemory;
53class KSharedMemoryInfo; 53class KSharedMemoryInfo;
54class KThread; 54class KThread;
55class KThreadLocalPage;
55class KTransferMemory; 56class KTransferMemory;
56class KWorkerTaskManager; 57class KWorkerTaskManager;
57class KWritableEvent; 58class KWritableEvent;
@@ -194,13 +195,13 @@ public:
194 /// Opens a port to a service previously registered with RegisterNamedService. 195 /// Opens a port to a service previously registered with RegisterNamedService.
195 KClientPort* CreateNamedServicePort(std::string name); 196 KClientPort* CreateNamedServicePort(std::string name);
196 197
197 /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is 198 /// Registers a server session or port with the gobal emulation state, to be freed on shutdown.
198 /// necessary because we do not emulate processes for HLE sessions. 199 /// This is necessary because we do not emulate processes for HLE sessions and ports.
199 void RegisterServerSession(KServerSession* server_session); 200 void RegisterServerObject(KAutoObject* server_object);
200 201
201 /// Unregisters a server session previously registered with RegisterServerSession when it was 202 /// Unregisters a server session or port previously registered with RegisterServerSession when
202 /// destroyed during the current emulation session. 203 /// it was destroyed during the current emulation session.
203 void UnregisterServerSession(KServerSession* server_session); 204 void UnregisterServerObject(KAutoObject* server_object);
204 205
205 /// Registers all kernel objects with the global emulation state, this is purely for tracking 206 /// Registers all kernel objects with the global emulation state, this is purely for tracking
206 /// leaks after emulation has been shutdown. 207 /// leaks after emulation has been shutdown.
@@ -239,12 +240,6 @@ public:
239 /// Gets the virtual memory manager for the kernel. 240 /// Gets the virtual memory manager for the kernel.
240 const KMemoryManager& MemoryManager() const; 241 const KMemoryManager& MemoryManager() const;
241 242
242 /// Gets the slab heap allocated for user space pages.
243 KSlabHeap<Page>& GetUserSlabHeapPages();
244
245 /// Gets the slab heap allocated for user space pages.
246 const KSlabHeap<Page>& GetUserSlabHeapPages() const;
247
248 /// Gets the shared memory object for HID services. 243 /// Gets the shared memory object for HID services.
249 Kernel::KSharedMemory& GetHidSharedMem(); 244 Kernel::KSharedMemory& GetHidSharedMem();
250 245
@@ -284,9 +279,11 @@ public:
284 void ExitSVCProfile(); 279 void ExitSVCProfile();
285 280
286 /** 281 /**
287 * Creates an HLE service thread, which are used to execute service routines asynchronously. 282 * Creates a host thread to execute HLE service requests, which are used to execute service
288 * While these are allocated per ServerSession, these need to be owned and managed outside 283 * routines asynchronously. While these are allocated per ServerSession, these need to be owned
289 * of ServerSession to avoid a circular dependency. 284 * and managed outside of ServerSession to avoid a circular dependency. In general, most
285 * services can just use the default service thread, and not need their own host service thread.
286 * See GetDefaultServiceThread.
290 * @param name String name for the ServerSession creating this thread, used for debug 287 * @param name String name for the ServerSession creating this thread, used for debug
291 * purposes. 288 * purposes.
292 * @returns The a weak pointer newly created service thread. 289 * @returns The a weak pointer newly created service thread.
@@ -294,6 +291,14 @@ public:
294 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); 291 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
295 292
296 /** 293 /**
294 * Gets the default host service thread, which executes HLE service requests. Unless service
295 * requests need to block on the host, the default service thread should be used in favor of
296 * creating a new service thread.
297 * @returns The a weak pointer for the default service thread.
298 */
299 std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
300
301 /**
297 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when 302 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
298 * the ServerSession associated with the thread is destroyed. 303 * the ServerSession associated with the thread is destroyed.
299 * @param service_thread Service thread to release. 304 * @param service_thread Service thread to release.
@@ -336,6 +341,10 @@ public:
336 return slab_heap_container->writeable_event; 341 return slab_heap_container->writeable_event;
337 } else if constexpr (std::is_same_v<T, KCodeMemory>) { 342 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
338 return slab_heap_container->code_memory; 343 return slab_heap_container->code_memory;
344 } else if constexpr (std::is_same_v<T, KPageBuffer>) {
345 return slab_heap_container->page_buffer;
346 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
347 return slab_heap_container->thread_local_page;
339 } 348 }
340 } 349 }
341 350
@@ -397,6 +406,8 @@ private:
397 KSlabHeap<KTransferMemory> transfer_memory; 406 KSlabHeap<KTransferMemory> transfer_memory;
398 KSlabHeap<KWritableEvent> writeable_event; 407 KSlabHeap<KWritableEvent> writeable_event;
399 KSlabHeap<KCodeMemory> code_memory; 408 KSlabHeap<KCodeMemory> code_memory;
409 KSlabHeap<KPageBuffer> page_buffer;
410 KSlabHeap<KThreadLocalPage> thread_local_page;
400 }; 411 };
401 412
402 std::unique_ptr<SlabHeapContainer> slab_heap_container; 413 std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 7477668e4..18a5f40f8 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -58,6 +58,7 @@ bool PhysicalCore::IsInterrupted() const {
58void PhysicalCore::Interrupt() { 58void PhysicalCore::Interrupt() {
59 guard->lock(); 59 guard->lock();
60 interrupts[core_index].SetInterrupt(true); 60 interrupts[core_index].SetInterrupt(true);
61 arm_interface->SignalInterrupt();
61 guard->unlock(); 62 guard->unlock();
62} 63}
63 64
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 4eb3a5988..52d25b837 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -49,12 +49,9 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
49 return; 49 return;
50 } 50 }
51 51
52 // Allocate a dummy guest thread for this host thread.
52 kernel.RegisterHostThread(); 53 kernel.RegisterHostThread();
53 54
54 // Ensure the dummy thread allocated for this host thread is closed on exit.
55 auto* dummy_thread = kernel.GetCurrentEmuThread();
56 SCOPE_EXIT({ dummy_thread->Close(); });
57
58 while (true) { 55 while (true) {
59 std::function<void()> task; 56 std::function<void()> task;
60 57
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h
index f1c11256e..dc1e48fc9 100644
--- a/src/core/hle/kernel/slab_helpers.h
+++ b/src/core/hle/kernel/slab_helpers.h
@@ -59,7 +59,7 @@ class KAutoObjectWithSlabHeapAndContainer : public Base {
59 59
60private: 60private:
61 static Derived* Allocate(KernelCore& kernel) { 61 static Derived* Allocate(KernelCore& kernel) {
62 return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel); 62 return kernel.SlabHeap<Derived>().Allocate(kernel);
63 } 63 }
64 64
65 static void Free(KernelCore& kernel, Derived* obj) { 65 static void Free(KernelCore& kernel, Derived* obj) {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 839171e85..0c86435b5 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1362,8 +1362,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
1362 ResultInvalidMemoryRegion); 1362 ResultInvalidMemoryRegion);
1363 1363
1364 // Create a new page group. 1364 // Create a new page group.
1365 KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address); 1365 KPageLinkedList pg;
1366 KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages()); 1366 R_TRY(src_pt.MakeAndOpenPageGroup(
1367 std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
1368 KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
1369 KMemoryAttribute::All, KMemoryAttribute::None));
1367 1370
1368 // Map the group. 1371 // Map the group.
1369 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, 1372 R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
@@ -1408,8 +1411,8 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
1408} 1411}
1409 1412
1410static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) { 1413static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
1411 LOG_TRACE(Kernel_SVC, "called, handle_out={}, address=0x{:X}, size=0x{:X}", 1414 LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
1412 static_cast<void*>(out), address, size); 1415
1413 // Get kernel instance. 1416 // Get kernel instance.
1414 auto& kernel = system.Kernel(); 1417 auto& kernel = system.Kernel();
1415 1418
@@ -1664,7 +1667,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1664 return ResultInvalidAddress; 1667 return ResultInvalidAddress;
1665 } 1668 }
1666 1669
1667 if (size == 0 || Common::Is4KBAligned(size)) { 1670 if (size == 0 || !Common::Is4KBAligned(size)) {
1668 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); 1671 LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
1669 return ResultInvalidSize; 1672 return ResultInvalidSize;
1670 } 1673 }
@@ -1710,7 +1713,8 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
1710 return ResultInvalidMemoryRegion; 1713 return ResultInvalidMemoryRegion;
1711 } 1714 }
1712 1715
1713 return page_table.UnmapCodeMemory(dst_address, src_address, size); 1716 return page_table.UnmapCodeMemory(dst_address, src_address, size,
1717 KPageTable::ICacheInvalidationStrategy::InvalidateAll);
1714} 1718}
1715 1719
1716/// Exits the current process 1720/// Exits the current process
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 365e22e4e..b2e9ec092 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -96,4 +96,6 @@ constexpr inline s32 IdealCoreNoUpdate = -3;
96constexpr inline s32 LowestThreadPriority = 63; 96constexpr inline s32 LowestThreadPriority = 63;
97constexpr inline s32 HighestThreadPriority = 0; 97constexpr inline s32 HighestThreadPriority = 0;
98 98
99constexpr inline size_t ThreadLocalRegionSize = 0x200;
100
99} // namespace Kernel::Svc 101} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index aa985d820..5b8fe8eae 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -24,7 +24,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
24} 24}
25 25
26void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { 26void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
27 std::lock_guard lock{mutex}; 27 std::scoped_lock lock{mutex};
28 if (nanoseconds > 0) { 28 if (nanoseconds > 0) {
29 ASSERT(thread); 29 ASSERT(thread);
30 ASSERT(thread->GetState() != ThreadState::Runnable); 30 ASSERT(thread->GetState() != ThreadState::Runnable);
@@ -35,7 +35,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
35} 35}
36 36
37void TimeManager::UnscheduleTimeEvent(KThread* thread) { 37void TimeManager::UnscheduleTimeEvent(KThread* thread) {
38 std::lock_guard lock{mutex}; 38 std::scoped_lock lock{mutex};
39 system.CoreTiming().UnscheduleEvent(time_manager_event_type, 39 system.CoreTiming().UnscheduleEvent(time_manager_event_type,
40 reinterpret_cast<uintptr_t>(thread)); 40 reinterpret_cast<uintptr_t>(thread));
41} 41}
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e34ef5a78..8a5332991 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -16,7 +16,6 @@
16#include "core/file_sys/control_metadata.h" 16#include "core/file_sys/control_metadata.h"
17#include "core/file_sys/patch_manager.h" 17#include "core/file_sys/patch_manager.h"
18#include "core/hle/ipc_helpers.h" 18#include "core/hle/ipc_helpers.h"
19#include "core/hle/kernel/kernel.h"
20#include "core/hle/service/acc/acc.h" 19#include "core/hle/service/acc/acc.h"
21#include "core/hle/service/acc/acc_aa.h" 20#include "core/hle/service/acc/acc_aa.h"
22#include "core/hle/service/acc/acc_su.h" 21#include "core/hle/service/acc/acc_su.h"
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 17347f7ef..5b690b406 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <optional> 8#include <optional>
9 9
10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "common/swap.h" 12#include "common/swap.h"
12#include "common/uuid.h" 13#include "common/uuid.h"
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 2f8e21568..4d7e5ecd3 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -980,7 +980,7 @@ private:
980 LOG_DEBUG(Service_AM, "called"); 980 LOG_DEBUG(Service_AM, "called");
981 981
982 IPC::RequestParser rp{ctx}; 982 IPC::RequestParser rp{ctx};
983 applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>()); 983 applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock());
984 984
985 IPC::ResponseBuilder rb{ctx, 2}; 985 IPC::ResponseBuilder rb{ctx, 2};
986 rb.Push(ResultSuccess); 986 rb.Push(ResultSuccess);
@@ -1007,7 +1007,7 @@ private:
1007 LOG_DEBUG(Service_AM, "called"); 1007 LOG_DEBUG(Service_AM, "called");
1008 1008
1009 IPC::RequestParser rp{ctx}; 1009 IPC::RequestParser rp{ctx};
1010 applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>()); 1010 applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock());
1011 1011
1012 ASSERT(applet->IsInitialized()); 1012 ASSERT(applet->IsInitialized());
1013 applet->ExecuteInteractive(); 1013 applet->ExecuteInteractive();
@@ -1337,7 +1337,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1337 {200, nullptr, "GetLastApplicationExitReason"}, 1337 {200, nullptr, "GetLastApplicationExitReason"},
1338 {500, nullptr, "StartContinuousRecordingFlushForDebug"}, 1338 {500, nullptr, "StartContinuousRecordingFlushForDebug"},
1339 {1000, nullptr, "CreateMovieMaker"}, 1339 {1000, nullptr, "CreateMovieMaker"},
1340 {1001, nullptr, "PrepareForJit"}, 1340 {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
1341 }; 1341 };
1342 // clang-format on 1342 // clang-format on
1343 1343
@@ -1787,6 +1787,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
1787 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent()); 1787 rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
1788} 1788}
1789 1789
1790void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) {
1791 LOG_WARNING(Service_AM, "(STUBBED) called");
1792
1793 IPC::ResponseBuilder rb{ctx, 2};
1794 rb.Push(ResultSuccess);
1795}
1796
1790void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, 1797void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
1791 Core::System& system) { 1798 Core::System& system) {
1792 auto message_queue = std::make_shared<AppletMessageQueue>(system); 1799 auto message_queue = std::make_shared<AppletMessageQueue>(system);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index fdd937b82..11a3c0459 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -336,6 +336,7 @@ private:
336 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); 336 void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
337 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx); 337 void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
338 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx); 338 void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
339 void PrepareForJit(Kernel::HLERequestContext& ctx);
339 340
340 KernelHelpers::ServiceContext service_context; 341 KernelHelpers::ServiceContext service_context;
341 342
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.cpp b/src/core/hle/service/am/applets/applet_mii_edit.cpp
new file mode 100644
index 000000000..8fbde1be4
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -0,0 +1,139 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/frontend/applets/mii_edit.h"
9#include "core/hle/service/am/am.h"
10#include "core/hle/service/am/applets/applet_mii_edit.h"
11#include "core/hle/service/mii/mii_manager.h"
12
13namespace Service::AM::Applets {
14
15MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
16 const Core::Frontend::MiiEditApplet& frontend_)
17 : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
18
19MiiEdit::~MiiEdit() = default;
20
21void MiiEdit::Initialize() {
22 // Note: MiiEdit is not initialized with common arguments.
23 // Instead, it is initialized by an AppletInput storage with size 0x100 bytes.
24 // Do NOT call Applet::Initialize() here.
25
26 const auto storage = broker.PopNormalDataToApplet();
27 ASSERT(storage != nullptr);
28
29 const auto applet_input_data = storage->GetData();
30 ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon));
31
32 std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon));
33
34 LOG_INFO(Service_AM,
35 "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}",
36 applet_input_common.version, applet_input_common.applet_mode);
37
38 switch (applet_input_common.version) {
39 case MiiEditAppletVersion::Version3:
40 ASSERT(applet_input_data.size() ==
41 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3));
42 std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
43 sizeof(MiiEditAppletInputV3));
44 break;
45 case MiiEditAppletVersion::Version4:
46 ASSERT(applet_input_data.size() ==
47 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
48 std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
49 sizeof(MiiEditAppletInputV4));
50 break;
51 default:
52 UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}",
53 applet_input_common.version, applet_input_data.size());
54 ASSERT(applet_input_data.size() >=
55 sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
56 std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
57 sizeof(MiiEditAppletInputV4));
58 break;
59 }
60}
61
62bool MiiEdit::TransactionComplete() const {
63 return is_complete;
64}
65
66ResultCode MiiEdit::GetStatus() const {
67 return ResultSuccess;
68}
69
70void MiiEdit::ExecuteInteractive() {
71 UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
72}
73
74void MiiEdit::Execute() {
75 if (is_complete) {
76 return;
77 }
78
79 // This is a default stub for each of the MiiEdit applet modes.
80 switch (applet_input_common.applet_mode) {
81 case MiiEditAppletMode::ShowMiiEdit:
82 case MiiEditAppletMode::AppendMii:
83 case MiiEditAppletMode::AppendMiiImage:
84 case MiiEditAppletMode::UpdateMiiImage:
85 MiiEditOutput(MiiEditResult::Success, 0);
86 break;
87 case MiiEditAppletMode::CreateMii:
88 case MiiEditAppletMode::EditMii: {
89 Service::Mii::MiiManager mii_manager;
90
91 const MiiEditCharInfo char_info{
92 .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
93 ? applet_input_v4.char_info.mii_info
94 : mii_manager.BuildDefault(0)},
95 };
96
97 MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info);
98 break;
99 }
100 default:
101 UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode);
102
103 MiiEditOutput(MiiEditResult::Success, 0);
104 break;
105 }
106}
107
108void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
109 const MiiEditAppletOutput applet_output{
110 .result{result},
111 .index{index},
112 };
113
114 std::vector<u8> out_data(sizeof(MiiEditAppletOutput));
115 std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput));
116
117 is_complete = true;
118
119 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
120 broker.SignalStateChanged();
121}
122
123void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
124 const MiiEditCharInfo& char_info) {
125 const MiiEditAppletOutputForCharInfoEditing applet_output{
126 .result{result},
127 .char_info{char_info},
128 };
129
130 std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing));
131 std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing));
132
133 is_complete = true;
134
135 broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
136 broker.SignalStateChanged();
137}
138
139} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_mii_edit.h b/src/core/hle/service/am/applets/applet_mii_edit.h
new file mode 100644
index 000000000..e9ca0e2af
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit.h
@@ -0,0 +1,45 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8#include "core/hle/service/am/applets/applet_mii_edit_types.h"
9#include "core/hle/service/am/applets/applets.h"
10
11namespace Core {
12class System;
13} // namespace Core
14
15namespace Service::AM::Applets {
16
17class MiiEdit final : public Applet {
18public:
19 explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
20 const Core::Frontend::MiiEditApplet& frontend_);
21 ~MiiEdit() override;
22
23 void Initialize() override;
24
25 bool TransactionComplete() const override;
26 ResultCode GetStatus() const override;
27 void ExecuteInteractive() override;
28 void Execute() override;
29
30 void MiiEditOutput(MiiEditResult result, s32 index);
31
32 void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info);
33
34private:
35 const Core::Frontend::MiiEditApplet& frontend;
36 Core::System& system;
37
38 MiiEditAppletInputCommon applet_input_common{};
39 MiiEditAppletInputV3 applet_input_v3{};
40 MiiEditAppletInputV4 applet_input_v4{};
41
42 bool is_complete{false};
43};
44
45} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_mii_edit_types.h b/src/core/hle/service/am/applets/applet_mii_edit_types.h
new file mode 100644
index 000000000..70dea0007
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h
@@ -0,0 +1,83 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/hle/service/mii/types.h"
12
13namespace Service::AM::Applets {
14
15enum class MiiEditAppletVersion : s32 {
16 Version3 = 0x3, // 1.0.0 - 10.1.1
17 Version4 = 0x4, // 10.2.0+
18};
19
20// This is nn::mii::AppletMode
21enum class MiiEditAppletMode : u32 {
22 ShowMiiEdit = 0,
23 AppendMii = 1,
24 AppendMiiImage = 2,
25 UpdateMiiImage = 3,
26 CreateMii = 4,
27 EditMii = 5,
28};
29
30enum class MiiEditResult : u32 {
31 Success,
32 Cancel,
33};
34
35struct MiiEditCharInfo {
36 Service::Mii::MiiInfo mii_info{};
37};
38static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");
39
40struct MiiEditAppletInputCommon {
41 MiiEditAppletVersion version{};
42 MiiEditAppletMode applet_mode{};
43};
44static_assert(sizeof(MiiEditAppletInputCommon) == 0x8,
45 "MiiEditAppletInputCommon has incorrect size.");
46
47struct MiiEditAppletInputV3 {
48 u32 special_mii_key_code{};
49 std::array<Common::UUID, 8> valid_uuids{};
50 Common::UUID used_uuid{};
51 INSERT_PADDING_BYTES(0x64);
52};
53static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon),
54 "MiiEditAppletInputV3 has incorrect size.");
55
56struct MiiEditAppletInputV4 {
57 u32 special_mii_key_code{};
58 MiiEditCharInfo char_info{};
59 INSERT_PADDING_BYTES(0x28);
60 Common::UUID used_uuid{};
61 INSERT_PADDING_BYTES(0x64);
62};
63static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon),
64 "MiiEditAppletInputV4 has incorrect size.");
65
66// This is nn::mii::AppletOutput
67struct MiiEditAppletOutput {
68 MiiEditResult result{};
69 s32 index{};
70 INSERT_PADDING_BYTES(0x18);
71};
72static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size.");
73
74// This is nn::mii::AppletOutputForCharInfoEditing
75struct MiiEditAppletOutputForCharInfoEditing {
76 MiiEditResult result{};
77 MiiEditCharInfo char_info{};
78 INSERT_PADDING_BYTES(0x24);
79};
80static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80,
81 "MiiEditAppletOutputForCharInfoEditing has incorrect size.");
82
83} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index f38f53f69..ee669686c 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -226,7 +226,7 @@ void SoftwareKeyboard::InitializeForeground() {
226 ASSERT(work_buffer_storage != nullptr); 226 ASSERT(work_buffer_storage != nullptr);
227 227
228 if (swkbd_config_common.initial_string_length == 0) { 228 if (swkbd_config_common.initial_string_length == 0) {
229 InitializeFrontendKeyboard(); 229 InitializeFrontendNormalKeyboard();
230 return; 230 return;
231 } 231 }
232 232
@@ -243,7 +243,7 @@ void SoftwareKeyboard::InitializeForeground() {
243 243
244 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text)); 244 LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
245 245
246 InitializeFrontendKeyboard(); 246 InitializeFrontendNormalKeyboard();
247} 247}
248 248
249void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) { 249void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
@@ -480,129 +480,173 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
480 ReplyDefault(); 480 ReplyDefault();
481} 481}
482 482
483void SoftwareKeyboard::InitializeFrontendKeyboard() { 483void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
484 if (is_background) { 484 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
485 const auto& appear_arg = swkbd_calc_arg.appear_arg; 485 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
486 486
487 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 487 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
488 appear_arg.ok_text.data(), appear_arg.ok_text.size()); 488 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
489 489
490 const u32 max_text_length = 490 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
491 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 491 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
492 ? appear_arg.max_text_length 492
493 : DEFAULT_MAX_TEXT_LENGTH; 493 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
494 494 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
495 const u32 min_text_length = 495
496 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0; 496 const u32 max_text_length =
497 497 swkbd_config_common.max_text_length > 0 &&
498 const s32 initial_cursor_position = 498 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
499 current_cursor_position > 0 ? current_cursor_position : 0; 499 ? swkbd_config_common.max_text_length
500 500 : DEFAULT_MAX_TEXT_LENGTH;
501 const auto text_draw_type = 501
502 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 502 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
503 503 ? swkbd_config_common.min_text_length
504 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 504 : 0;
505 .ok_text{std::move(ok_text)}, 505
506 .header_text{}, 506 const s32 initial_cursor_position = [this] {
507 .sub_text{}, 507 switch (swkbd_config_common.initial_cursor_position) {
508 .guide_text{}, 508 case SwkbdInitialCursorPosition::Start:
509 .initial_text{current_text}, 509 default:
510 .max_text_length{max_text_length}, 510 return 0;
511 .min_text_length{min_text_length}, 511 case SwkbdInitialCursorPosition::End:
512 .initial_cursor_position{initial_cursor_position}, 512 return static_cast<s32>(initial_text.size());
513 .type{appear_arg.type}, 513 }
514 .password_mode{SwkbdPasswordMode::Disabled}, 514 }();
515 .text_draw_type{text_draw_type}, 515
516 .key_disable_flags{appear_arg.key_disable_flags}, 516 const auto text_draw_type = [this, max_text_length] {
517 .use_blur_background{false}, 517 switch (swkbd_config_common.text_draw_type) {
518 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, 518 case SwkbdTextDrawType::Line:
519 .enable_return_button{appear_arg.enable_return_button}, 519 default:
520 .disable_cancel_button{appear_arg.disable_cancel_button}, 520 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
521 }; 521 case SwkbdTextDrawType::Box:
522 522 case SwkbdTextDrawType::DownloadCode:
523 frontend.InitializeKeyboard( 523 return swkbd_config_common.text_draw_type;
524 true, std::move(initialize_parameters), {}, 524 }
525 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) { 525 }();
526 SubmitTextInline(reply_type, submitted_text, cursor_position); 526
527 }); 527 const auto enable_return_button =
528 } else { 528 text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
529 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 529
530 swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size()); 530 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
531 531 ? swkbd_config_new.disable_cancel_button
532 std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 532 : false;
533 swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size()); 533
534 534 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
535 std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 535 .ok_text{std::move(ok_text)},
536 swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size()); 536 .header_text{std::move(header_text)},
537 537 .sub_text{std::move(sub_text)},
538 std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 538 .guide_text{std::move(guide_text)},
539 swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size()); 539 .initial_text{initial_text},
540 540 .max_text_length{max_text_length},
541 const u32 max_text_length = 541 .min_text_length{min_text_length},
542 swkbd_config_common.max_text_length > 0 && 542 .initial_cursor_position{initial_cursor_position},
543 swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 543 .type{swkbd_config_common.type},
544 ? swkbd_config_common.max_text_length 544 .password_mode{swkbd_config_common.password_mode},
545 : DEFAULT_MAX_TEXT_LENGTH; 545 .text_draw_type{text_draw_type},
546 546 .key_disable_flags{swkbd_config_common.key_disable_flags},
547 const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length 547 .use_blur_background{swkbd_config_common.use_blur_background},
548 ? swkbd_config_common.min_text_length 548 .enable_backspace_button{true},
549 : 0; 549 .enable_return_button{enable_return_button},
550 550 .disable_cancel_button{disable_cancel_button},
551 const s32 initial_cursor_position = [this] { 551 };
552 switch (swkbd_config_common.initial_cursor_position) { 552
553 case SwkbdInitialCursorPosition::Start: 553 frontend.InitializeKeyboard(
554 default: 554 false, std::move(initialize_parameters),
555 return 0; 555 [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
556 case SwkbdInitialCursorPosition::End: 556 SubmitTextNormal(result, submitted_text, confirmed);
557 return static_cast<s32>(initial_text.size()); 557 },
558 } 558 {});
559 }(); 559}
560 560
561 const auto text_draw_type = [this, max_text_length] { 561void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
562 switch (swkbd_config_common.text_draw_type) { 562 Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
563 case SwkbdTextDrawType::Line: 563 frontend.InitializeKeyboard(
564 default: 564 true, std::move(initialize_parameters), {},
565 return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box; 565 [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
566 case SwkbdTextDrawType::Box: 566 SubmitTextInline(reply_type, submitted_text, cursor_position);
567 case SwkbdTextDrawType::DownloadCode: 567 });
568 return swkbd_config_common.text_draw_type; 568}
569 } 569
570 }(); 570void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
571 571 const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
572 const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box 572
573 ? swkbd_config_common.enable_return_button 573 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
574 : false; 574 appear_arg.ok_text.data(), appear_arg.ok_text.size());
575 575
576 const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227 576 const u32 max_text_length =
577 ? swkbd_config_new.disable_cancel_button 577 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
578 : false; 578 ? appear_arg.max_text_length
579 579 : DEFAULT_MAX_TEXT_LENGTH;
580 Core::Frontend::KeyboardInitializeParameters initialize_parameters{ 580
581 .ok_text{std::move(ok_text)}, 581 const u32 min_text_length =
582 .header_text{std::move(header_text)}, 582 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
583 .sub_text{std::move(sub_text)}, 583
584 .guide_text{std::move(guide_text)}, 584 const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
585 .initial_text{initial_text}, 585
586 .max_text_length{max_text_length}, 586 const auto text_draw_type =
587 .min_text_length{min_text_length}, 587 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
588 .initial_cursor_position{initial_cursor_position}, 588
589 .type{swkbd_config_common.type}, 589 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
590 .password_mode{swkbd_config_common.password_mode}, 590 .ok_text{std::move(ok_text)},
591 .text_draw_type{text_draw_type}, 591 .header_text{},
592 .key_disable_flags{swkbd_config_common.key_disable_flags}, 592 .sub_text{},
593 .use_blur_background{swkbd_config_common.use_blur_background}, 593 .guide_text{},
594 .enable_backspace_button{true}, 594 .initial_text{current_text},
595 .enable_return_button{enable_return_button}, 595 .max_text_length{max_text_length},
596 .disable_cancel_button{disable_cancel_button}, 596 .min_text_length{min_text_length},
597 }; 597 .initial_cursor_position{initial_cursor_position},
598 598 .type{appear_arg.type},
599 frontend.InitializeKeyboard( 599 .password_mode{SwkbdPasswordMode::Disabled},
600 false, std::move(initialize_parameters), 600 .text_draw_type{text_draw_type},
601 [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) { 601 .key_disable_flags{appear_arg.key_disable_flags},
602 SubmitTextNormal(result, submitted_text, confirmed); 602 .use_blur_background{false},
603 }, 603 .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
604 {}); 604 .enable_return_button{appear_arg.enable_return_button},
605 } 605 .disable_cancel_button{appear_arg.disable_cancel_button},
606 };
607
608 InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
609}
610
611void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
612 const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
613
614 std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
615 appear_arg.ok_text.data(), appear_arg.ok_text.size());
616
617 const u32 max_text_length =
618 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
619 ? appear_arg.max_text_length
620 : DEFAULT_MAX_TEXT_LENGTH;
621
622 const u32 min_text_length =
623 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
624
625 const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
626
627 const auto text_draw_type =
628 max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
629
630 Core::Frontend::KeyboardInitializeParameters initialize_parameters{
631 .ok_text{std::move(ok_text)},
632 .header_text{},
633 .sub_text{},
634 .guide_text{},
635 .initial_text{current_text},
636 .max_text_length{max_text_length},
637 .min_text_length{min_text_length},
638 .initial_cursor_position{initial_cursor_position},
639 .type{appear_arg.type},
640 .password_mode{SwkbdPasswordMode::Disabled},
641 .text_draw_type{text_draw_type},
642 .key_disable_flags{appear_arg.key_disable_flags},
643 .use_blur_background{false},
644 .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
645 .enable_return_button{appear_arg.enable_return_button},
646 .disable_cancel_button{appear_arg.disable_cancel_button},
647 };
648
649 InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
606} 650}
607 651
608void SoftwareKeyboard::ShowNormalKeyboard() { 652void SoftwareKeyboard::ShowNormalKeyboard() {
@@ -614,14 +658,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
614 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message)); 658 frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
615} 659}
616 660
617void SoftwareKeyboard::ShowInlineKeyboard() { 661void SoftwareKeyboard::ShowInlineKeyboard(
662 Core::Frontend::InlineAppearParameters appear_parameters) {
663 frontend.ShowInlineKeyboard(std::move(appear_parameters));
664
665 ChangeState(SwkbdState::InitializedIsShown);
666}
667
668void SoftwareKeyboard::ShowInlineKeyboardOld() {
618 if (swkbd_state != SwkbdState::InitializedIsHidden) { 669 if (swkbd_state != SwkbdState::InitializedIsHidden) {
619 return; 670 return;
620 } 671 }
621 672
622 ChangeState(SwkbdState::InitializedIsAppearing); 673 ChangeState(SwkbdState::InitializedIsAppearing);
623 674
624 const auto& appear_arg = swkbd_calc_arg.appear_arg; 675 const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
625 676
626 const u32 max_text_length = 677 const u32 max_text_length =
627 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH 678 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
@@ -634,21 +685,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
634 Core::Frontend::InlineAppearParameters appear_parameters{ 685 Core::Frontend::InlineAppearParameters appear_parameters{
635 .max_text_length{max_text_length}, 686 .max_text_length{max_text_length},
636 .min_text_length{min_text_length}, 687 .min_text_length{min_text_length},
637 .key_top_scale_x{swkbd_calc_arg.key_top_scale_x}, 688 .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
638 .key_top_scale_y{swkbd_calc_arg.key_top_scale_y}, 689 .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
639 .key_top_translate_x{swkbd_calc_arg.key_top_translate_x}, 690 .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
640 .key_top_translate_y{swkbd_calc_arg.key_top_translate_y}, 691 .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
641 .type{appear_arg.type}, 692 .type{appear_arg.type},
642 .key_disable_flags{appear_arg.key_disable_flags}, 693 .key_disable_flags{appear_arg.key_disable_flags},
643 .key_top_as_floating{swkbd_calc_arg.key_top_as_floating}, 694 .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
644 .enable_backspace_button{swkbd_calc_arg.enable_backspace_button}, 695 .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
645 .enable_return_button{appear_arg.enable_return_button}, 696 .enable_return_button{appear_arg.enable_return_button},
646 .disable_cancel_button{appear_arg.disable_cancel_button}, 697 .disable_cancel_button{appear_arg.disable_cancel_button},
647 }; 698 };
648 699
649 frontend.ShowInlineKeyboard(std::move(appear_parameters)); 700 ShowInlineKeyboard(std::move(appear_parameters));
701}
650 702
651 ChangeState(SwkbdState::InitializedIsShown); 703void SoftwareKeyboard::ShowInlineKeyboardNew() {
704 if (swkbd_state != SwkbdState::InitializedIsHidden) {
705 return;
706 }
707
708 ChangeState(SwkbdState::InitializedIsAppearing);
709
710 const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
711
712 const u32 max_text_length =
713 appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
714 ? appear_arg.max_text_length
715 : DEFAULT_MAX_TEXT_LENGTH;
716
717 const u32 min_text_length =
718 appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
719
720 Core::Frontend::InlineAppearParameters appear_parameters{
721 .max_text_length{max_text_length},
722 .min_text_length{min_text_length},
723 .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
724 .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
725 .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
726 .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
727 .type{appear_arg.type},
728 .key_disable_flags{appear_arg.key_disable_flags},
729 .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
730 .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
731 .enable_return_button{appear_arg.enable_return_button},
732 .disable_cancel_button{appear_arg.disable_cancel_button},
733 };
734
735 ShowInlineKeyboard(std::move(appear_parameters));
652} 736}
653 737
654void SoftwareKeyboard::HideInlineKeyboard() { 738void SoftwareKeyboard::HideInlineKeyboard() {
@@ -693,6 +777,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
693 777
694void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) { 778void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
695 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented."); 779 LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
780
781 ReplyReleasedUserWordInfo();
696} 782}
697 783
698void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) { 784void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
@@ -702,53 +788,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
702void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) { 788void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
703 LOG_DEBUG(Service_AM, "Processing Request: Calc"); 789 LOG_DEBUG(Service_AM, "Processing Request: Calc");
704 790
705 ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg)); 791 ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
706 792
707 std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand), 793 std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
708 sizeof(SwkbdCalcArg)); 794 sizeof(SwkbdCalcArgCommon));
795
796 switch (swkbd_calc_arg_common.calc_arg_size) {
797 case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
798 ASSERT(request_data.size() ==
799 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
800 std::memcpy(&swkbd_calc_arg_old,
801 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
802 sizeof(SwkbdCalcArgOld));
803 RequestCalcOld();
804 break;
805 case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
806 ASSERT(request_data.size() ==
807 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
808 std::memcpy(&swkbd_calc_arg_new,
809 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
810 sizeof(SwkbdCalcArgNew));
811 RequestCalcNew();
812 break;
813 default:
814 UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
815 ASSERT(request_data.size() >=
816 sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
817 std::memcpy(&swkbd_calc_arg_new,
818 request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
819 sizeof(SwkbdCalcArgNew));
820 RequestCalcNew();
821 break;
822 }
823}
824
825void SoftwareKeyboard::RequestCalcOld() {
826 if (swkbd_calc_arg_common.flags.set_input_text) {
827 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
828 swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
829 }
830
831 if (swkbd_calc_arg_common.flags.set_cursor_position) {
832 current_cursor_position = swkbd_calc_arg_old.cursor_position;
833 }
834
835 if (swkbd_calc_arg_common.flags.set_utf8_mode) {
836 inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
837 }
838
839 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
840 swkbd_calc_arg_common.flags.unset_customize_dic) {
841 ReplyUnsetCustomizeDic();
842 }
843
844 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
845 swkbd_calc_arg_common.flags.unset_user_word_info) {
846 ReplyReleasedUserWordInfo();
847 }
848
849 if (swkbd_state == SwkbdState::NotInitialized &&
850 swkbd_calc_arg_common.flags.set_initialize_arg) {
851 InitializeFrontendInlineKeyboardOld();
852
853 ChangeState(SwkbdState::InitializedIsHidden);
854
855 ReplyFinishedInitialize();
856 }
857
858 if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
859 (swkbd_calc_arg_common.flags.set_input_text ||
860 swkbd_calc_arg_common.flags.set_cursor_position)) {
861 InlineTextChanged();
862 }
863
864 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
865 ShowInlineKeyboardOld();
866 return;
867 }
868
869 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
870 HideInlineKeyboard();
871 return;
872 }
873}
709 874
710 if (swkbd_calc_arg.flags.set_input_text) { 875void SoftwareKeyboard::RequestCalcNew() {
876 if (swkbd_calc_arg_common.flags.set_input_text) {
711 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer( 877 current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
712 swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size()); 878 swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
713 } 879 }
714 880
715 if (swkbd_calc_arg.flags.set_cursor_position) { 881 if (swkbd_calc_arg_common.flags.set_cursor_position) {
716 current_cursor_position = swkbd_calc_arg.cursor_position; 882 current_cursor_position = swkbd_calc_arg_new.cursor_position;
717 } 883 }
718 884
719 if (swkbd_calc_arg.flags.set_utf8_mode) { 885 if (swkbd_calc_arg_common.flags.set_utf8_mode) {
720 inline_use_utf8 = swkbd_calc_arg.utf8_mode; 886 inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
721 } 887 }
722 888
723 if (swkbd_state <= SwkbdState::InitializedIsHidden && 889 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
724 swkbd_calc_arg.flags.unset_customize_dic) { 890 swkbd_calc_arg_common.flags.unset_customize_dic) {
725 ReplyUnsetCustomizeDic(); 891 ReplyUnsetCustomizeDic();
726 } 892 }
727 893
728 if (swkbd_state <= SwkbdState::InitializedIsHidden && 894 if (swkbd_state <= SwkbdState::InitializedIsHidden &&
729 swkbd_calc_arg.flags.unset_user_word_info) { 895 swkbd_calc_arg_common.flags.unset_user_word_info) {
730 ReplyReleasedUserWordInfo(); 896 ReplyReleasedUserWordInfo();
731 } 897 }
732 898
733 if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) { 899 if (swkbd_state == SwkbdState::NotInitialized &&
734 InitializeFrontendKeyboard(); 900 swkbd_calc_arg_common.flags.set_initialize_arg) {
901 InitializeFrontendInlineKeyboardNew();
735 902
736 ChangeState(SwkbdState::InitializedIsHidden); 903 ChangeState(SwkbdState::InitializedIsHidden);
737 904
738 ReplyFinishedInitialize(); 905 ReplyFinishedInitialize();
739 } 906 }
740 907
741 if (!swkbd_calc_arg.flags.set_initialize_arg && 908 if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
742 (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) { 909 (swkbd_calc_arg_common.flags.set_input_text ||
910 swkbd_calc_arg_common.flags.set_cursor_position)) {
743 InlineTextChanged(); 911 InlineTextChanged();
744 } 912 }
745 913
746 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) { 914 if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
747 ShowInlineKeyboard(); 915 ShowInlineKeyboardNew();
748 return; 916 return;
749 } 917 }
750 918
751 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) { 919 if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
752 HideInlineKeyboard(); 920 HideInlineKeyboard();
753 return; 921 return;
754 } 922 }
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.h b/src/core/hle/service/am/applets/applet_software_keyboard.h
index a0fddd965..7ee4c5eea 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.h
@@ -13,6 +13,11 @@ namespace Core {
13class System; 13class System;
14} 14}
15 15
16namespace Core::Frontend {
17struct KeyboardInitializeParameters;
18struct InlineAppearParameters;
19} // namespace Core::Frontend
20
16namespace Service::AM::Applets { 21namespace Service::AM::Applets {
17 22
18class SoftwareKeyboard final : public Applet { 23class SoftwareKeyboard final : public Applet {
@@ -78,13 +83,22 @@ private:
78 void ChangeState(SwkbdState state); 83 void ChangeState(SwkbdState state);
79 84
80 /** 85 /**
81 * Signals the frontend to initialize the software keyboard with common parameters. 86 * Signals the frontend to initialize the normal software keyboard with common parameters.
82 * This initializes either the normal software keyboard or the inline software keyboard
83 * depending on the state of is_background.
84 * Note that this does not cause the keyboard to appear. 87 * Note that this does not cause the keyboard to appear.
85 * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear. 88 * Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
86 */ 89 */
87 void InitializeFrontendKeyboard(); 90 void InitializeFrontendNormalKeyboard();
91
92 /**
93 * Signals the frontend to initialize the inline software keyboard with common parameters.
94 * Note that this does not cause the keyboard to appear.
95 * Use the ShowInlineKeyboard() to cause the keyboard to appear.
96 */
97 void InitializeFrontendInlineKeyboard(
98 Core::Frontend::KeyboardInitializeParameters initialize_parameters);
99
100 void InitializeFrontendInlineKeyboardOld();
101 void InitializeFrontendInlineKeyboardNew();
88 102
89 /// Signals the frontend to show the normal software keyboard. 103 /// Signals the frontend to show the normal software keyboard.
90 void ShowNormalKeyboard(); 104 void ShowNormalKeyboard();
@@ -94,7 +108,10 @@ private:
94 std::u16string text_check_message); 108 std::u16string text_check_message);
95 109
96 /// Signals the frontend to show the inline software keyboard. 110 /// Signals the frontend to show the inline software keyboard.
97 void ShowInlineKeyboard(); 111 void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
112
113 void ShowInlineKeyboardOld();
114 void ShowInlineKeyboardNew();
98 115
99 /// Signals the frontend to hide the inline software keyboard. 116 /// Signals the frontend to hide the inline software keyboard.
100 void HideInlineKeyboard(); 117 void HideInlineKeyboard();
@@ -111,6 +128,8 @@ private:
111 void RequestSetUserWordInfo(const std::vector<u8>& request_data); 128 void RequestSetUserWordInfo(const std::vector<u8>& request_data);
112 void RequestSetCustomizeDic(const std::vector<u8>& request_data); 129 void RequestSetCustomizeDic(const std::vector<u8>& request_data);
113 void RequestCalc(const std::vector<u8>& request_data); 130 void RequestCalc(const std::vector<u8>& request_data);
131 void RequestCalcOld();
132 void RequestCalcNew();
114 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data); 133 void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
115 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data); 134 void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
116 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data); 135 void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
@@ -149,7 +168,9 @@ private:
149 168
150 SwkbdState swkbd_state{SwkbdState::NotInitialized}; 169 SwkbdState swkbd_state{SwkbdState::NotInitialized};
151 SwkbdInitializeArg swkbd_initialize_arg; 170 SwkbdInitializeArg swkbd_initialize_arg;
152 SwkbdCalcArg swkbd_calc_arg; 171 SwkbdCalcArgCommon swkbd_calc_arg_common;
172 SwkbdCalcArgOld swkbd_calc_arg_old;
173 SwkbdCalcArgNew swkbd_calc_arg_new;
153 bool use_changed_string_v2{false}; 174 bool use_changed_string_v2{false};
154 bool use_moved_cursor_v2{false}; 175 bool use_moved_cursor_v2{false};
155 bool inline_use_utf8{false}; 176 bool inline_use_utf8{false};
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard_types.h b/src/core/hle/service/am/applets/applet_software_keyboard_types.h
index 21aa8e800..de1ca7aa5 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard_types.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard_types.h
@@ -10,6 +10,7 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "common/uuid.h"
13 14
14namespace Service::AM::Applets { 15namespace Service::AM::Applets {
15 16
@@ -216,7 +217,7 @@ struct SwkbdInitializeArg {
216}; 217};
217static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size."); 218static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
218 219
219struct SwkbdAppearArg { 220struct SwkbdAppearArgOld {
220 SwkbdType type{}; 221 SwkbdType type{};
221 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{}; 222 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
222 char16_t left_optional_symbol_key{}; 223 char16_t left_optional_symbol_key{};
@@ -229,19 +230,76 @@ struct SwkbdAppearArg {
229 bool enable_return_button{}; 230 bool enable_return_button{};
230 INSERT_PADDING_BYTES(3); 231 INSERT_PADDING_BYTES(3);
231 u32 flags{}; 232 u32 flags{};
232 INSERT_PADDING_WORDS(6); 233 bool is_use_save_data{};
234 INSERT_PADDING_BYTES(7);
235 Common::UUID user_id{};
233}; 236};
234static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size."); 237static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
235 238
236struct SwkbdCalcArg { 239struct SwkbdAppearArgNew {
240 SwkbdType type{};
241 std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
242 char16_t left_optional_symbol_key{};
243 char16_t right_optional_symbol_key{};
244 bool use_prediction{};
245 bool disable_cancel_button{};
246 SwkbdKeyDisableFlags key_disable_flags{};
247 u32 max_text_length{};
248 u32 min_text_length{};
249 bool enable_return_button{};
250 INSERT_PADDING_BYTES(3);
251 u32 flags{};
252 bool is_use_save_data{};
253 INSERT_PADDING_BYTES(7);
254 Common::UUID user_id{};
255 u64 start_sampling_number{};
256 INSERT_PADDING_WORDS(8);
257};
258static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
259
260struct SwkbdCalcArgCommon {
237 u32 unknown{}; 261 u32 unknown{};
238 u16 calc_arg_size{}; 262 u16 calc_arg_size{};
239 INSERT_PADDING_BYTES(2); 263 INSERT_PADDING_BYTES(2);
240 SwkbdCalcArgFlags flags{}; 264 SwkbdCalcArgFlags flags{};
241 SwkbdInitializeArg initialize_arg{}; 265 SwkbdInitializeArg initialize_arg{};
266};
267static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
268
269struct SwkbdCalcArgOld {
270 f32 volume{};
271 s32 cursor_position{};
272 SwkbdAppearArgOld appear_arg{};
273 std::array<char16_t, 0x1FA> input_text{};
274 bool utf8_mode{};
275 INSERT_PADDING_BYTES(1);
276 bool enable_backspace_button{};
277 INSERT_PADDING_BYTES(3);
278 bool key_top_as_floating{};
279 bool footer_scalable{};
280 bool alpha_enabled_in_input_mode{};
281 u8 input_mode_fade_type{};
282 bool disable_touch{};
283 bool disable_hardware_keyboard{};
284 INSERT_PADDING_BYTES(8);
285 f32 key_top_scale_x{};
286 f32 key_top_scale_y{};
287 f32 key_top_translate_x{};
288 f32 key_top_translate_y{};
289 f32 key_top_bg_alpha{};
290 f32 footer_bg_alpha{};
291 f32 balloon_scale{};
292 INSERT_PADDING_WORDS(4);
293 u8 se_group{};
294 INSERT_PADDING_BYTES(3);
295};
296static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
297 "SwkbdCalcArgOld has incorrect size.");
298
299struct SwkbdCalcArgNew {
300 SwkbdAppearArgNew appear_arg{};
242 f32 volume{}; 301 f32 volume{};
243 s32 cursor_position{}; 302 s32 cursor_position{};
244 SwkbdAppearArg appear_arg{};
245 std::array<char16_t, 0x1FA> input_text{}; 303 std::array<char16_t, 0x1FA> input_text{};
246 bool utf8_mode{}; 304 bool utf8_mode{};
247 INSERT_PADDING_BYTES(1); 305 INSERT_PADDING_BYTES(1);
@@ -264,8 +322,10 @@ struct SwkbdCalcArg {
264 INSERT_PADDING_WORDS(4); 322 INSERT_PADDING_WORDS(4);
265 u8 se_group{}; 323 u8 se_group{};
266 INSERT_PADDING_BYTES(3); 324 INSERT_PADDING_BYTES(3);
325 INSERT_PADDING_WORDS(8);
267}; 326};
268static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size."); 327static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
328 "SwkbdCalcArgNew has incorrect size.");
269 329
270struct SwkbdChangedStringArg { 330struct SwkbdChangedStringArg {
271 u32 text_length{}; 331 u32 text_length{};
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index bb5cb61be..a4b3fb187 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -446,6 +446,14 @@ void WebBrowser::ExecuteLogin() {
446} 446}
447 447
448void WebBrowser::ExecuteOffline() { 448void WebBrowser::ExecuteOffline() {
449 // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
450 // Super Mario 3D All-Stars.
451 // TODO (Morph): Implement WebSession.
452 if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
453 LOG_WARNING(Service_AM, "WebSession is not implemented");
454 return;
455 }
456
449 const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); 457 const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
450 458
451 if (!Common::FS::Exists(main_url)) { 459 if (!Common::FS::Exists(main_url)) {
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 134ac1ee2..1f4c9786a 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -9,6 +9,7 @@
9#include "core/frontend/applets/controller.h" 9#include "core/frontend/applets/controller.h"
10#include "core/frontend/applets/error.h" 10#include "core/frontend/applets/error.h"
11#include "core/frontend/applets/general_frontend.h" 11#include "core/frontend/applets/general_frontend.h"
12#include "core/frontend/applets/mii_edit.h"
12#include "core/frontend/applets/profile_select.h" 13#include "core/frontend/applets/profile_select.h"
13#include "core/frontend/applets/software_keyboard.h" 14#include "core/frontend/applets/software_keyboard.h"
14#include "core/frontend/applets/web_browser.h" 15#include "core/frontend/applets/web_browser.h"
@@ -19,6 +20,7 @@
19#include "core/hle/service/am/applets/applet_controller.h" 20#include "core/hle/service/am/applets/applet_controller.h"
20#include "core/hle/service/am/applets/applet_error.h" 21#include "core/hle/service/am/applets/applet_error.h"
21#include "core/hle/service/am/applets/applet_general_backend.h" 22#include "core/hle/service/am/applets/applet_general_backend.h"
23#include "core/hle/service/am/applets/applet_mii_edit.h"
22#include "core/hle/service/am/applets/applet_profile_select.h" 24#include "core/hle/service/am/applets/applet_profile_select.h"
23#include "core/hle/service/am/applets/applet_software_keyboard.h" 25#include "core/hle/service/am/applets/applet_software_keyboard.h"
24#include "core/hle/service/am/applets/applet_web_browser.h" 26#include "core/hle/service/am/applets/applet_web_browser.h"
@@ -171,11 +173,12 @@ void Applet::Initialize() {
171AppletFrontendSet::AppletFrontendSet() = default; 173AppletFrontendSet::AppletFrontendSet() = default;
172 174
173AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 175AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
176 MiiEdit mii_edit_,
174 ParentalControlsApplet parental_controls_applet, 177 ParentalControlsApplet parental_controls_applet,
175 PhotoViewer photo_viewer_, ProfileSelect profile_select_, 178 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
176 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_) 179 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
177 : controller{std::move(controller_applet)}, error{std::move(error_applet)}, 180 : controller{std::move(controller_applet)}, error{std::move(error_applet)},
178 parental_controls{std::move(parental_controls_applet)}, 181 mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
179 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)}, 182 photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
180 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {} 183 software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
181 184
@@ -202,6 +205,10 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
202 frontend.error = std::move(set.error); 205 frontend.error = std::move(set.error);
203 } 206 }
204 207
208 if (set.mii_edit != nullptr) {
209 frontend.mii_edit = std::move(set.mii_edit);
210 }
211
205 if (set.parental_controls != nullptr) { 212 if (set.parental_controls != nullptr) {
206 frontend.parental_controls = std::move(set.parental_controls); 213 frontend.parental_controls = std::move(set.parental_controls);
207 } 214 }
@@ -238,6 +245,10 @@ void AppletManager::SetDefaultAppletsIfMissing() {
238 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 245 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
239 } 246 }
240 247
248 if (frontend.mii_edit == nullptr) {
249 frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
250 }
251
241 if (frontend.parental_controls == nullptr) { 252 if (frontend.parental_controls == nullptr) {
242 frontend.parental_controls = 253 frontend.parental_controls =
243 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); 254 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
@@ -277,6 +288,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
277 return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select); 288 return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select);
278 case AppletId::SoftwareKeyboard: 289 case AppletId::SoftwareKeyboard:
279 return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard); 290 return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
291 case AppletId::MiiEdit:
292 return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit);
280 case AppletId::Web: 293 case AppletId::Web:
281 case AppletId::Shop: 294 case AppletId::Shop:
282 case AppletId::OfflineWeb: 295 case AppletId::OfflineWeb:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 15eeb4ee1..50a7bdceb 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -20,6 +20,7 @@ namespace Core::Frontend {
20class ControllerApplet; 20class ControllerApplet;
21class ECommerceApplet; 21class ECommerceApplet;
22class ErrorApplet; 22class ErrorApplet;
23class MiiEditApplet;
23class ParentalControlsApplet; 24class ParentalControlsApplet;
24class PhotoViewerApplet; 25class PhotoViewerApplet;
25class ProfileSelectApplet; 26class ProfileSelectApplet;
@@ -178,6 +179,7 @@ protected:
178struct AppletFrontendSet { 179struct AppletFrontendSet {
179 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; 180 using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
180 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 181 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
182 using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
181 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; 183 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
182 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 184 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
183 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; 185 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
@@ -186,9 +188,9 @@ struct AppletFrontendSet {
186 188
187 AppletFrontendSet(); 189 AppletFrontendSet();
188 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet, 190 AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
189 ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_, 191 MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
190 ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_, 192 PhotoViewer photo_viewer_, ProfileSelect profile_select_,
191 WebBrowser web_browser_); 193 SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
192 ~AppletFrontendSet(); 194 ~AppletFrontendSet();
193 195
194 AppletFrontendSet(const AppletFrontendSet&) = delete; 196 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -199,6 +201,7 @@ struct AppletFrontendSet {
199 201
200 ControllerApplet controller; 202 ControllerApplet controller;
201 ErrorApplet error; 203 ErrorApplet error;
204 MiiEdit mii_edit;
202 ParentalControlsApplet parental_controls; 205 ParentalControlsApplet parental_controls;
203 PhotoViewer photo_viewer; 206 PhotoViewer photo_viewer;
204 ProfileSelect profile_select; 207 ProfileSelect profile_select;
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index affa7971c..a72956a28 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -41,9 +41,10 @@ public:
41 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_, 41 explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
42 AudioCore::AudioOut& audio_core_, std::string&& device_name_, 42 AudioCore::AudioOut& audio_core_, std::string&& device_name_,
43 std::string&& unique_name) 43 std::string&& unique_name)
44 : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_}, 44 : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
45 device_name{std::move(device_name_)}, audio_params{audio_params_}, 45 audio_core{audio_core_}, device_name{std::move(device_name_)},
46 main_memory{system.Memory()}, service_context{system_, "IAudioOut"} { 46 audio_params{audio_params_}, main_memory{system.Memory()}, service_context{system_,
47 "IAudioOut"} {
47 // clang-format off 48 // clang-format off
48 static const FunctionInfo functions[] = { 49 static const FunctionInfo functions[] = {
49 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, 50 {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index f45e5cecc..d4ffeb21d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -24,7 +24,8 @@ public:
24 explicit IAudioRenderer(Core::System& system_, 24 explicit IAudioRenderer(Core::System& system_,
25 const AudioCommon::AudioRendererParameter& audren_params, 25 const AudioCommon::AudioRendererParameter& audren_params,
26 const std::size_t instance_number) 26 const std::size_t instance_number)
27 : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} { 27 : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
28 service_context{system_, "IAudioRenderer"} {
28 // clang-format off 29 // clang-format off
29 static const FunctionInfo functions[] = { 30 static const FunctionInfo functions[] = {
30 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, 31 {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3703ca4c6..4208337db 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -174,7 +174,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
174 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); 174 ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
175 175
176 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), 176 ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
177 "Could not write all of the bytes but everything else has succeded."); 177 "Could not write all of the bytes but everything else has succeeded.");
178 178
179 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { 179 if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
180 // TODO(DarkLordZach): Find a better error code for this 180 // TODO(DarkLordZach): Find a better error code for this
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index b087e7bba..c07929ab8 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -58,7 +58,8 @@ enum class FileSystemType : u8 {
58class IStorage final : public ServiceFramework<IStorage> { 58class IStorage final : public ServiceFramework<IStorage> {
59public: 59public:
60 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_) 60 explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
61 : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) { 61 : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew},
62 backend(std::move(backend_)) {
62 static const FunctionInfo functions[] = { 63 static const FunctionInfo functions[] = {
63 {0, &IStorage::Read, "Read"}, 64 {0, &IStorage::Read, "Read"},
64 {1, nullptr, "Write"}, 65 {1, nullptr, "Write"},
@@ -116,7 +117,8 @@ private:
116class IFile final : public ServiceFramework<IFile> { 117class IFile final : public ServiceFramework<IFile> {
117public: 118public:
118 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_) 119 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
119 : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { 120 : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew},
121 backend(std::move(backend_)) {
120 static const FunctionInfo functions[] = { 122 static const FunctionInfo functions[] = {
121 {0, &IFile::Read, "Read"}, 123 {0, &IFile::Read, "Read"},
122 {1, &IFile::Write, "Write"}, 124 {1, &IFile::Write, "Write"},
@@ -252,7 +254,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
252class IDirectory final : public ServiceFramework<IDirectory> { 254class IDirectory final : public ServiceFramework<IDirectory> {
253public: 255public:
254 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_) 256 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
255 : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { 257 : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew},
258 backend(std::move(backend_)) {
256 static const FunctionInfo functions[] = { 259 static const FunctionInfo functions[] = {
257 {0, &IDirectory::Read, "Read"}, 260 {0, &IDirectory::Read, "Read"},
258 {1, &IDirectory::GetEntryCount, "GetEntryCount"}, 261 {1, &IDirectory::GetEntryCount, "GetEntryCount"},
@@ -308,8 +311,8 @@ private:
308class IFileSystem final : public ServiceFramework<IFileSystem> { 311class IFileSystem final : public ServiceFramework<IFileSystem> {
309public: 312public:
310 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) 313 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
311 : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( 314 : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew},
312 size_)} { 315 backend{std::move(backend_)}, size{std::move(size_)} {
313 static const FunctionInfo functions[] = { 316 static const FunctionInfo functions[] = {
314 {0, &IFileSystem::CreateFile, "CreateFile"}, 317 {0, &IFileSystem::CreateFile, "CreateFile"},
315 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 318 {1, &IFileSystem::DeleteFile, "DeleteFile"},
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
index a727b3582..8450eaf93 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
@@ -2,7 +2,6 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/settings.h"
6#include "core/core_timing.h" 5#include "core/core_timing.h"
7#include "core/hid/emulated_console.h" 6#include "core/hid/emulated_console.h"
8#include "core/hid/hid_core.h" 7#include "core/hid/hid_core.h"
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h
index 26d153f0c..93454585a 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.h
@@ -8,7 +8,6 @@
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/quaternion.h" 10#include "common/quaternion.h"
11#include "core/hid/hid_types.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 11#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h" 12#include "core/hle/service/hid/ring_lifo.h"
14 13
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 7450eb20a..5e464149c 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/swap.h"
9 8
10namespace Core::Timing { 9namespace Core::Timing {
11class CoreTiming; 10class CoreTiming;
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index afe374fc2..4396780a1 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -4,11 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "common/bit_field.h" 7#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h" 8#include "common/common_types.h"
11#include "common/swap.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 9#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h" 10#include "core/hle/service/hid/ring_lifo.h"
14 11
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index cf62d3896..b869352b9 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -4,11 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "common/bit_field.h"
9#include "common/common_funcs.h"
10#include "common/common_types.h" 7#include "common/common_types.h"
11#include "common/swap.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 8#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h" 9#include "core/hle/service/hid/ring_lifo.h"
14 10
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 7559fc78d..8858887b2 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -4,10 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include "common/bit_field.h"
9#include "common/common_types.h" 7#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hle/service/hid/controllers/controller_base.h" 8#include "core/hle/service/hid/controllers/controller_base.h"
12#include "core/hle/service/hid/ring_lifo.h" 9#include "core/hle/service/hid/ring_lifo.h"
13 10
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e5c951e06..4e17a952e 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -262,11 +262,6 @@ void Controller_NPad::OnInit() {
262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); 262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
263 } 263 }
264 264
265 if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
266 // We want to support all controllers
267 hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
268 }
269
270 supported_npad_id_types.resize(npad_id_list.size()); 265 supported_npad_id_types.resize(npad_id_list.size());
271 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 266 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
272 npad_id_list.size() * sizeof(Core::HID::NpadIdType)); 267 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
@@ -288,14 +283,6 @@ void Controller_NPad::OnInit() {
288 WriteEmptyEntry(npad); 283 WriteEmptyEntry(npad);
289 } 284 }
290 } 285 }
291
292 // Connect controllers
293 for (auto& controller : controller_data) {
294 const auto& device = controller.device;
295 if (device->IsConnected()) {
296 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
297 }
298 }
299} 286}
300 287
301void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { 288void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
@@ -320,6 +307,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
320} 307}
321 308
322void Controller_NPad::OnRelease() { 309void Controller_NPad::OnRelease() {
310 is_controller_initialized = false;
323 for (std::size_t i = 0; i < controller_data.size(); ++i) { 311 for (std::size_t i = 0; i < controller_data.size(); ++i) {
324 auto& controller = controller_data[i]; 312 auto& controller = controller_data[i];
325 service_context.CloseEvent(controller.styleset_changed_event); 313 service_context.CloseEvent(controller.styleset_changed_event);
@@ -330,7 +318,7 @@ void Controller_NPad::OnRelease() {
330} 318}
331 319
332void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { 320void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
333 std::lock_guard lock{mutex}; 321 std::scoped_lock lock{mutex};
334 auto& controller = GetControllerFromNpadIdType(npad_id); 322 auto& controller = GetControllerFromNpadIdType(npad_id);
335 const auto controller_type = controller.device->GetNpadStyleIndex(); 323 const auto controller_type = controller.device->GetNpadStyleIndex();
336 if (!controller.device->IsConnected()) { 324 if (!controller.device->IsConnected()) {
@@ -651,9 +639,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
651 639
652void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { 640void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
653 hid_core.SetSupportedStyleTag(style_set); 641 hid_core.SetSupportedStyleTag(style_set);
642
643 if (is_controller_initialized) {
644 return;
645 }
646
647 // Once SetSupportedStyleSet is called controllers are fully initialized
648 is_controller_initialized = true;
649
650 // Connect all active controllers
651 for (auto& controller : controller_data) {
652 const auto& device = controller.device;
653 if (device->IsConnected()) {
654 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
655 }
656 }
654} 657}
655 658
656Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 659Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
660 if (!is_controller_initialized) {
661 return {Core::HID::NpadStyleSet::None};
662 }
657 return hid_core.GetSupportedStyleTag(); 663 return hid_core.GetSupportedStyleTag();
658} 664}
659 665
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 6b2872bad..967379f05 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -10,7 +10,8 @@
10 10
11#include "common/bit_field.h" 11#include "common/bit_field.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/quaternion.h" 13#include "common/vector_math.h"
14
14#include "core/hid/hid_types.h" 15#include "core/hid/hid_types.h"
15#include "core/hle/service/hid/controllers/controller_base.h" 16#include "core/hle/service/hid/controllers/controller_base.h"
16#include "core/hle/service/hid/ring_lifo.h" 17#include "core/hle/service/hid/ring_lifo.h"
@@ -190,16 +191,16 @@ private:
190 191
191 // This is nn::hid::detail::NpadFullKeyColorState 192 // This is nn::hid::detail::NpadFullKeyColorState
192 struct NpadFullKeyColorState { 193 struct NpadFullKeyColorState {
193 ColorAttribute attribute; 194 ColorAttribute attribute{ColorAttribute::NoController};
194 Core::HID::NpadControllerColor fullkey; 195 Core::HID::NpadControllerColor fullkey{};
195 }; 196 };
196 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); 197 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
197 198
198 // This is nn::hid::detail::NpadJoyColorState 199 // This is nn::hid::detail::NpadJoyColorState
199 struct NpadJoyColorState { 200 struct NpadJoyColorState {
200 ColorAttribute attribute; 201 ColorAttribute attribute{ColorAttribute::NoController};
201 Core::HID::NpadControllerColor left; 202 Core::HID::NpadControllerColor left{};
202 Core::HID::NpadControllerColor right; 203 Core::HID::NpadControllerColor right{};
203 }; 204 };
204 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); 205 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
205 206
@@ -225,11 +226,11 @@ private:
225 // This is nn::hid::NpadPalmaState 226 // This is nn::hid::NpadPalmaState
226 // This is nn::hid::NpadSystemExtState 227 // This is nn::hid::NpadSystemExtState
227 struct NPadGenericState { 228 struct NPadGenericState {
228 s64_le sampling_number; 229 s64_le sampling_number{};
229 Core::HID::NpadButtonState npad_buttons; 230 Core::HID::NpadButtonState npad_buttons{};
230 Core::HID::AnalogStickState l_stick; 231 Core::HID::AnalogStickState l_stick{};
231 Core::HID::AnalogStickState r_stick; 232 Core::HID::AnalogStickState r_stick{};
232 NpadAttribute connection_status; 233 NpadAttribute connection_status{};
233 INSERT_PADDING_BYTES(4); // Reserved 234 INSERT_PADDING_BYTES(4); // Reserved
234 }; 235 };
235 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 236 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -252,7 +253,7 @@ private:
252 Common::Vec3f gyro{}; 253 Common::Vec3f gyro{};
253 Common::Vec3f rotation{}; 254 Common::Vec3f rotation{};
254 std::array<Common::Vec3f, 3> orientation{}; 255 std::array<Common::Vec3f, 3> orientation{};
255 SixAxisSensorAttribute attribute; 256 SixAxisSensorAttribute attribute{};
256 INSERT_PADDING_BYTES(4); // Reserved 257 INSERT_PADDING_BYTES(4); // Reserved
257 }; 258 };
258 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); 259 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -324,11 +325,11 @@ private:
324 325
325 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl 326 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
326 struct NfcXcdDeviceHandleStateImpl { 327 struct NfcXcdDeviceHandleStateImpl {
327 u64 handle; 328 u64 handle{};
328 bool is_available; 329 bool is_available{};
329 bool is_activated; 330 bool is_activated{};
330 INSERT_PADDING_BYTES(0x6); // Reserved 331 INSERT_PADDING_BYTES(0x6); // Reserved
331 u64 sampling_number; 332 u64 sampling_number{};
332 }; 333 };
333 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 334 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
334 "NfcXcdDeviceHandleStateImpl is an invalid size"); 335 "NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -365,8 +366,8 @@ private:
365 }; 366 };
366 367
367 struct AppletFooterUi { 368 struct AppletFooterUi {
368 AppletFooterUiAttributes attributes; 369 AppletFooterUiAttributes attributes{};
369 AppletFooterUiType type; 370 AppletFooterUiType type{AppletFooterUiType::None};
370 INSERT_PADDING_BYTES(0x5B); // Reserved 371 INSERT_PADDING_BYTES(0x5B); // Reserved
371 }; 372 };
372 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); 373 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -403,41 +404,41 @@ private:
403 404
404 // This is nn::hid::detail::NpadInternalState 405 // This is nn::hid::detail::NpadInternalState
405 struct NpadInternalState { 406 struct NpadInternalState {
406 Core::HID::NpadStyleTag style_tag; 407 Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
407 NpadJoyAssignmentMode assignment_mode; 408 NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
408 NpadFullKeyColorState fullkey_color; 409 NpadFullKeyColorState fullkey_color{};
409 NpadJoyColorState joycon_color; 410 NpadJoyColorState joycon_color{};
410 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; 411 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
411 Lifo<NPadGenericState, hid_entry_count> handheld_lifo; 412 Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
412 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; 413 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
413 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; 414 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
414 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; 415 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
415 Lifo<NPadGenericState, hid_entry_count> palma_lifo; 416 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
416 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; 417 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
417 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; 418 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
418 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; 419 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
419 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; 420 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
420 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; 421 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
421 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; 422 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
422 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; 423 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
423 DeviceType device_type; 424 DeviceType device_type{};
424 INSERT_PADDING_BYTES(0x4); // Reserved 425 INSERT_PADDING_BYTES(0x4); // Reserved
425 NPadSystemProperties system_properties; 426 NPadSystemProperties system_properties{};
426 NpadSystemButtonProperties button_properties; 427 NpadSystemButtonProperties button_properties{};
427 Core::HID::NpadBatteryLevel battery_level_dual; 428 Core::HID::NpadBatteryLevel battery_level_dual{};
428 Core::HID::NpadBatteryLevel battery_level_left; 429 Core::HID::NpadBatteryLevel battery_level_left{};
429 Core::HID::NpadBatteryLevel battery_level_right; 430 Core::HID::NpadBatteryLevel battery_level_right{};
430 union { 431 union {
431 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; 432 AppletFooterUi applet_footer{};
432 AppletFooterUi applet_footer; 433 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
433 }; 434 };
434 INSERT_PADDING_BYTES(0x20); // Unknown 435 INSERT_PADDING_BYTES(0x20); // Unknown
435 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; 436 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
436 NpadLarkType lark_type_l_and_main; 437 NpadLarkType lark_type_l_and_main{};
437 NpadLarkType lark_type_r; 438 NpadLarkType lark_type_r{};
438 NpadLuciaType lucia_type; 439 NpadLuciaType lucia_type{};
439 NpadLagonType lagon_type; 440 NpadLagonType lagon_type{};
440 NpadLagerType lager_type; 441 NpadLagerType lager_type{};
441 // FW 13.x Investigate there is some sort of bitflag related to joycons 442 // FW 13.x Investigate there is some sort of bitflag related to joycons
442 INSERT_PADDING_BYTES(0x4); 443 INSERT_PADDING_BYTES(0x4);
443 INSERT_PADDING_BYTES(0xc08); // Unknown 444 INSERT_PADDING_BYTES(0xc08); // Unknown
@@ -510,7 +511,8 @@ private:
510 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 511 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
511 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 512 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
512 bool permit_vibration_session_enabled{false}; 513 bool permit_vibration_session_enabled{false};
513 bool analog_stick_use_center_clamp{}; 514 bool analog_stick_use_center_clamp{false};
514 bool is_in_lr_assignment_mode{false}; 515 bool is_in_lr_assignment_mode{false};
516 bool is_controller_initialized{false};
515}; 517};
516} // namespace Service::HID 518} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 48978e5c6..65b799e78 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -5,7 +5,6 @@
5#include <algorithm> 5#include <algorithm>
6#include <cstring> 6#include <cstring>
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "common/logging/log.h"
9#include "common/settings.h" 8#include "common/settings.h"
10#include "core/core.h" 9#include "core/core.h"
11#include "core/core_timing.h" 10#include "core/core_timing.h"
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 708dde4f0..483552767 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -4,11 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h"
8#include "common/common_funcs.h" 7#include "common/common_funcs.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/point.h"
11#include "common/swap.h"
12#include "core/hid/hid_types.h" 9#include "core/hid/hid_types.h"
13#include "core/hle/service/hid/controllers/controller_base.h" 10#include "core/hle/service/hid/controllers/controller_base.h"
14#include "core/hle/service/hid/ring_lifo.h" 11#include "core/hle/service/hid/ring_lifo.h"
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index ba8db8d9d..4973e1bd9 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -5,9 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include "common/bit_field.h" 7#include "common/bit_field.h"
8#include "common/common_funcs.h"
9#include "common/common_types.h" 8#include "common/common_types.h"
10#include "common/swap.h"
11#include "core/hid/hid_types.h" 9#include "core/hid/hid_types.h"
12#include "core/hle/service/hid/controllers/controller_base.h" 10#include "core/hle/service/hid/controllers/controller_base.h"
13#include "core/hle/service/hid/ring_lifo.h" 11#include "core/hle/service/hid/ring_lifo.h"
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d9202ea6c..b2cec2253 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -878,6 +878,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
878 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", 878 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
879 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); 879 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
880 880
881 // Games expect this event to be signaled after calling this function
882 applet_resource->GetController<Controller_NPad>(HidController::NPad)
883 .SignalStyleSetChangedEvent(parameters.npad_id);
884
881 IPC::ResponseBuilder rb{ctx, 2, 1}; 885 IPC::ResponseBuilder rb{ctx, 2, 1};
882 rb.Push(ResultSuccess); 886 rb.Push(ResultSuccess);
883 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 887 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
new file mode 100644
index 000000000..0f9e33ef6
--- /dev/null
+++ b/src/core/hle/service/jit/jit.cpp
@@ -0,0 +1,332 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/arm/symbols.h"
6#include "core/core.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/kernel/k_code_memory.h"
9#include "core/hle/kernel/k_transfer_memory.h"
10#include "core/hle/result.h"
11#include "core/hle/service/jit/jit.h"
12#include "core/hle/service/jit/jit_context.h"
13#include "core/hle/service/service.h"
14#include "core/memory.h"
15
16namespace Service::JIT {
17
18struct CodeRange {
19 u64 offset;
20 u64 size;
21};
22
23class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
24public:
25 explicit IJitEnvironment(Core::System& system_, CodeRange user_rx, CodeRange user_ro)
26 : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew},
27 context{system_.Memory()} {
28 // clang-format off
29 static const FunctionInfo functions[] = {
30 {0, &IJitEnvironment::GenerateCode, "GenerateCode"},
31 {1, &IJitEnvironment::Control, "Control"},
32 {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
33 {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
34 };
35 // clang-format on
36
37 RegisterHandlers(functions);
38
39 // Identity map user code range into sysmodule context
40 configuration.user_ro_memory = user_ro;
41 configuration.user_rx_memory = user_rx;
42 configuration.sys_ro_memory = user_ro;
43 configuration.sys_rx_memory = user_rx;
44 }
45
46 void GenerateCode(Kernel::HLERequestContext& ctx) {
47 struct Parameters {
48 u32 data_size;
49 u64 command;
50 CodeRange cr1;
51 CodeRange cr2;
52 Struct32 data;
53 };
54
55 IPC::RequestParser rp{ctx};
56 const auto parameters{rp.PopRaw<Parameters>()};
57 std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
58 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
59
60 const VAddr return_ptr{context.AddHeap(0u)};
61 const VAddr cr1_in_ptr{context.AddHeap(parameters.cr1)};
62 const VAddr cr2_in_ptr{context.AddHeap(parameters.cr2)};
63 const VAddr cr1_out_ptr{
64 context.AddHeap(CodeRange{.offset = parameters.cr1.offset, .size = 0})};
65 const VAddr cr2_out_ptr{
66 context.AddHeap(CodeRange{.offset = parameters.cr2.offset, .size = 0})};
67 const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
68 const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
69 const VAddr data_ptr{context.AddHeap(parameters.data)};
70 const VAddr configuration_ptr{context.AddHeap(configuration)};
71
72 context.CallFunction(callbacks.GenerateCode, return_ptr, cr1_out_ptr, cr2_out_ptr,
73 configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
74 cr1_in_ptr, cr2_in_ptr, data_ptr, parameters.data_size, output_ptr,
75 output_buffer.size());
76
77 const s32 return_value{context.GetHeap<s32>(return_ptr)};
78
79 if (return_value == 0) {
80 system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset,
81 configuration.user_rx_memory.size);
82
83 if (ctx.CanWriteBuffer()) {
84 context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
85 ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
86 }
87 const auto cr1_out{context.GetHeap<CodeRange>(cr1_out_ptr)};
88 const auto cr2_out{context.GetHeap<CodeRange>(cr2_out_ptr)};
89
90 IPC::ResponseBuilder rb{ctx, 8};
91 rb.Push(ResultSuccess);
92 rb.Push<u64>(return_value);
93 rb.PushRaw(cr1_out);
94 rb.PushRaw(cr2_out);
95 } else {
96 LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
97 IPC::ResponseBuilder rb{ctx, 2};
98 rb.Push(ResultUnknown);
99 }
100 };
101
102 void Control(Kernel::HLERequestContext& ctx) {
103 IPC::RequestParser rp{ctx};
104 const auto command{rp.PopRaw<u64>()};
105 const auto input_buffer{ctx.ReadBuffer()};
106 std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
107
108 const VAddr return_ptr{context.AddHeap(0u)};
109 const VAddr configuration_ptr{context.AddHeap(configuration)};
110 const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
111 const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
112 const u64 wrapper_value{
113 context.CallFunction(callbacks.Control, return_ptr, configuration_ptr, command,
114 input_ptr, input_buffer.size(), output_ptr, output_buffer.size())};
115 const s32 return_value{context.GetHeap<s32>(return_ptr)};
116
117 if (wrapper_value == 0 && return_value == 0) {
118 if (ctx.CanWriteBuffer()) {
119 context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
120 ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
121 }
122 IPC::ResponseBuilder rb{ctx, 3};
123 rb.Push(ResultSuccess);
124 rb.Push(return_value);
125 } else {
126 LOG_WARNING(Service_JIT, "plugin Control callback failed");
127 IPC::ResponseBuilder rb{ctx, 2};
128 rb.Push(ResultUnknown);
129 }
130 }
131
132 void LoadPlugin(Kernel::HLERequestContext& ctx) {
133 IPC::RequestParser rp{ctx};
134 const auto tmem_size{rp.PopRaw<u64>()};
135 if (tmem_size == 0) {
136 LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
137 IPC::ResponseBuilder rb{ctx, 2};
138 rb.Push(ResultUnknown);
139 return;
140 }
141
142 const auto tmem_handle{ctx.GetCopyHandle(0)};
143 auto tmem{system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(
144 tmem_handle)};
145 if (tmem.IsNull()) {
146 LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
147 IPC::ResponseBuilder rb{ctx, 2};
148 rb.Push(ResultUnknown);
149 return;
150 }
151
152 configuration.work_memory.offset = tmem->GetSourceAddress();
153 configuration.work_memory.size = tmem_size;
154
155 const auto nro_plugin{ctx.ReadBuffer(1)};
156 auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
157 const auto GetSymbol{[&](std::string name) { return symbols[name].first; }};
158
159 callbacks =
160 GuestCallbacks{.rtld_fini = GetSymbol("_fini"),
161 .rtld_init = GetSymbol("_init"),
162 .Control = GetSymbol("nnjitpluginControl"),
163 .ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols"),
164 .SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics"),
165 .Configure = GetSymbol("nnjitpluginConfigure"),
166 .GenerateCode = GetSymbol("nnjitpluginGenerateCode"),
167 .GetVersion = GetSymbol("nnjitpluginGetVersion"),
168 .Keeper = GetSymbol("nnjitpluginKeeper"),
169 .OnPrepared = GetSymbol("nnjitpluginOnPrepared")};
170
171 if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
172 callbacks.OnPrepared == 0) {
173 LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
174 IPC::ResponseBuilder rb{ctx, 2};
175 rb.Push(ResultUnknown);
176 return;
177 }
178
179 if (!context.LoadNRO(nro_plugin)) {
180 LOG_ERROR(Service_JIT, "failed to load plugin");
181 IPC::ResponseBuilder rb{ctx, 2};
182 rb.Push(ResultUnknown);
183 return;
184 }
185
186 context.MapProcessMemory(configuration.sys_ro_memory.offset,
187 configuration.sys_ro_memory.size);
188 context.MapProcessMemory(configuration.sys_rx_memory.offset,
189 configuration.sys_rx_memory.size);
190 context.MapProcessMemory(configuration.work_memory.offset, configuration.work_memory.size);
191
192 if (callbacks.rtld_init != 0) {
193 context.CallFunction(callbacks.rtld_init);
194 }
195
196 const auto version{context.CallFunction(callbacks.GetVersion)};
197 if (version != 1) {
198 LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
199 IPC::ResponseBuilder rb{ctx, 2};
200 rb.Push(ResultUnknown);
201 return;
202 }
203
204 const auto resolve{context.GetHelper("_resolve")};
205 if (callbacks.ResolveBasicSymbols != 0) {
206 context.CallFunction(callbacks.ResolveBasicSymbols, resolve);
207 }
208 const auto resolve_ptr{context.AddHeap(resolve)};
209 if (callbacks.SetupDiagnostics != 0) {
210 context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr);
211 }
212
213 context.CallFunction(callbacks.Configure, 0u);
214 const auto configuration_ptr{context.AddHeap(configuration)};
215 context.CallFunction(callbacks.OnPrepared, configuration_ptr);
216
217 IPC::ResponseBuilder rb{ctx, 2};
218 rb.Push(ResultSuccess);
219 }
220
221 void GetCodeAddress(Kernel::HLERequestContext& ctx) {
222 IPC::ResponseBuilder rb{ctx, 6};
223 rb.Push(ResultSuccess);
224 rb.Push(configuration.user_rx_memory.offset);
225 rb.Push(configuration.user_ro_memory.offset);
226 }
227
228private:
229 using Struct32 = std::array<u8, 32>;
230
231 struct GuestCallbacks {
232 VAddr rtld_fini;
233 VAddr rtld_init;
234 VAddr Control;
235 VAddr ResolveBasicSymbols;
236 VAddr SetupDiagnostics;
237 VAddr Configure;
238 VAddr GenerateCode;
239 VAddr GetVersion;
240 VAddr Keeper;
241 VAddr OnPrepared;
242 };
243
244 struct JITConfiguration {
245 CodeRange user_rx_memory;
246 CodeRange user_ro_memory;
247 CodeRange work_memory;
248 CodeRange sys_rx_memory;
249 CodeRange sys_ro_memory;
250 };
251
252 GuestCallbacks callbacks;
253 JITConfiguration configuration;
254 JITContext context;
255};
256
257class JITU final : public ServiceFramework<JITU> {
258public:
259 explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
260 // clang-format off
261 static const FunctionInfo functions[] = {
262 {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
263 };
264 // clang-format on
265
266 RegisterHandlers(functions);
267 }
268
269 void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
270 LOG_DEBUG(Service_JIT, "called");
271
272 struct Parameters {
273 u64 rx_size;
274 u64 ro_size;
275 };
276
277 IPC::RequestParser rp{ctx};
278 const auto parameters{rp.PopRaw<Parameters>()};
279 const auto executable_mem_handle{ctx.GetCopyHandle(1)};
280 const auto readable_mem_handle{ctx.GetCopyHandle(2)};
281
282 if (parameters.rx_size == 0 || parameters.ro_size == 0) {
283 LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
284 IPC::ResponseBuilder rb{ctx, 2};
285 rb.Push(ResultUnknown);
286 return;
287 }
288
289 // The copy handle at index 0 is the process handle, but handle tables are
290 // per-process, so there is no point reading it here until we are multiprocess
291 const auto& process{*system.CurrentProcess()};
292
293 auto executable_mem{
294 process.GetHandleTable().GetObject<Kernel::KCodeMemory>(executable_mem_handle)};
295 if (executable_mem.IsNull()) {
296 LOG_ERROR(Service_JIT, "executable_mem is null for handle=0x{:08X}",
297 executable_mem_handle);
298 IPC::ResponseBuilder rb{ctx, 2};
299 rb.Push(ResultUnknown);
300 return;
301 }
302
303 auto readable_mem{
304 process.GetHandleTable().GetObject<Kernel::KCodeMemory>(readable_mem_handle)};
305 if (readable_mem.IsNull()) {
306 LOG_ERROR(Service_JIT, "readable_mem is null for handle=0x{:08X}", readable_mem_handle);
307 IPC::ResponseBuilder rb{ctx, 2};
308 rb.Push(ResultUnknown);
309 return;
310 }
311
312 const CodeRange user_rx{
313 .offset = executable_mem->GetSourceAddress(),
314 .size = parameters.rx_size,
315 };
316
317 const CodeRange user_ro{
318 .offset = readable_mem->GetSourceAddress(),
319 .size = parameters.ro_size,
320 };
321
322 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
323 rb.Push(ResultSuccess);
324 rb.PushIpcInterface<IJitEnvironment>(system, user_rx, user_ro);
325 }
326};
327
328void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
329 std::make_shared<JITU>(system)->InstallAsService(sm);
330}
331
332} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit.h b/src/core/hle/service/jit/jit.h
new file mode 100644
index 000000000..8fbf504a1
--- /dev/null
+++ b/src/core/hle/service/jit/jit.h
@@ -0,0 +1,20 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Core {
8class System;
9}
10
11namespace Service::SM {
12class ServiceManager;
13}
14
15namespace Service::JIT {
16
17/// Registers all JIT services with the specified service manager.
18void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
19
20} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp
new file mode 100644
index 000000000..630368fb3
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -0,0 +1,424 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <map>
7#include <span>
8#include <boost/icl/interval_set.hpp>
9#include <dynarmic/interface/A64/a64.h>
10#include <dynarmic/interface/A64/config.h>
11
12#include "common/alignment.h"
13#include "common/common_funcs.h"
14#include "common/div_ceil.h"
15#include "common/logging/log.h"
16#include "core/hle/service/jit/jit_context.h"
17#include "core/memory.h"
18
19namespace Service::JIT {
20
21constexpr std::array<u8, 4> STOP_ARM64 = {
22 0x01, 0x00, 0x00, 0xd4, // svc #0
23};
24
25constexpr std::array<u8, 8> RESOLVE_ARM64 = {
26 0x21, 0x00, 0x00, 0xd4, // svc #1
27 0xc0, 0x03, 0x5f, 0xd6, // ret
28};
29
30constexpr std::array<u8, 4> PANIC_ARM64 = {
31 0x41, 0x00, 0x00, 0xd4, // svc #2
32};
33
34constexpr std::array<u8, 60> MEMMOVE_ARM64 = {
35 0x1f, 0x00, 0x01, 0xeb, // cmp x0, x1
36 0x83, 0x01, 0x00, 0x54, // b.lo #+34
37 0x42, 0x04, 0x00, 0xd1, // sub x2, x2, 1
38 0x22, 0x01, 0xf8, 0xb7, // tbnz x2, #63, #+36
39 0x23, 0x68, 0x62, 0x38, // ldrb w3, [x1, x2]
40 0x03, 0x68, 0x22, 0x38, // strb w3, [x0, x2]
41 0xfc, 0xff, 0xff, 0x17, // b #-16
42 0x24, 0x68, 0x63, 0x38, // ldrb w4, [x1, x3]
43 0x04, 0x68, 0x23, 0x38, // strb w4, [x0, x3]
44 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
45 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
46 0x8b, 0xff, 0xff, 0x54, // b.lt #-16
47 0xc0, 0x03, 0x5f, 0xd6, // ret
48 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
49 0xfc, 0xff, 0xff, 0x17, // b #-16
50};
51
52constexpr std::array<u8, 28> MEMSET_ARM64 = {
53 0x03, 0x00, 0x80, 0xd2, // mov x3, 0
54 0x7f, 0x00, 0x02, 0xeb, // cmp x3, x2
55 0x4b, 0x00, 0x00, 0x54, // b.lt #+8
56 0xc0, 0x03, 0x5f, 0xd6, // ret
57 0x01, 0x68, 0x23, 0x38, // strb w1, [x0, x3]
58 0x63, 0x04, 0x00, 0x91, // add x3, x3, 1
59 0xfb, 0xff, 0xff, 0x17, // b #-20
60};
61
62struct HelperFunction {
63 const char* name;
64 const std::span<const u8> data;
65};
66
67constexpr std::array<HelperFunction, 6> HELPER_FUNCTIONS{{
68 {"_stop", STOP_ARM64},
69 {"_resolve", RESOLVE_ARM64},
70 {"_panic", PANIC_ARM64},
71 {"memcpy", MEMMOVE_ARM64},
72 {"memmove", MEMMOVE_ARM64},
73 {"memset", MEMSET_ARM64},
74}};
75
76struct Elf64_Dyn {
77 u64 d_tag;
78 u64 d_un;
79};
80
81struct Elf64_Rela {
82 u64 r_offset;
83 u64 r_info;
84 s64 r_addend;
85};
86
87static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) {
88 return static_cast<u32>(rela->r_info);
89}
90
91constexpr int DT_RELA = 7; /* Address of Rela relocs */
92constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */
93constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */
94
95constexpr size_t STACK_ALIGN = 16;
96
97class JITContextImpl;
98
99using IntervalSet = boost::icl::interval_set<VAddr>::type;
100using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
101
102class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
103public:
104 explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_,
105 IntervalSet& mapped_ranges_, JITContextImpl& parent_)
106 : memory{memory_}, local_memory{local_memory_},
107 mapped_ranges{mapped_ranges_}, parent{parent_} {}
108
109 u8 MemoryRead8(u64 vaddr) override {
110 return ReadMemory<u8>(vaddr);
111 }
112 u16 MemoryRead16(u64 vaddr) override {
113 return ReadMemory<u16>(vaddr);
114 }
115 u32 MemoryRead32(u64 vaddr) override {
116 return ReadMemory<u32>(vaddr);
117 }
118 u64 MemoryRead64(u64 vaddr) override {
119 return ReadMemory<u64>(vaddr);
120 }
121 u128 MemoryRead128(u64 vaddr) override {
122 return ReadMemory<u128>(vaddr);
123 }
124 std::string MemoryReadCString(u64 vaddr) {
125 std::string result;
126 u8 next;
127
128 while ((next = MemoryRead8(vaddr++)) != 0) {
129 result += next;
130 }
131
132 return result;
133 }
134
135 void MemoryWrite8(u64 vaddr, u8 value) override {
136 WriteMemory<u8>(vaddr, value);
137 }
138 void MemoryWrite16(u64 vaddr, u16 value) override {
139 WriteMemory<u16>(vaddr, value);
140 }
141 void MemoryWrite32(u64 vaddr, u32 value) override {
142 WriteMemory<u32>(vaddr, value);
143 }
144 void MemoryWrite64(u64 vaddr, u64 value) override {
145 WriteMemory<u64>(vaddr, value);
146 }
147 void MemoryWrite128(u64 vaddr, u128 value) override {
148 WriteMemory<u128>(vaddr, value);
149 }
150
151 bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override {
152 return WriteMemory<u8>(vaddr, value);
153 }
154 bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override {
155 return WriteMemory<u16>(vaddr, value);
156 }
157 bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override {
158 return WriteMemory<u32>(vaddr, value);
159 }
160 bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override {
161 return WriteMemory<u64>(vaddr, value);
162 }
163 bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override {
164 return WriteMemory<u128>(vaddr, value);
165 }
166
167 void CallSVC(u32 swi) override;
168 void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override;
169 void InterpreterFallback(u64 pc, size_t num_instructions) override;
170
171 void AddTicks(u64 ticks) override {}
172 u64 GetTicksRemaining() override {
173 return std::numeric_limits<u32>::max();
174 }
175 u64 GetCNTPCT() override {
176 return 0;
177 }
178
179 template <class T>
180 T ReadMemory(u64 vaddr) {
181 T ret{};
182 if (boost::icl::contains(mapped_ranges, vaddr)) {
183 memory.ReadBlock(vaddr, &ret, sizeof(T));
184 } else if (vaddr + sizeof(T) > local_memory.size()) {
185 LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr);
186 } else {
187 std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T));
188 }
189 return ret;
190 }
191
192 template <class T>
193 bool WriteMemory(u64 vaddr, const T value) {
194 if (boost::icl::contains(mapped_ranges, vaddr)) {
195 memory.WriteBlock(vaddr, &value, sizeof(T));
196 } else if (vaddr + sizeof(T) > local_memory.size()) {
197 LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr);
198 } else {
199 std::memcpy(local_memory.data() + vaddr, &value, sizeof(T));
200 }
201 return true;
202 }
203
204private:
205 Core::Memory::Memory& memory;
206 std::vector<u8>& local_memory;
207 IntervalSet& mapped_ranges;
208 JITContextImpl& parent;
209};
210
211class JITContextImpl {
212public:
213 explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
214 callbacks =
215 std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this);
216 user_config.callbacks = callbacks.get();
217 jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
218 }
219
220 bool LoadNRO(std::span<const u8> data) {
221 local_memory.clear();
222 local_memory.insert(local_memory.end(), data.begin(), data.end());
223
224 if (FixupRelocations()) {
225 InsertHelperFunctions();
226 InsertStack();
227 return true;
228 } else {
229 return false;
230 }
231 }
232
233 bool FixupRelocations() {
234 const VAddr mod_offset{callbacks->MemoryRead32(4)};
235 if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
236 return false;
237 }
238
239 VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
240 VAddr rela_dyn = 0;
241 size_t num_rela = 0;
242 while (true) {
243 const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
244 dynamic_offset += sizeof(Elf64_Dyn);
245
246 if (!dyn.d_tag) {
247 break;
248 }
249 if (dyn.d_tag == DT_RELA) {
250 rela_dyn = dyn.d_un;
251 }
252 if (dyn.d_tag == DT_RELASZ) {
253 num_rela = dyn.d_un / sizeof(Elf64_Rela);
254 }
255 }
256
257 for (size_t i = 0; i < num_rela; i++) {
258 const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
259 if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) {
260 continue;
261 }
262 const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
263 callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
264 }
265
266 return true;
267 }
268
269 void InsertHelperFunctions() {
270 for (const auto& [name, contents] : HELPER_FUNCTIONS) {
271 helpers[name] = local_memory.size();
272 local_memory.insert(local_memory.end(), contents.begin(), contents.end());
273 }
274 }
275
276 void InsertStack() {
277 const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -
278 local_memory.size()};
279 local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
280 top_of_stack = local_memory.size();
281 heap_pointer = top_of_stack;
282 }
283
284 void MapProcessMemory(VAddr dest_address, std::size_t size) {
285 mapped_ranges.add(IntervalType{dest_address, dest_address + size});
286 }
287
288 void PushArgument(const void* data, size_t size) {
289 const size_t num_words = Common::DivCeil(size, sizeof(u64));
290 const size_t current_pos = argument_stack.size();
291 argument_stack.insert(argument_stack.end(), num_words, 0);
292 std::memcpy(argument_stack.data() + current_pos, data, size);
293 }
294
295 void SetupArguments() {
296 for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
297 jit->SetRegister(i, argument_stack[i]);
298 }
299 if (argument_stack.size() > 8) {
300 const VAddr new_sp = Common::AlignDown(
301 top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
302 for (size_t i = 8; i < argument_stack.size(); i++) {
303 callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
304 }
305 jit->SetSP(new_sp);
306 }
307 argument_stack.clear();
308 heap_pointer = top_of_stack;
309 }
310
311 u64 CallFunction(VAddr func) {
312 jit->SetRegister(30, helpers["_stop"]);
313 jit->SetSP(top_of_stack);
314 SetupArguments();
315
316 jit->SetPC(func);
317 jit->Run();
318 return jit->GetRegister(0);
319 }
320
321 VAddr GetHelper(const std::string& name) {
322 return helpers[name];
323 }
324
325 VAddr AddHeap(const void* data, size_t size) {
326 const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)};
327 if (heap_pointer + num_bytes > local_memory.size()) {
328 local_memory.insert(local_memory.end(),
329 (heap_pointer + num_bytes) - local_memory.size(), 0);
330 }
331 const VAddr location{heap_pointer};
332 std::memcpy(local_memory.data() + location, data, size);
333 heap_pointer += num_bytes;
334 return location;
335 }
336
337 void GetHeap(VAddr location, void* data, size_t size) {
338 std::memcpy(data, local_memory.data() + location, size);
339 }
340
341 std::unique_ptr<DynarmicCallbacks64> callbacks;
342 std::vector<u8> local_memory;
343 std::vector<u64> argument_stack;
344 IntervalSet mapped_ranges;
345 Dynarmic::A64::UserConfig user_config;
346 std::unique_ptr<Dynarmic::A64::Jit> jit;
347 std::map<std::string, VAddr, std::less<>> helpers;
348 Core::Memory::Memory& memory;
349 VAddr top_of_stack;
350 VAddr heap_pointer;
351};
352
353void DynarmicCallbacks64::CallSVC(u32 swi) {
354 switch (swi) {
355 case 0:
356 parent.jit->HaltExecution();
357 break;
358
359 case 1: {
360 // X0 contains a char* for a symbol to resolve
361 std::string name{MemoryReadCString(parent.jit->GetRegister(0))};
362 const auto helper{parent.helpers[name]};
363
364 if (helper != 0) {
365 parent.jit->SetRegister(0, helper);
366 } else {
367 LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
368 parent.jit->SetRegister(0, parent.helpers["_panic"]);
369 }
370 break;
371 }
372
373 case 2:
374 default:
375 LOG_CRITICAL(Service_JIT, "plugin panicked!");
376 parent.jit->HaltExecution();
377 break;
378 }
379}
380
381void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
382 LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
383 parent.jit->HaltExecution();
384}
385
386void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) {
387 LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc);
388 parent.jit->HaltExecution();
389}
390
391JITContext::JITContext(Core::Memory::Memory& memory)
392 : impl{std::make_unique<JITContextImpl>(memory)} {}
393
394JITContext::~JITContext() {}
395
396bool JITContext::LoadNRO(std::span<const u8> data) {
397 return impl->LoadNRO(data);
398}
399
400void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) {
401 impl->MapProcessMemory(dest_address, size);
402}
403
404u64 JITContext::CallFunction(VAddr func) {
405 return impl->CallFunction(func);
406}
407
408void JITContext::PushArgument(const void* data, size_t size) {
409 impl->PushArgument(data, size);
410}
411
412VAddr JITContext::GetHelper(const std::string& name) {
413 return impl->GetHelper(name);
414}
415
416VAddr JITContext::AddHeap(const void* data, size_t size) {
417 return impl->AddHeap(data, size);
418}
419
420void JITContext::GetHeap(VAddr location, void* data, size_t size) {
421 impl->GetHeap(location, data, size);
422}
423
424} // namespace Service::JIT
diff --git a/src/core/hle/service/jit/jit_context.h b/src/core/hle/service/jit/jit_context.h
new file mode 100644
index 000000000..d8bf76cff
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.h
@@ -0,0 +1,65 @@
1// Copyright 2022 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <span>
9#include <string>
10
11#include "common/common_types.h"
12
13namespace Core::Memory {
14class Memory;
15}
16
17namespace Service::JIT {
18
19class JITContextImpl;
20
21class JITContext {
22public:
23 explicit JITContext(Core::Memory::Memory& memory);
24 ~JITContext();
25
26 [[nodiscard]] bool LoadNRO(std::span<const u8> data);
27 void MapProcessMemory(VAddr dest_address, std::size_t size);
28
29 template <typename T, typename... Ts>
30 u64 CallFunction(VAddr func, T argument, Ts... rest) {
31 static_assert(std::is_trivially_copyable_v<T>);
32 PushArgument(&argument, sizeof(argument));
33
34 if constexpr (sizeof...(rest) > 0) {
35 return CallFunction(func, rest...);
36 } else {
37 return CallFunction(func);
38 }
39 }
40
41 u64 CallFunction(VAddr func);
42 VAddr GetHelper(const std::string& name);
43
44 template <typename T>
45 VAddr AddHeap(T argument) {
46 return AddHeap(&argument, sizeof(argument));
47 }
48 VAddr AddHeap(const void* data, size_t size);
49
50 template <typename T>
51 T GetHeap(VAddr location) {
52 static_assert(std::is_trivially_copyable_v<T>);
53 T result;
54 GetHeap(location, &result, sizeof(result));
55 return result;
56 }
57 void GetHeap(VAddr location, void* data, size_t size);
58
59private:
60 std::unique_ptr<JITContextImpl> impl;
61
62 void PushArgument(const void* data, size_t size);
63};
64
65} // namespace Service::JIT
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index b8c2c6e51..ff0bbb788 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -17,21 +17,12 @@ namespace Service::KernelHelpers {
17 17
18ServiceContext::ServiceContext(Core::System& system_, std::string name_) 18ServiceContext::ServiceContext(Core::System& system_, std::string name_)
19 : kernel(system_.Kernel()) { 19 : kernel(system_.Kernel()) {
20
21 // Create a resource limit for the process.
22 const auto physical_memory_size =
23 kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::System);
24 auto* resource_limit = Kernel::CreateResourceLimitForProcess(system_, physical_memory_size);
25
26 // Create the process. 20 // Create the process.
27 process = Kernel::KProcess::Create(kernel); 21 process = Kernel::KProcess::Create(kernel);
28 ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_), 22 ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
29 Kernel::KProcess::ProcessType::KernelInternal, 23 Kernel::KProcess::ProcessType::KernelInternal,
30 resource_limit) 24 kernel.GetSystemResourceLimit())
31 .IsSuccess()); 25 .IsSuccess());
32
33 // Close reference to our resource limit, as the process opens one.
34 resource_limit->Close();
35} 26}
36 27
37ServiceContext::~ServiceContext() { 28ServiceContext::~ServiceContext() {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 099276420..cf727c167 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -12,7 +12,6 @@
12#include "core/core.h" 12#include "core/core.h"
13#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/k_page_table.h" 14#include "core/hle/kernel/k_page_table.h"
15#include "core/hle/kernel/k_system_control.h"
16#include "core/hle/kernel/svc_results.h" 15#include "core/hle/kernel/svc_results.h"
17#include "core/hle/kernel/svc_types.h" 16#include "core/hle/kernel/svc_types.h"
18#include "core/hle/service/ldr/ldr.h" 17#include "core/hle/service/ldr/ldr.h"
@@ -390,8 +389,12 @@ public:
390 389
391 if (bss_size) { 390 if (bss_size) {
392 auto block_guard = detail::ScopeExit([&] { 391 auto block_guard = detail::ScopeExit([&] {
393 page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); 392 page_table.UnmapCodeMemory(
394 page_table.UnmapCodeMemory(addr, nro_addr, nro_size); 393 addr + nro_size, bss_addr, bss_size,
394 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
395 page_table.UnmapCodeMemory(
396 addr, nro_addr, nro_size,
397 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
395 }); 398 });
396 399
397 const ResultCode result{ 400 const ResultCode result{
@@ -571,17 +574,21 @@ public:
571 auto& page_table{system.CurrentProcess()->PageTable()}; 574 auto& page_table{system.CurrentProcess()->PageTable()};
572 575
573 if (info.bss_size != 0) { 576 if (info.bss_size != 0) {
574 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + 577 CASCADE_CODE(page_table.UnmapCodeMemory(
575 info.ro_size + info.data_size, 578 info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
576 info.bss_address, info.bss_size)); 579 info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
577 } 580 }
578 581
579 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, 582 CASCADE_CODE(page_table.UnmapCodeMemory(
580 info.src_addr + info.text_size + info.ro_size, 583 info.nro_address + info.text_size + info.ro_size,
581 info.data_size)); 584 info.src_addr + info.text_size + info.ro_size, info.data_size,
582 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, 585 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
583 info.src_addr + info.text_size, info.ro_size)); 586 CASCADE_CODE(page_table.UnmapCodeMemory(
584 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); 587 info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
588 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
589 CASCADE_CODE(page_table.UnmapCodeMemory(
590 info.nro_address, info.src_addr, info.text_size,
591 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
585 return ResultSuccess; 592 return ResultSuccess;
586 } 593 }
587 594
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 0a57c3cde..188231615 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -1,4 +1,4 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
@@ -12,7 +12,6 @@
12#include "core/hle/service/acc/profile_manager.h" 12#include "core/hle/service/acc/profile_manager.h"
13#include "core/hle/service/mii/mii_manager.h" 13#include "core/hle/service/mii/mii_manager.h"
14#include "core/hle/service/mii/raw_data.h" 14#include "core/hle/service/mii/raw_data.h"
15#include "core/hle/service/mii/types.h"
16 15
17namespace Service::Mii { 16namespace Service::Mii {
18 17
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 6999d15b1..5d134c425 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -1,315 +1,16 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <vector> 7#include <vector>
9#include "common/bit_field.h" 8
10#include "common/common_funcs.h"
11#include "common/uuid.h"
12#include "core/hle/result.h" 9#include "core/hle/result.h"
13#include "core/hle/service/mii/types.h" 10#include "core/hle/service/mii/types.h"
14 11
15namespace Service::Mii { 12namespace Service::Mii {
16 13
17enum class Source : u32 {
18 Database = 0,
19 Default = 1,
20 Account = 2,
21 Friend = 3,
22};
23
24enum class SourceFlag : u32 {
25 None = 0,
26 Database = 1 << 0,
27 Default = 1 << 1,
28};
29DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
30
31struct MiiInfo {
32 Common::UUID uuid;
33 std::array<char16_t, 11> name;
34 u8 font_region;
35 u8 favorite_color;
36 u8 gender;
37 u8 height;
38 u8 build;
39 u8 type;
40 u8 region_move;
41 u8 faceline_type;
42 u8 faceline_color;
43 u8 faceline_wrinkle;
44 u8 faceline_make;
45 u8 hair_type;
46 u8 hair_color;
47 u8 hair_flip;
48 u8 eye_type;
49 u8 eye_color;
50 u8 eye_scale;
51 u8 eye_aspect;
52 u8 eye_rotate;
53 u8 eye_x;
54 u8 eye_y;
55 u8 eyebrow_type;
56 u8 eyebrow_color;
57 u8 eyebrow_scale;
58 u8 eyebrow_aspect;
59 u8 eyebrow_rotate;
60 u8 eyebrow_x;
61 u8 eyebrow_y;
62 u8 nose_type;
63 u8 nose_scale;
64 u8 nose_y;
65 u8 mouth_type;
66 u8 mouth_color;
67 u8 mouth_scale;
68 u8 mouth_aspect;
69 u8 mouth_y;
70 u8 beard_color;
71 u8 beard_type;
72 u8 mustache_type;
73 u8 mustache_scale;
74 u8 mustache_y;
75 u8 glasses_type;
76 u8 glasses_color;
77 u8 glasses_scale;
78 u8 glasses_y;
79 u8 mole_type;
80 u8 mole_scale;
81 u8 mole_x;
82 u8 mole_y;
83 u8 padding;
84
85 std::u16string Name() const;
86};
87static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
88static_assert(std::has_unique_object_representations_v<MiiInfo>,
89 "All bits of MiiInfo must contribute to its value.");
90
91#pragma pack(push, 4)
92
93struct MiiInfoElement {
94 MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
95
96 MiiInfo info{};
97 Source source{};
98};
99static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
100
101struct MiiStoreBitFields {
102 union {
103 u32 word_0{};
104
105 BitField<0, 8, u32> hair_type;
106 BitField<8, 7, u32> height;
107 BitField<15, 1, u32> mole_type;
108 BitField<16, 7, u32> build;
109 BitField<23, 1, HairFlip> hair_flip;
110 BitField<24, 7, u32> hair_color;
111 BitField<31, 1, u32> type;
112 };
113
114 union {
115 u32 word_1{};
116
117 BitField<0, 7, u32> eye_color;
118 BitField<7, 1, Gender> gender;
119 BitField<8, 7, u32> eyebrow_color;
120 BitField<16, 7, u32> mouth_color;
121 BitField<24, 7, u32> beard_color;
122 };
123
124 union {
125 u32 word_2{};
126
127 BitField<0, 7, u32> glasses_color;
128 BitField<8, 6, u32> eye_type;
129 BitField<14, 2, u32> region_move;
130 BitField<16, 6, u32> mouth_type;
131 BitField<22, 2, FontRegion> font_region;
132 BitField<24, 5, u32> eye_y;
133 BitField<29, 3, u32> glasses_scale;
134 };
135
136 union {
137 u32 word_3{};
138
139 BitField<0, 5, u32> eyebrow_type;
140 BitField<5, 3, MustacheType> mustache_type;
141 BitField<8, 5, u32> nose_type;
142 BitField<13, 3, BeardType> beard_type;
143 BitField<16, 5, u32> nose_y;
144 BitField<21, 3, u32> mouth_aspect;
145 BitField<24, 5, u32> mouth_y;
146 BitField<29, 3, u32> eyebrow_aspect;
147 };
148
149 union {
150 u32 word_4{};
151
152 BitField<0, 5, u32> mustache_y;
153 BitField<5, 3, u32> eye_rotate;
154 BitField<8, 5, u32> glasses_y;
155 BitField<13, 3, u32> eye_aspect;
156 BitField<16, 5, u32> mole_x;
157 BitField<21, 3, u32> eye_scale;
158 BitField<24, 5, u32> mole_y;
159 };
160
161 union {
162 u32 word_5{};
163
164 BitField<0, 5, u32> glasses_type;
165 BitField<8, 4, u32> favorite_color;
166 BitField<12, 4, u32> faceline_type;
167 BitField<16, 4, u32> faceline_color;
168 BitField<20, 4, u32> faceline_wrinkle;
169 BitField<24, 4, u32> faceline_makeup;
170 BitField<28, 4, u32> eye_x;
171 };
172
173 union {
174 u32 word_6{};
175
176 BitField<0, 4, u32> eyebrow_scale;
177 BitField<4, 4, u32> eyebrow_rotate;
178 BitField<8, 4, u32> eyebrow_x;
179 BitField<12, 4, u32> eyebrow_y;
180 BitField<16, 4, u32> nose_scale;
181 BitField<20, 4, u32> mouth_scale;
182 BitField<24, 4, u32> mustache_scale;
183 BitField<28, 4, u32> mole_scale;
184 };
185};
186static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
187static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
188 "MiiStoreBitFields is not trivially copyable.");
189
190struct MiiStoreData {
191 using Name = std::array<char16_t, 10>;
192
193 MiiStoreData();
194 MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
195 const Common::UUID& user_id);
196
197 // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
198 // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
199 // not suitable for our uses.
200 struct {
201 std::array<u8, 0x1C> data{};
202 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
203
204 Name name{};
205 Common::UUID uuid{};
206 } data;
207
208 u16 data_crc{};
209 u16 device_crc{};
210};
211static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
212
213struct MiiStoreDataElement {
214 MiiStoreData data{};
215 Source source{};
216};
217static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
218
219struct MiiDatabase {
220 u32 magic{}; // 'NFDB'
221 std::array<MiiStoreData, 0x64> miis{};
222 INSERT_PADDING_BYTES(1);
223 u8 count{};
224 u16 crc{};
225};
226static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
227
228struct RandomMiiValues {
229 std::array<u8, 0xbc> values{};
230};
231static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
232
233struct RandomMiiData4 {
234 Gender gender{};
235 Age age{};
236 Race race{};
237 u32 values_count{};
238 std::array<u32, 47> values{};
239};
240static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
241
242struct RandomMiiData3 {
243 u32 arg_1;
244 u32 arg_2;
245 u32 values_count;
246 std::array<u32, 47> values{};
247};
248static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
249
250struct RandomMiiData2 {
251 u32 arg_1;
252 u32 values_count;
253 std::array<u32, 47> values{};
254};
255static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
256
257struct DefaultMii {
258 u32 face_type{};
259 u32 face_color{};
260 u32 face_wrinkle{};
261 u32 face_makeup{};
262 u32 hair_type{};
263 u32 hair_color{};
264 u32 hair_flip{};
265 u32 eye_type{};
266 u32 eye_color{};
267 u32 eye_scale{};
268 u32 eye_aspect{};
269 u32 eye_rotate{};
270 u32 eye_x{};
271 u32 eye_y{};
272 u32 eyebrow_type{};
273 u32 eyebrow_color{};
274 u32 eyebrow_scale{};
275 u32 eyebrow_aspect{};
276 u32 eyebrow_rotate{};
277 u32 eyebrow_x{};
278 u32 eyebrow_y{};
279 u32 nose_type{};
280 u32 nose_scale{};
281 u32 nose_y{};
282 u32 mouth_type{};
283 u32 mouth_color{};
284 u32 mouth_scale{};
285 u32 mouth_aspect{};
286 u32 mouth_y{};
287 u32 mustache_type{};
288 u32 beard_type{};
289 u32 beard_color{};
290 u32 mustache_scale{};
291 u32 mustache_y{};
292 u32 glasses_type{};
293 u32 glasses_color{};
294 u32 glasses_scale{};
295 u32 glasses_y{};
296 u32 mole_type{};
297 u32 mole_scale{};
298 u32 mole_x{};
299 u32 mole_y{};
300 u32 height{};
301 u32 weight{};
302 Gender gender{};
303 u32 favorite_color{};
304 u32 region{};
305 FontRegion font_region{};
306 u32 type{};
307 INSERT_PADDING_WORDS(5);
308};
309static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
310
311#pragma pack(pop)
312
313// The Mii manager is responsible for loading and storing the Miis to the database in NAND along 14// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
314// with providing an easy interface for HLE emulation of the mii service. 15// with providing an easy interface for HLE emulation of the mii service.
315class MiiManager { 16class MiiManager {
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h
index bd90c2162..2e39c0d4f 100644
--- a/src/core/hle/service/mii/raw_data.h
+++ b/src/core/hle/service/mii/raw_data.h
@@ -6,7 +6,7 @@
6 6
7#include <array> 7#include <array>
8 8
9#include "core/hle/service/mii/mii_manager.h" 9#include "core/hle/service/mii/types.h"
10 10
11namespace Service::Mii::RawData { 11namespace Service::Mii::RawData {
12 12
diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h
index d65a1055e..5580b8c6a 100644
--- a/src/core/hle/service/mii/types.h
+++ b/src/core/hle/service/mii/types.h
@@ -1,11 +1,16 @@
1// Copyright 2020 yuzu emulator team 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <type_traits>
9
10#include "common/bit_field.h"
7#include "common/common_funcs.h" 11#include "common/common_funcs.h"
8#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/uuid.h"
9 14
10namespace Service::Mii { 15namespace Service::Mii {
11 16
@@ -25,7 +30,11 @@ enum class BeardType : u32 {
25 Beard5, 30 Beard5,
26}; 31};
27 32
28enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache }; 33enum class BeardAndMustacheFlag : u32 {
34 Beard = 1,
35 Mustache,
36 All = Beard | Mustache,
37};
29DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag); 38DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
30 39
31enum class FontRegion : u32 { 40enum class FontRegion : u32 {
@@ -64,4 +73,298 @@ enum class Race : u32 {
64 All, 73 All,
65}; 74};
66 75
76enum class Source : u32 {
77 Database = 0,
78 Default = 1,
79 Account = 2,
80 Friend = 3,
81};
82
83enum class SourceFlag : u32 {
84 None = 0,
85 Database = 1 << 0,
86 Default = 1 << 1,
87};
88DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
89
90struct MiiInfo {
91 Common::UUID uuid;
92 std::array<char16_t, 11> name;
93 u8 font_region;
94 u8 favorite_color;
95 u8 gender;
96 u8 height;
97 u8 build;
98 u8 type;
99 u8 region_move;
100 u8 faceline_type;
101 u8 faceline_color;
102 u8 faceline_wrinkle;
103 u8 faceline_make;
104 u8 hair_type;
105 u8 hair_color;
106 u8 hair_flip;
107 u8 eye_type;
108 u8 eye_color;
109 u8 eye_scale;
110 u8 eye_aspect;
111 u8 eye_rotate;
112 u8 eye_x;
113 u8 eye_y;
114 u8 eyebrow_type;
115 u8 eyebrow_color;
116 u8 eyebrow_scale;
117 u8 eyebrow_aspect;
118 u8 eyebrow_rotate;
119 u8 eyebrow_x;
120 u8 eyebrow_y;
121 u8 nose_type;
122 u8 nose_scale;
123 u8 nose_y;
124 u8 mouth_type;
125 u8 mouth_color;
126 u8 mouth_scale;
127 u8 mouth_aspect;
128 u8 mouth_y;
129 u8 beard_color;
130 u8 beard_type;
131 u8 mustache_type;
132 u8 mustache_scale;
133 u8 mustache_y;
134 u8 glasses_type;
135 u8 glasses_color;
136 u8 glasses_scale;
137 u8 glasses_y;
138 u8 mole_type;
139 u8 mole_scale;
140 u8 mole_x;
141 u8 mole_y;
142 u8 padding;
143};
144static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
145static_assert(std::has_unique_object_representations_v<MiiInfo>,
146 "All bits of MiiInfo must contribute to its value.");
147
148#pragma pack(push, 4)
149
150struct MiiInfoElement {
151 MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
152
153 MiiInfo info{};
154 Source source{};
155};
156static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
157
158struct MiiStoreBitFields {
159 union {
160 u32 word_0{};
161
162 BitField<0, 8, u32> hair_type;
163 BitField<8, 7, u32> height;
164 BitField<15, 1, u32> mole_type;
165 BitField<16, 7, u32> build;
166 BitField<23, 1, HairFlip> hair_flip;
167 BitField<24, 7, u32> hair_color;
168 BitField<31, 1, u32> type;
169 };
170
171 union {
172 u32 word_1{};
173
174 BitField<0, 7, u32> eye_color;
175 BitField<7, 1, Gender> gender;
176 BitField<8, 7, u32> eyebrow_color;
177 BitField<16, 7, u32> mouth_color;
178 BitField<24, 7, u32> beard_color;
179 };
180
181 union {
182 u32 word_2{};
183
184 BitField<0, 7, u32> glasses_color;
185 BitField<8, 6, u32> eye_type;
186 BitField<14, 2, u32> region_move;
187 BitField<16, 6, u32> mouth_type;
188 BitField<22, 2, FontRegion> font_region;
189 BitField<24, 5, u32> eye_y;
190 BitField<29, 3, u32> glasses_scale;
191 };
192
193 union {
194 u32 word_3{};
195
196 BitField<0, 5, u32> eyebrow_type;
197 BitField<5, 3, MustacheType> mustache_type;
198 BitField<8, 5, u32> nose_type;
199 BitField<13, 3, BeardType> beard_type;
200 BitField<16, 5, u32> nose_y;
201 BitField<21, 3, u32> mouth_aspect;
202 BitField<24, 5, u32> mouth_y;
203 BitField<29, 3, u32> eyebrow_aspect;
204 };
205
206 union {
207 u32 word_4{};
208
209 BitField<0, 5, u32> mustache_y;
210 BitField<5, 3, u32> eye_rotate;
211 BitField<8, 5, u32> glasses_y;
212 BitField<13, 3, u32> eye_aspect;
213 BitField<16, 5, u32> mole_x;
214 BitField<21, 3, u32> eye_scale;
215 BitField<24, 5, u32> mole_y;
216 };
217
218 union {
219 u32 word_5{};
220
221 BitField<0, 5, u32> glasses_type;
222 BitField<8, 4, u32> favorite_color;
223 BitField<12, 4, u32> faceline_type;
224 BitField<16, 4, u32> faceline_color;
225 BitField<20, 4, u32> faceline_wrinkle;
226 BitField<24, 4, u32> faceline_makeup;
227 BitField<28, 4, u32> eye_x;
228 };
229
230 union {
231 u32 word_6{};
232
233 BitField<0, 4, u32> eyebrow_scale;
234 BitField<4, 4, u32> eyebrow_rotate;
235 BitField<8, 4, u32> eyebrow_x;
236 BitField<12, 4, u32> eyebrow_y;
237 BitField<16, 4, u32> nose_scale;
238 BitField<20, 4, u32> mouth_scale;
239 BitField<24, 4, u32> mustache_scale;
240 BitField<28, 4, u32> mole_scale;
241 };
242};
243static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
244static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
245 "MiiStoreBitFields is not trivially copyable.");
246
247struct MiiStoreData {
248 using Name = std::array<char16_t, 10>;
249
250 MiiStoreData();
251 MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
252 const Common::UUID& user_id);
253
254 // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
255 // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
256 // not suitable for our uses.
257 struct {
258 std::array<u8, 0x1C> data{};
259 static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
260
261 Name name{};
262 Common::UUID uuid{};
263 } data;
264
265 u16 data_crc{};
266 u16 device_crc{};
267};
268static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
269
270struct MiiStoreDataElement {
271 MiiStoreData data{};
272 Source source{};
273};
274static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
275
276struct MiiDatabase {
277 u32 magic{}; // 'NFDB'
278 std::array<MiiStoreData, 0x64> miis{};
279 INSERT_PADDING_BYTES(1);
280 u8 count{};
281 u16 crc{};
282};
283static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
284
285struct RandomMiiValues {
286 std::array<u8, 0xbc> values{};
287};
288static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
289
290struct RandomMiiData4 {
291 Gender gender{};
292 Age age{};
293 Race race{};
294 u32 values_count{};
295 std::array<u32, 47> values{};
296};
297static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
298
299struct RandomMiiData3 {
300 u32 arg_1;
301 u32 arg_2;
302 u32 values_count;
303 std::array<u32, 47> values{};
304};
305static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
306
307struct RandomMiiData2 {
308 u32 arg_1;
309 u32 values_count;
310 std::array<u32, 47> values{};
311};
312static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
313
314struct DefaultMii {
315 u32 face_type{};
316 u32 face_color{};
317 u32 face_wrinkle{};
318 u32 face_makeup{};
319 u32 hair_type{};
320 u32 hair_color{};
321 u32 hair_flip{};
322 u32 eye_type{};
323 u32 eye_color{};
324 u32 eye_scale{};
325 u32 eye_aspect{};
326 u32 eye_rotate{};
327 u32 eye_x{};
328 u32 eye_y{};
329 u32 eyebrow_type{};
330 u32 eyebrow_color{};
331 u32 eyebrow_scale{};
332 u32 eyebrow_aspect{};
333 u32 eyebrow_rotate{};
334 u32 eyebrow_x{};
335 u32 eyebrow_y{};
336 u32 nose_type{};
337 u32 nose_scale{};
338 u32 nose_y{};
339 u32 mouth_type{};
340 u32 mouth_color{};
341 u32 mouth_scale{};
342 u32 mouth_aspect{};
343 u32 mouth_y{};
344 u32 mustache_type{};
345 u32 beard_type{};
346 u32 beard_color{};
347 u32 mustache_scale{};
348 u32 mustache_y{};
349 u32 glasses_type{};
350 u32 glasses_color{};
351 u32 glasses_scale{};
352 u32 glasses_y{};
353 u32 mole_type{};
354 u32 mole_scale{};
355 u32 mole_x{};
356 u32 mole_y{};
357 u32 height{};
358 u32 weight{};
359 Gender gender{};
360 u32 favorite_color{};
361 u32 region{};
362 FontRegion font_region{};
363 u32 type{};
364 INSERT_PADDING_WORDS(5);
365};
366static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
367
368#pragma pack(pop)
369
67} // namespace Service::Mii 370} // namespace Service::Mii
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 513107715..dab99b675 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -12,6 +12,7 @@
12#include "core/hid/hid_types.h" 12#include "core/hid/hid_types.h"
13#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/k_event.h" 14#include "core/hle/kernel/k_event.h"
15#include "core/hle/service/mii/mii_manager.h"
15#include "core/hle/service/nfp/nfp.h" 16#include "core/hle/service/nfp/nfp.h"
16#include "core/hle/service/nfp/nfp_user.h" 17#include "core/hle/service/nfp/nfp_user.h"
17 18
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 022f13b29..ab652f635 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -9,7 +9,7 @@
9 9
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11#include "core/hle/service/kernel_helpers.h" 11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/mii/mii_manager.h" 12#include "core/hle/service/mii/types.h"
13#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
14 14
15namespace Kernel { 15namespace Kernel {
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index a253dd066..edb576ad3 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -5,7 +5,6 @@
5#include "core/core.h" 5#include "core/core.h"
6#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/k_event.h" 7#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/service/kernel_helpers.h" 8#include "core/hle/service/kernel_helpers.h"
10#include "core/hle/service/nifm/nifm.h" 9#include "core/hle/service/nifm/nifm.h"
11#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
index 36ce46353..3a83d0698 100644
--- a/src/core/hle/service/ns/pdm_qry.cpp
+++ b/src/core/hle/service/ns/pdm_qry.cpp
@@ -9,7 +9,6 @@
9#include "core/hle/ipc_helpers.h" 9#include "core/hle/ipc_helpers.h"
10#include "core/hle/service/ns/pdm_qry.h" 10#include "core/hle/service/ns/pdm_qry.h"
11#include "core/hle/service/service.h" 11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13 12
14namespace Service::NS { 13namespace Service::NS {
15 14
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 68f1e9060..9fad45fe1 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -38,18 +38,16 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
38void nvdisp_disp0::OnOpen(DeviceFD fd) {} 38void nvdisp_disp0::OnOpen(DeviceFD fd) {}
39void nvdisp_disp0::OnClose(DeviceFD fd) {} 39void nvdisp_disp0::OnClose(DeviceFD fd) {}
40 40
41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, 41void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
42 u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform, 42 u32 height, u32 stride, android::BufferTransformFlags transform,
43 const Common::Rectangle<int>& crop_rect) { 43 const Common::Rectangle<int>& crop_rect) {
44 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); 44 const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
45 LOG_TRACE(Service, 45 LOG_TRACE(Service,
46 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", 46 "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
47 addr, offset, width, height, stride, format); 47 addr, offset, width, height, stride, format);
48 48
49 const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format); 49 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
50 const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform); 50 stride, format, transform, crop_rect};
51 const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
52 stride, pixel_format, transform_flags, crop_rect};
53 51
54 system.GetPerfStats().EndSystemFrame(); 52 system.GetPerfStats().EndSystemFrame();
55 system.GPU().SwapBuffers(&framebuffer); 53 system.GPU().SwapBuffers(&framebuffer);
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index de01e1d5f..30b5da429 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -9,7 +9,8 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/math_util.h" 10#include "common/math_util.h"
11#include "core/hle/service/nvdrv/devices/nvdevice.h" 11#include "core/hle/service/nvdrv/devices/nvdevice.h"
12#include "core/hle/service/nvflinger/buffer_queue.h" 12#include "core/hle/service/nvflinger/buffer_transform_flags.h"
13#include "core/hle/service/nvflinger/pixel_format.h"
13 14
14namespace Service::Nvidia::Devices { 15namespace Service::Nvidia::Devices {
15 16
@@ -31,8 +32,8 @@ public:
31 void OnClose(DeviceFD fd) override; 32 void OnClose(DeviceFD fd) override;
32 33
33 /// Performs a screen flip, drawing the buffer pointed to by the handle. 34 /// Performs a screen flip, drawing the buffer pointed to by the handle.
34 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 35 void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
35 NVFlinger::BufferQueue::BufferTransformFlags transform, 36 u32 stride, android::BufferTransformFlags transform,
36 const Common::Rectangle<int>& crop_rect); 37 const Common::Rectangle<int>& crop_rect);
37 38
38private: 39private:
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index f9b82b504..44c54c665 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -134,7 +134,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector
134 } 134 }
135 135
136 EventState status = events_interface.status[event_id]; 136 EventState status = events_interface.status[event_id];
137 const bool bad_parameter = status != EventState::Free && status != EventState::Registered; 137 const bool bad_parameter = status == EventState::Busy;
138 if (bad_parameter) { 138 if (bad_parameter) {
139 std::memcpy(output.data(), &params, sizeof(params)); 139 std::memcpy(output.data(), &params, sizeof(params));
140 return NvResult::BadParameter; 140 return NvResult::BadParameter;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 898d00a17..f434f6929 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -5,6 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <vector>
8
9#include "common/common_funcs.h"
8#include "common/common_types.h" 10#include "common/common_types.h"
9#include "common/swap.h" 11#include "common/swap.h"
10#include "core/hle/service/nvdrv/devices/nvdevice.h" 12#include "core/hle/service/nvdrv/devices/nvdevice.h"
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 0a043e386..dde5b1507 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -187,7 +187,7 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve
187 return NvResult::Success; 187 return NvResult::Success;
188} 188}
189 189
190static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { 190static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
191 return { 191 return {
192 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 192 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
193 Tegra::SubmissionMode::Increasing), 193 Tegra::SubmissionMode::Increasing),
@@ -198,7 +198,8 @@ static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
198 }; 198 };
199} 199}
200 200
201static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) { 201static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence,
202 u32 add_increment) {
202 std::vector<Tegra::CommandHeader> result{ 203 std::vector<Tegra::CommandHeader> result{
203 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, 204 Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
204 Tegra::SubmissionMode::Increasing), 205 Tegra::SubmissionMode::Increasing),
@@ -213,7 +214,7 @@ static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence,
213 return result; 214 return result;
214} 215}
215 216
216static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence, 217static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence,
217 u32 add_increment) { 218 u32 add_increment) {
218 std::vector<Tegra::CommandHeader> result{ 219 std::vector<Tegra::CommandHeader> result{
219 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, 220 Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index f27a82bff..b2e943e45 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8#include <vector> 8#include <vector>
9#include "common/bit_field.h" 9#include "common/bit_field.h"
10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "common/swap.h" 12#include "common/swap.h"
12#include "core/hle/service/nvdrv/devices/nvdevice.h" 13#include "core/hle/service/nvdrv/devices/nvdevice.h"
@@ -108,7 +109,7 @@ private:
108 static_assert(sizeof(IoctlGetErrorNotification) == 16, 109 static_assert(sizeof(IoctlGetErrorNotification) == 16,
109 "IoctlGetErrorNotification is incorrect size"); 110 "IoctlGetErrorNotification is incorrect size");
110 111
111 static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); 112 static_assert(sizeof(NvFence) == 8, "Fence is incorrect size");
112 113
113 struct IoctlAllocGpfifoEx { 114 struct IoctlAllocGpfifoEx {
114 u32_le num_entries{}; 115 u32_le num_entries{};
@@ -126,7 +127,7 @@ private:
126 u32_le num_entries{}; // in 127 u32_le num_entries{}; // in
127 u32_le flags{}; // in 128 u32_le flags{}; // in
128 u32_le unk0{}; // in (1 works) 129 u32_le unk0{}; // in (1 works)
129 Fence fence_out{}; // out 130 NvFence fence_out{}; // out
130 u32_le unk1{}; // in 131 u32_le unk1{}; // in
131 u32_le unk2{}; // in 132 u32_le unk2{}; // in
132 u32_le unk3{}; // in 133 u32_le unk3{}; // in
@@ -152,13 +153,13 @@ private:
152 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt 153 BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
153 BitField<8, 1, u32_le> increment; // increment the returned fence 154 BitField<8, 1, u32_le> increment; // increment the returned fence
154 } flags; 155 } flags;
155 Fence fence_out{}; // returned new fence object for others to wait on 156 NvFence fence_out{}; // returned new fence object for others to wait on
156 157
157 u32 AddIncrementValue() const { 158 u32 AddIncrementValue() const {
158 return flags.add_increment.Value() << 1; 159 return flags.add_increment.Value() << 1;
159 } 160 }
160 }; 161 };
161 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), 162 static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
162 "IoctlSubmitGpfifo is incorrect size"); 163 "IoctlSubmitGpfifo is incorrect size");
163 164
164 struct IoctlGetWaitbase { 165 struct IoctlGetWaitbase {
@@ -193,7 +194,7 @@ private:
193 194
194 std::shared_ptr<nvmap> nvmap_dev; 195 std::shared_ptr<nvmap> nvmap_dev;
195 SyncpointManager& syncpoint_manager; 196 SyncpointManager& syncpoint_manager;
196 Fence channel_fence; 197 NvFence channel_fence;
197}; 198};
198 199
199} // namespace Service::Nvidia::Devices 200} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 5ab221fc1..3069c3c80 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -16,17 +16,11 @@ using DeviceFD = s32;
16 16
17constexpr DeviceFD INVALID_NVDRV_FD = -1; 17constexpr DeviceFD INVALID_NVDRV_FD = -1;
18 18
19struct Fence { 19struct NvFence {
20 s32 id; 20 s32 id;
21 u32 value; 21 u32 value;
22}; 22};
23 23static_assert(sizeof(NvFence) == 8, "NvFence has wrong size");
24static_assert(sizeof(Fence) == 8, "Fence has wrong size");
25
26struct MultiFence {
27 u32 num_fences;
28 std::array<Fence, 4> fences;
29};
30 24
31enum class NvResult : u32 { 25enum class NvResult : u32 {
32 Success = 0x0, 26 Success = 0x0,
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index a5af5b785..11cf6c0d1 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -12,6 +12,7 @@
12#include "core/hle/service/kernel_helpers.h" 12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvdrv/nvdata.h" 13#include "core/hle/service/nvdrv/nvdata.h"
14#include "core/hle/service/nvdrv/syncpoint_manager.h" 14#include "core/hle/service/nvdrv/syncpoint_manager.h"
15#include "core/hle/service/nvflinger/ui/fence.h"
15#include "core/hle/service/service.h" 16#include "core/hle/service/service.h"
16 17
17namespace Core { 18namespace Core {
@@ -37,7 +38,7 @@ class nvdevice;
37/// Represents an Nvidia event 38/// Represents an Nvidia event
38struct NvEvent { 39struct NvEvent {
39 Kernel::KEvent* event{}; 40 Kernel::KEvent* event{};
40 Fence fence{}; 41 NvFence fence{};
41}; 42};
42 43
43struct EventInterface { 44struct EventInterface {
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index c16babe14..8467b50e4 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -26,7 +26,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
26 rb.Push<DeviceFD>(0); 26 rb.Push<DeviceFD>(0);
27 rb.PushEnum(NvResult::NotInitialized); 27 rb.PushEnum(NvResult::NotInitialized);
28 28
29 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 29 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
30 return; 30 return;
31 } 31 }
32 32
@@ -61,7 +61,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
61 61
62 if (!is_initialized) { 62 if (!is_initialized) {
63 ServiceError(ctx, NvResult::NotInitialized); 63 ServiceError(ctx, NvResult::NotInitialized);
64 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 64 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
65 return; 65 return;
66 } 66 }
67 67
@@ -87,7 +87,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
87 87
88 if (!is_initialized) { 88 if (!is_initialized) {
89 ServiceError(ctx, NvResult::NotInitialized); 89 ServiceError(ctx, NvResult::NotInitialized);
90 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 90 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
91 return; 91 return;
92 } 92 }
93 93
@@ -114,7 +114,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
114 114
115 if (!is_initialized) { 115 if (!is_initialized) {
116 ServiceError(ctx, NvResult::NotInitialized); 116 ServiceError(ctx, NvResult::NotInitialized);
117 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 117 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
118 return; 118 return;
119 } 119 }
120 120
@@ -139,7 +139,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
139 139
140 if (!is_initialized) { 140 if (!is_initialized) {
141 ServiceError(ctx, NvResult::NotInitialized); 141 ServiceError(ctx, NvResult::NotInitialized);
142 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 142 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
143 return; 143 return;
144 } 144 }
145 145
@@ -170,7 +170,7 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
170 170
171 if (!is_initialized) { 171 if (!is_initialized) {
172 ServiceError(ctx, NvResult::NotInitialized); 172 ServiceError(ctx, NvResult::NotInitialized);
173 LOG_ERROR(Service_NVDRV, "NvServices is not initalized!"); 173 LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
174 return; 174 return;
175 } 175 }
176 176
@@ -230,7 +230,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
230} 230}
231 231
232NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name) 232NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name)
233 : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} { 233 : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} {
234 static const FunctionInfo functions[] = { 234 static const FunctionInfo functions[] = {
235 {0, &NVDRV::Open, "Open"}, 235 {0, &NVDRV::Open, "Open"},
236 {1, &NVDRV::Ioctl1, "Ioctl"}, 236 {1, &NVDRV::Ioctl1, "Ioctl"},
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
new file mode 100644
index 000000000..7d0d4d819
--- /dev/null
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -0,0 +1,42 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
6
7#pragma once
8
9#include "common/common_types.h"
10
11namespace Kernel {
12class HLERequestContext;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::android {
17
18enum class TransactionId {
19 RequestBuffer = 1,
20 SetBufferCount = 2,
21 DequeueBuffer = 3,
22 DetachBuffer = 4,
23 DetachNextBuffer = 5,
24 AttachBuffer = 6,
25 QueueBuffer = 7,
26 CancelBuffer = 8,
27 Query = 9,
28 Connect = 10,
29 Disconnect = 11,
30 AllocateBuffers = 13,
31 SetPreallocatedBuffer = 14,
32 GetBufferHistory = 17,
33};
34
35class IBinder {
36public:
37 virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
38 u32 flags) = 0;
39 virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
40};
41
42} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item.h b/src/core/hle/service/nvflinger/buffer_item.h
new file mode 100644
index 000000000..64b82b851
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item.h
@@ -0,0 +1,46 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "common/math_util.h"
13#include "core/hle/service/nvflinger/ui/fence.h"
14#include "core/hle/service/nvflinger/window.h"
15
16namespace Service::android {
17
18class GraphicBuffer;
19
20class BufferItem final {
21public:
22 constexpr BufferItem() = default;
23
24 std::shared_ptr<GraphicBuffer> graphic_buffer;
25 Fence fence;
26 Common::Rectangle<s32> crop;
27 NativeWindowTransform transform{};
28 u32 scaling_mode{};
29 s64 timestamp{};
30 bool is_auto_timestamp{};
31 u64 frame_number{};
32
33 // The default value for buf, used to indicate this doesn't correspond to a slot.
34 static constexpr s32 INVALID_BUFFER_SLOT = -1;
35 union {
36 s32 slot{INVALID_BUFFER_SLOT};
37 s32 buf;
38 };
39
40 bool is_droppable{};
41 bool acquire_called{};
42 bool transform_to_display_inverse{};
43 s32 swap_interval{};
44};
45
46} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.cpp b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
new file mode 100644
index 000000000..d7ee5362b
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
@@ -0,0 +1,59 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hle/service/nvflinger/buffer_item.h"
10#include "core/hle/service/nvflinger/buffer_item_consumer.h"
11#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
12
13namespace Service::android {
14
15BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
16 : ConsumerBase{std::move(consumer_)} {}
17
18Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
19 bool wait_for_fence) {
20 if (!item) {
21 return Status::BadValue;
22 }
23
24 std::scoped_lock lock{mutex};
25
26 if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
27 if (status != Status::NoBufferAvailable) {
28 LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
29 }
30 return status;
31 }
32
33 if (wait_for_fence) {
34 UNIMPLEMENTED();
35 }
36
37 item->graphic_buffer = slots[item->slot].graphic_buffer;
38
39 return Status::NoError;
40}
41
42Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
43 std::scoped_lock lock{mutex};
44
45 if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
46 status != Status::NoError) {
47 LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
48 }
49
50 if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
51 status != Status::NoError) {
52 LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
53 return status;
54 }
55
56 return Status::NoError;
57}
58
59} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_item_consumer.h b/src/core/hle/service/nvflinger/buffer_item_consumer.h
new file mode 100644
index 000000000..536db81e2
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
6
7#pragma once
8
9#include <chrono>
10#include <memory>
11
12#include "common/common_types.h"
13#include "core/hle/service/nvflinger/consumer_base.h"
14#include "core/hle/service/nvflinger/status.h"
15
16namespace Service::android {
17
18class BufferItem;
19
20class BufferItemConsumer final : public ConsumerBase {
21public:
22 explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
23 Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
24 bool wait_for_fence = true);
25 Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
26};
27
28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
deleted file mode 100644
index 5fead6d1b..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/core.h"
10#include "core/hle/kernel/k_writable_event.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/service/kernel_helpers.h"
13#include "core/hle/service/nvflinger/buffer_queue.h"
14
15namespace Service::NVFlinger {
16
17BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
18 KernelHelpers::ServiceContext& service_context_)
19 : id(id_), layer_id(layer_id_), service_context{service_context_} {
20 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
21}
22
23BufferQueue::~BufferQueue() {
24 service_context.CloseEvent(buffer_wait_event);
25}
26
27void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
28 ASSERT(slot < buffer_slots);
29 LOG_WARNING(Service, "Adding graphics buffer {}", slot);
30
31 {
32 std::unique_lock lock{free_buffers_mutex};
33 free_buffers.push_back(slot);
34 }
35 free_buffers_condition.notify_one();
36
37 buffers[slot] = {
38 .slot = slot,
39 .status = Buffer::Status::Free,
40 .igbp_buffer = igbp_buffer,
41 .transform = {},
42 .crop_rect = {},
43 .swap_interval = 0,
44 .multi_fence = {},
45 };
46
47 buffer_wait_event->GetWritableEvent().Signal();
48}
49
50std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
51 u32 height) {
52 // Wait for first request before trying to dequeue
53 {
54 std::unique_lock lock{free_buffers_mutex};
55 free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
56 }
57
58 if (!is_connect) {
59 // Buffer was disconnected while the thread was blocked, this is most likely due to
60 // emulation being stopped
61 return std::nullopt;
62 }
63
64 std::unique_lock lock{free_buffers_mutex};
65
66 auto f_itr = free_buffers.begin();
67 auto slot = buffers.size();
68
69 while (f_itr != free_buffers.end()) {
70 const Buffer& buffer = buffers[*f_itr];
71 if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
72 buffer.igbp_buffer.height == height) {
73 slot = *f_itr;
74 free_buffers.erase(f_itr);
75 break;
76 }
77 ++f_itr;
78 }
79 if (slot == buffers.size()) {
80 return std::nullopt;
81 }
82 buffers[slot].status = Buffer::Status::Dequeued;
83 return {{buffers[slot].slot, &buffers[slot].multi_fence}};
84}
85
86const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
87 ASSERT(slot < buffers.size());
88 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
89 ASSERT(buffers[slot].slot == slot);
90
91 return buffers[slot].igbp_buffer;
92}
93
94void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
95 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
96 Service::Nvidia::MultiFence& multi_fence) {
97 ASSERT(slot < buffers.size());
98 ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
99 ASSERT(buffers[slot].slot == slot);
100
101 buffers[slot].status = Buffer::Status::Queued;
102 buffers[slot].transform = transform;
103 buffers[slot].crop_rect = crop_rect;
104 buffers[slot].swap_interval = swap_interval;
105 buffers[slot].multi_fence = multi_fence;
106 std::unique_lock lock{queue_sequence_mutex};
107 queue_sequence.push_back(slot);
108}
109
110void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
111 ASSERT(slot < buffers.size());
112 ASSERT(buffers[slot].status != Buffer::Status::Free);
113 ASSERT(buffers[slot].slot == slot);
114
115 buffers[slot].status = Buffer::Status::Free;
116 buffers[slot].multi_fence = multi_fence;
117 buffers[slot].swap_interval = 0;
118
119 {
120 std::unique_lock lock{free_buffers_mutex};
121 free_buffers.push_back(slot);
122 }
123 free_buffers_condition.notify_one();
124
125 buffer_wait_event->GetWritableEvent().Signal();
126}
127
128std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
129 std::unique_lock lock{queue_sequence_mutex};
130 std::size_t buffer_slot = buffers.size();
131 // Iterate to find a queued buffer matching the requested slot.
132 while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
133 const auto slot = static_cast<std::size_t>(queue_sequence.front());
134 ASSERT(slot < buffers.size());
135 if (buffers[slot].status == Buffer::Status::Queued) {
136 ASSERT(buffers[slot].slot == slot);
137 buffer_slot = slot;
138 }
139 queue_sequence.pop_front();
140 }
141 if (buffer_slot == buffers.size()) {
142 return std::nullopt;
143 }
144 buffers[buffer_slot].status = Buffer::Status::Acquired;
145 return {{buffers[buffer_slot]}};
146}
147
148void BufferQueue::ReleaseBuffer(u32 slot) {
149 ASSERT(slot < buffers.size());
150 ASSERT(buffers[slot].status == Buffer::Status::Acquired);
151 ASSERT(buffers[slot].slot == slot);
152
153 buffers[slot].status = Buffer::Status::Free;
154 {
155 std::unique_lock lock{free_buffers_mutex};
156 free_buffers.push_back(slot);
157 }
158 free_buffers_condition.notify_one();
159
160 buffer_wait_event->GetWritableEvent().Signal();
161}
162
163void BufferQueue::Connect() {
164 std::unique_lock lock{queue_sequence_mutex};
165 queue_sequence.clear();
166 is_connect = true;
167}
168
169void BufferQueue::Disconnect() {
170 buffers.fill({});
171 {
172 std::unique_lock lock{queue_sequence_mutex};
173 queue_sequence.clear();
174 }
175 buffer_wait_event->GetWritableEvent().Signal();
176 is_connect = false;
177 free_buffers_condition.notify_one();
178}
179
180u32 BufferQueue::Query(QueryType type) {
181 LOG_WARNING(Service, "(STUBBED) called type={}", type);
182
183 switch (type) {
184 case QueryType::NativeWindowFormat:
185 return static_cast<u32>(PixelFormat::RGBA8888);
186 case QueryType::NativeWindowWidth:
187 case QueryType::NativeWindowHeight:
188 break;
189 case QueryType::NativeWindowMinUndequeuedBuffers:
190 return 0;
191 case QueryType::NativeWindowConsumerUsageBits:
192 return 0;
193 }
194 UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
195 return 0;
196}
197
198Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
199 return buffer_wait_event->GetWritableEvent();
200}
201
202Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
203 return buffer_wait_event->GetReadableEvent();
204}
205
206} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
deleted file mode 100644
index f2a579133..000000000
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ /dev/null
@@ -1,154 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <condition_variable>
8#include <list>
9#include <mutex>
10#include <optional>
11
12#include "common/common_funcs.h"
13#include "common/math_util.h"
14#include "common/swap.h"
15#include "core/hle/kernel/k_event.h"
16#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/nvdata.h"
18
19namespace Kernel {
20class KernelCore;
21class KEvent;
22class KReadableEvent;
23class KWritableEvent;
24} // namespace Kernel
25
26namespace Service::KernelHelpers {
27class ServiceContext;
28} // namespace Service::KernelHelpers
29
30namespace Service::NVFlinger {
31
32constexpr u32 buffer_slots = 0x40;
33struct IGBPBuffer {
34 u32_le magic;
35 u32_le width;
36 u32_le height;
37 u32_le stride;
38 u32_le format;
39 u32_le usage;
40 INSERT_PADDING_WORDS(1);
41 u32_le index;
42 INSERT_PADDING_WORDS(3);
43 u32_le gpu_buffer_id;
44 INSERT_PADDING_WORDS(6);
45 u32_le external_format;
46 INSERT_PADDING_WORDS(10);
47 u32_le nvmap_handle;
48 u32_le offset;
49 INSERT_PADDING_WORDS(60);
50};
51
52static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
53
54class BufferQueue final {
55public:
56 enum class QueryType {
57 NativeWindowWidth = 0,
58 NativeWindowHeight = 1,
59 NativeWindowFormat = 2,
60 /// The minimum number of buffers that must remain un-dequeued after a buffer has been
61 /// queued
62 NativeWindowMinUndequeuedBuffers = 3,
63 /// The consumer gralloc usage bits currently set by the consumer
64 NativeWindowConsumerUsageBits = 10,
65 };
66
67 explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
68 KernelHelpers::ServiceContext& service_context_);
69 ~BufferQueue();
70
71 enum class BufferTransformFlags : u32 {
72 /// No transform flags are set
73 Unset = 0x00,
74 /// Flip source image horizontally (around the vertical axis)
75 FlipH = 0x01,
76 /// Flip source image vertically (around the horizontal axis)
77 FlipV = 0x02,
78 /// Rotate source image 90 degrees clockwise
79 Rotate90 = 0x04,
80 /// Rotate source image 180 degrees
81 Rotate180 = 0x03,
82 /// Rotate source image 270 degrees clockwise
83 Rotate270 = 0x07,
84 };
85
86 enum class PixelFormat : u32 {
87 RGBA8888 = 1,
88 RGBX8888 = 2,
89 RGB888 = 3,
90 RGB565 = 4,
91 BGRA8888 = 5,
92 RGBA5551 = 6,
93 RRGBA4444 = 7,
94 };
95
96 struct Buffer {
97 enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
98
99 u32 slot;
100 Status status = Status::Free;
101 IGBPBuffer igbp_buffer;
102 BufferTransformFlags transform;
103 Common::Rectangle<int> crop_rect;
104 u32 swap_interval;
105 Service::Nvidia::MultiFence multi_fence;
106 };
107
108 void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
109 std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
110 u32 height);
111 const IGBPBuffer& RequestBuffer(u32 slot) const;
112 void QueueBuffer(u32 slot, BufferTransformFlags transform,
113 const Common::Rectangle<int>& crop_rect, u32 swap_interval,
114 Service::Nvidia::MultiFence& multi_fence);
115 void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
116 std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
117 void ReleaseBuffer(u32 slot);
118 void Connect();
119 void Disconnect();
120 u32 Query(QueryType type);
121
122 u32 GetId() const {
123 return id;
124 }
125
126 bool IsConnected() const {
127 return is_connect;
128 }
129
130 Kernel::KWritableEvent& GetWritableBufferWaitEvent();
131
132 Kernel::KReadableEvent& GetBufferWaitEvent();
133
134private:
135 BufferQueue(const BufferQueue&) = delete;
136
137 u32 id{};
138 u64 layer_id{};
139 std::atomic_bool is_connect{};
140
141 std::list<u32> free_buffers;
142 std::array<Buffer, buffer_slots> buffers;
143 std::list<u32> queue_sequence;
144 Kernel::KEvent* buffer_wait_event{};
145
146 std::mutex free_buffers_mutex;
147 std::condition_variable free_buffers_condition;
148
149 std::mutex queue_sequence_mutex;
150
151 KernelHelpers::ServiceContext& service_context;
152};
153
154} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
new file mode 100644
index 000000000..3ab9a8c05
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -0,0 +1,231 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
6
7#include "common/logging/log.h"
8#include "core/hle/service/nvflinger/buffer_item.h"
9#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
10#include "core/hle/service/nvflinger/buffer_queue_core.h"
11#include "core/hle/service/nvflinger/producer_listener.h"
12
13namespace Service::android {
14
15BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_)
16 : core{std::move(core_)}, slots{core->slots} {}
17
18BufferQueueConsumer::~BufferQueueConsumer() = default;
19
20Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
21 std::chrono::nanoseconds expected_present) {
22 std::scoped_lock lock{core->mutex};
23
24 // Check that the consumer doesn't currently have the maximum number of buffers acquired.
25 const s32 num_acquired_buffers{
26 static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
27 return slot.buffer_state == BufferState::Acquired;
28 }))};
29
30 if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
31 LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
32 num_acquired_buffers, core->max_acquired_buffer_count);
33 return Status::InvalidOperation;
34 }
35
36 // Check if the queue is empty.
37 if (core->queue.empty()) {
38 return Status::NoBufferAvailable;
39 }
40
41 auto front(core->queue.begin());
42
43 // If expected_present is specified, we may not want to return a buffer yet.
44 if (expected_present.count() != 0) {
45 constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
46
47 // The expected_present argument indicates when the buffer is expected to be presented
48 // on-screen.
49 while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
50 const auto& buffer_item{core->queue[1]};
51
52 // If entry[1] is timely, drop entry[0] (and repeat).
53 const auto desired_present = buffer_item.timestamp;
54 if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
55 desired_present > expected_present.count()) {
56 // This buffer is set to display in the near future, or desired_present is garbage.
57 LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
58 expected_present.count());
59 break;
60 }
61
62 LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
63 expected_present.count(), core->queue.size());
64
65 if (core->StillTracking(*front)) {
66 // Front buffer is still in mSlots, so mark the slot as free
67 slots[front->slot].buffer_state = BufferState::Free;
68 }
69
70 core->queue.erase(front);
71 front = core->queue.begin();
72 }
73
74 // See if the front buffer is ready to be acquired.
75 const auto desired_present = front->timestamp;
76 if (desired_present > expected_present.count() &&
77 desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
78 LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
79 expected_present.count());
80 return Status::PresentLater;
81 }
82
83 LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
84 expected_present.count());
85 }
86
87 const auto slot = front->slot;
88 *out_buffer = *front;
89
90 LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
91
92 // If the front buffer is still being tracked, update its slot state
93 if (core->StillTracking(*front)) {
94 slots[slot].acquire_called = true;
95 slots[slot].needs_cleanup_on_release = false;
96 slots[slot].buffer_state = BufferState::Acquired;
97 slots[slot].fence = Fence::NoFence();
98 }
99
100 // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
101 // avoid unnecessarily remapping this buffer on the consumer side.
102 if (out_buffer->acquire_called) {
103 out_buffer->graphic_buffer = nullptr;
104 }
105
106 core->queue.erase(front);
107
108 // We might have freed a slot while dropping old buffers, or the producer may be blocked
109 // waiting for the number of buffers in the queue to decrease.
110 core->SignalDequeueCondition();
111
112 return Status::NoError;
113}
114
115Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
116 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
117 LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
118 return Status::BadValue;
119 }
120
121 std::shared_ptr<IProducerListener> listener;
122 {
123 std::scoped_lock lock{core->mutex};
124
125 // If the frame number has changed because the buffer has been reallocated, we can ignore
126 // this ReleaseBuffer for the old buffer.
127 if (frame_number != slots[slot].frame_number) {
128 return Status::StaleBufferSlot;
129 }
130
131 // Make sure this buffer hasn't been queued while acquired by the consumer.
132 auto current(core->queue.begin());
133 while (current != core->queue.end()) {
134 if (current->slot == slot) {
135 LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
136 slot);
137 return Status::BadValue;
138 }
139 ++current;
140 }
141
142 if (slots[slot].buffer_state == BufferState::Acquired) {
143 slots[slot].fence = release_fence;
144 slots[slot].buffer_state = BufferState::Free;
145
146 listener = core->connected_producer_listener;
147
148 LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
149 } else if (slots[slot].needs_cleanup_on_release) {
150 LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot,
151 slots[slot].buffer_state);
152
153 slots[slot].needs_cleanup_on_release = false;
154
155 return Status::StaleBufferSlot;
156 } else {
157 LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}",
158 slot, slots[slot].buffer_state);
159
160 return Status::BadValue;
161 }
162
163 core->SignalDequeueCondition();
164 }
165
166 // Call back without lock held
167 if (listener != nullptr) {
168 listener->OnBufferReleased();
169 }
170
171 return Status::NoError;
172}
173
174Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
175 bool controlled_by_app) {
176 if (consumer_listener == nullptr) {
177 LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
178 return Status::BadValue;
179 }
180
181 LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
182
183 std::scoped_lock lock{core->mutex};
184
185 if (core->is_abandoned) {
186 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
187 return Status::NoInit;
188 }
189
190 core->consumer_listener = consumer_listener;
191 core->consumer_controlled_by_app = controlled_by_app;
192
193 return Status::NoError;
194}
195
196Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
197 if (out_slot_mask == nullptr) {
198 LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
199 return Status::BadValue;
200 }
201
202 std::scoped_lock lock{core->mutex};
203
204 if (core->is_abandoned) {
205 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
206 return Status::NoInit;
207 }
208
209 u64 mask = 0;
210 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
211 if (!slots[s].acquire_called) {
212 mask |= (1ULL << s);
213 }
214 }
215
216 // Remove from the mask queued buffers for which acquire has been called, since the consumer
217 // will not receive their buffer addresses and so must retain their cached information
218 auto current(core->queue.begin());
219 while (current != core->queue.end()) {
220 if (current->acquire_called) {
221 mask &= ~(1ULL << current->slot);
222 }
223 ++current;
224 }
225
226 LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
227 *out_slot_mask = mask;
228 return Status::NoError;
229}
230
231} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.h b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
new file mode 100644
index 000000000..8a047fe06
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -0,0 +1,37 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
6
7#pragma once
8
9#include <chrono>
10#include <memory>
11
12#include "common/common_types.h"
13#include "core/hle/service/nvflinger/buffer_queue_defs.h"
14#include "core/hle/service/nvflinger/status.h"
15
16namespace Service::android {
17
18class BufferItem;
19class BufferQueueCore;
20class IConsumerListener;
21
22class BufferQueueConsumer final {
23public:
24 explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_);
25 ~BufferQueueConsumer();
26
27 Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
28 Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
29 Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
30 Status GetReleasedBuffers(u64* out_slot_mask);
31
32private:
33 std::shared_ptr<BufferQueueCore> core;
34 BufferQueueDefs::SlotsType& slots;
35};
36
37} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
new file mode 100644
index 000000000..ec5aabaeb
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -0,0 +1,117 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp
6
7#include "common/assert.h"
8
9#include "core/hle/service/nvflinger/buffer_queue_core.h"
10
11namespace Service::android {
12
13BufferQueueCore::BufferQueueCore() = default;
14
15BufferQueueCore::~BufferQueueCore() = default;
16
17void BufferQueueCore::NotifyShutdown() {
18 std::scoped_lock lock{mutex};
19
20 is_shutting_down = true;
21
22 SignalDequeueCondition();
23}
24
25void BufferQueueCore::SignalDequeueCondition() {
26 dequeue_condition.notify_all();
27}
28
29bool BufferQueueCore::WaitForDequeueCondition() {
30 if (is_shutting_down) {
31 return false;
32 }
33
34 dequeue_condition.wait(mutex);
35
36 return true;
37}
38
39s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
40 // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
41 if (!use_async_buffer) {
42 return max_acquired_buffer_count;
43 }
44
45 if (dequeue_buffer_cannot_block || async) {
46 return max_acquired_buffer_count + 1;
47 }
48
49 return max_acquired_buffer_count;
50}
51
52s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
53 return GetMinUndequeuedBufferCountLocked(async) + 1;
54}
55
56s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
57 const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
58 auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
59
60 if (override_max_buffer_count != 0) {
61 ASSERT(override_max_buffer_count >= min_buffer_count);
62 max_buffer_count = override_max_buffer_count;
63 }
64
65 // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
66 // need to have their slots preserved.
67 for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
68 const auto state = slots[slot].buffer_state;
69 if (state == BufferState::Queued || state == BufferState::Dequeued) {
70 max_buffer_count = slot + 1;
71 }
72 }
73
74 return max_buffer_count;
75}
76
77s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
78 return static_cast<s32>(std::count_if(slots.begin(), slots.end(),
79 [](const auto& slot) { return slot.is_preallocated; }));
80}
81
82void BufferQueueCore::FreeBufferLocked(s32 slot) {
83 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
84
85 slots[slot].graphic_buffer.reset();
86
87 if (slots[slot].buffer_state == BufferState::Acquired) {
88 slots[slot].needs_cleanup_on_release = true;
89 }
90
91 slots[slot].buffer_state = BufferState::Free;
92 slots[slot].frame_number = UINT32_MAX;
93 slots[slot].acquire_called = false;
94 slots[slot].fence = Fence::NoFence();
95}
96
97void BufferQueueCore::FreeAllBuffersLocked() {
98 buffer_has_been_queued = false;
99
100 for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
101 FreeBufferLocked(slot);
102 }
103}
104
105bool BufferQueueCore::StillTracking(const BufferItem& item) const {
106 const BufferSlot& slot = slots[item.slot];
107
108 return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer);
109}
110
111void BufferQueueCore::WaitWhileAllocatingLocked() const {
112 while (is_allocating) {
113 is_allocating_condition.wait(mutex);
114 }
115}
116
117} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h
new file mode 100644
index 000000000..e4e0937cb
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -0,0 +1,79 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h
6
7#pragma once
8
9#include <condition_variable>
10#include <list>
11#include <memory>
12#include <mutex>
13#include <set>
14#include <vector>
15
16#include "core/hle/service/nvflinger/buffer_item.h"
17#include "core/hle/service/nvflinger/buffer_queue_defs.h"
18#include "core/hle/service/nvflinger/pixel_format.h"
19#include "core/hle/service/nvflinger/status.h"
20#include "core/hle/service/nvflinger/window.h"
21
22namespace Service::android {
23
24class IConsumerListener;
25class IProducerListener;
26
27class BufferQueueCore final {
28 friend class BufferQueueProducer;
29 friend class BufferQueueConsumer;
30
31public:
32 static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
33
34 BufferQueueCore();
35 ~BufferQueueCore();
36
37 void NotifyShutdown();
38
39private:
40 void SignalDequeueCondition();
41 bool WaitForDequeueCondition();
42
43 s32 GetMinUndequeuedBufferCountLocked(bool async) const;
44 s32 GetMinMaxBufferCountLocked(bool async) const;
45 s32 GetMaxBufferCountLocked(bool async) const;
46 s32 GetPreallocatedBufferCountLocked() const;
47 void FreeBufferLocked(s32 slot);
48 void FreeAllBuffersLocked();
49 bool StillTracking(const BufferItem& item) const;
50 void WaitWhileAllocatingLocked() const;
51
52private:
53 mutable std::mutex mutex;
54 bool is_abandoned{};
55 bool consumer_controlled_by_app{};
56 std::shared_ptr<IConsumerListener> consumer_listener;
57 u32 consumer_usage_bit{};
58 NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi};
59 std::shared_ptr<IProducerListener> connected_producer_listener;
60 BufferQueueDefs::SlotsType slots{};
61 std::vector<BufferItem> queue;
62 s32 override_max_buffer_count{};
63 mutable std::condition_variable_any dequeue_condition;
64 const bool use_async_buffer{}; // This is always disabled on HOS
65 bool dequeue_buffer_cannot_block{};
66 PixelFormat default_buffer_format{PixelFormat::Rgba8888};
67 u32 default_width{1};
68 u32 default_height{1};
69 s32 default_max_buffer_count{2};
70 const s32 max_acquired_buffer_count{}; // This is always zero on HOS
71 bool buffer_has_been_queued{};
72 u64 frame_counter{};
73 u32 transform_hint{};
74 bool is_allocating{};
75 mutable std::condition_variable_any is_allocating_condition;
76 bool is_shutting_down{};
77};
78
79} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_defs.h b/src/core/hle/service/nvflinger/buffer_queue_defs.h
new file mode 100644
index 000000000..387d3d36a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_defs.h
@@ -0,0 +1,21 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h
6
7#pragma once
8
9#include <array>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/buffer_slot.h"
13
14namespace Service::android::BufferQueueDefs {
15
16// BufferQueue will keep track of at most this value of buffers.
17constexpr s32 NUM_BUFFER_SLOTS = 64;
18
19using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>;
20
21} // namespace Service::android::BufferQueueDefs
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
new file mode 100644
index 000000000..6f604a88e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -0,0 +1,923 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h"
11#include "core/hle/kernel/hle_ipc.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/service/kernel_helpers.h"
17#include "core/hle/service/nvdrv/nvdrv.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/consumer_listener.h"
21#include "core/hle/service/nvflinger/parcel.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
23#include "core/hle/service/nvflinger/window.h"
24#include "core/hle/service/vi/vi.h"
25
26namespace Service::android {
27
28BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
29 std::shared_ptr<BufferQueueCore> buffer_queue_core_)
30 : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots) {
31 buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
32}
33
34BufferQueueProducer::~BufferQueueProducer() {
35 service_context.CloseEvent(buffer_wait_event);
36}
37
38Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
39 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
40
41 std::scoped_lock lock{core->mutex};
42
43 if (core->is_abandoned) {
44 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
45 return Status::NoInit;
46 }
47 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
48 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
49 BufferQueueDefs::NUM_BUFFER_SLOTS);
50 return Status::BadValue;
51 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
52 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
53 slots[slot].buffer_state);
54 return Status::BadValue;
55 }
56
57 slots[slot].request_buffer_called = true;
58 *buf = slots[slot].graphic_buffer;
59
60 return Status::NoError;
61}
62
63Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
64 LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
65
66 std::shared_ptr<IConsumerListener> listener;
67 {
68 std::scoped_lock lock{core->mutex};
69 core->WaitWhileAllocatingLocked();
70
71 if (core->is_abandoned) {
72 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
73 return Status::NoInit;
74 }
75
76 if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) {
77 LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count,
78 BufferQueueDefs::NUM_BUFFER_SLOTS);
79 return Status::BadValue;
80 }
81
82 // There must be no dequeued buffers when changing the buffer count.
83 for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
84 if (slots[s].buffer_state == BufferState::Dequeued) {
85 LOG_ERROR(Service_NVFlinger, "buffer owned by producer");
86 return Status::BadValue;
87 }
88 }
89
90 if (buffer_count == 0) {
91 core->override_max_buffer_count = 0;
92 core->SignalDequeueCondition();
93 return Status::NoError;
94 }
95
96 const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false);
97 if (buffer_count < min_buffer_slots) {
98 LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}",
99 buffer_count, min_buffer_slots);
100 return Status::BadValue;
101 }
102
103 // Here we are guaranteed that the producer doesn't have any dequeued buffers and will
104 // release all of its buffer references.
105 if (core->GetPreallocatedBufferCountLocked() <= 0) {
106 core->FreeAllBuffersLocked();
107 }
108
109 core->override_max_buffer_count = buffer_count;
110 core->SignalDequeueCondition();
111 buffer_wait_event->GetWritableEvent().Signal();
112 listener = core->consumer_listener;
113 }
114
115 // Call back without lock held
116 if (listener != nullptr) {
117 listener->OnBuffersReleased();
118 }
119
120 return Status::NoError;
121}
122
123Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
124 Status* return_flags) const {
125 bool try_again = true;
126
127 while (try_again) {
128 if (core->is_abandoned) {
129 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
130 return Status::NoInit;
131 }
132
133 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
134 if (async && core->override_max_buffer_count) {
135 if (core->override_max_buffer_count < max_buffer_count) {
136 LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override");
137 return Status::BadValue;
138 }
139 }
140
141 // Free up any buffers that are in slots beyond the max buffer count
142 for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
143 ASSERT(slots[s].buffer_state == BufferState::Free);
144 if (slots[s].graphic_buffer != nullptr) {
145 core->FreeBufferLocked(s);
146 *return_flags |= Status::ReleaseAllBuffers;
147 }
148 }
149
150 // Look for a free buffer to give to the client
151 *found = BufferQueueCore::INVALID_BUFFER_SLOT;
152 s32 dequeued_count{};
153 s32 acquired_count{};
154 for (s32 s{}; s < max_buffer_count; ++s) {
155 switch (slots[s].buffer_state) {
156 case BufferState::Dequeued:
157 ++dequeued_count;
158 break;
159 case BufferState::Acquired:
160 ++acquired_count;
161 break;
162 case BufferState::Free:
163 // We return the oldest of the free buffers to avoid stalling the producer if
164 // possible, since the consumer may still have pending reads of in-flight buffers
165 if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
166 slots[s].frame_number < slots[*found].frame_number) {
167 *found = s;
168 }
169 break;
170 default:
171 break;
172 }
173 }
174
175 // Producers are not allowed to dequeue more than one buffer if they did not set a buffer
176 // count
177 if (!core->override_max_buffer_count && dequeued_count) {
178 LOG_ERROR(Service_NVFlinger,
179 "can't dequeue multiple buffers without setting the buffer count");
180 return Status::InvalidOperation;
181 }
182
183 // See whether a buffer has been queued since the last SetBufferCount so we know whether to
184 // perform the min undequeued buffers check below
185 if (core->buffer_has_been_queued) {
186 // Make sure the producer is not trying to dequeue more buffers than allowed
187 const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1);
188 const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async);
189 if (new_undequeued_count < min_undequeued_count) {
190 LOG_ERROR(Service_NVFlinger,
191 "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})",
192 min_undequeued_count, dequeued_count, new_undequeued_count);
193 return Status::InvalidOperation;
194 }
195 }
196
197 // If we disconnect and reconnect quickly, we can be in a state where our slots are empty
198 // but we have many buffers in the queue. This can cause us to run out of memory if we
199 // outrun the consumer. Wait here if it looks like we have too many buffers queued up.
200 const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
201 if (too_many_buffers) {
202 LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
203 }
204
205 // If no buffer is found, or if the queue has too many buffers outstanding, wait for a
206 // buffer to be acquired or released, or for the max buffer count to change.
207 try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers;
208 if (try_again) {
209 // Return an error if we're in non-blocking mode (producer and consumer are controlled
210 // by the application).
211 if (core->dequeue_buffer_cannot_block &&
212 (acquired_count <= core->max_acquired_buffer_count)) {
213 return Status::WouldBlock;
214 }
215
216 if (!core->WaitForDequeueCondition()) {
217 // We are no longer running
218 return Status::NoError;
219 }
220 }
221 }
222
223 return Status::NoError;
224}
225
226Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width,
227 u32 height, PixelFormat format, u32 usage) {
228 LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false",
229 width, height, format, usage);
230
231 if ((width != 0 && height == 0) || (width == 0 && height != 0)) {
232 LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height);
233 return Status::BadValue;
234 }
235
236 Status return_flags = Status::NoError;
237 bool attached_by_consumer = false;
238 {
239 std::scoped_lock lock{core->mutex};
240 core->WaitWhileAllocatingLocked();
241
242 if (format == PixelFormat::NoFormat) {
243 format = core->default_buffer_format;
244 }
245
246 // Enable the usage bits the consumer requested
247 usage |= core->consumer_usage_bit;
248
249 s32 found{};
250 Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
251 if (status != Status::NoError) {
252 return status;
253 }
254
255 // This should not happen
256 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
257 LOG_ERROR(Service_NVFlinger, "no available buffer slots");
258 return Status::Busy;
259 }
260
261 *out_slot = found;
262
263 attached_by_consumer = slots[found].attached_by_consumer;
264
265 const bool use_default_size = !width && !height;
266 if (use_default_size) {
267 width = core->default_width;
268 height = core->default_height;
269 }
270
271 slots[found].buffer_state = BufferState::Dequeued;
272
273 const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
274 if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) ||
275 (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) {
276 slots[found].acquire_called = false;
277 slots[found].graphic_buffer = nullptr;
278 slots[found].request_buffer_called = false;
279 slots[found].fence = Fence::NoFence();
280
281 return_flags |= Status::BufferNeedsReallocation;
282 }
283
284 *out_fence = slots[found].fence;
285 slots[found].fence = Fence::NoFence();
286 }
287
288 if ((return_flags & Status::BufferNeedsReallocation) != Status::None) {
289 LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot);
290
291 auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage);
292 if (graphic_buffer == nullptr) {
293 LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed");
294 return Status::NoMemory;
295 }
296
297 {
298 std::scoped_lock lock{core->mutex};
299
300 if (core->is_abandoned) {
301 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
302 return Status::NoInit;
303 }
304
305 slots[*out_slot].frame_number = UINT32_MAX;
306 slots[*out_slot].graphic_buffer = graphic_buffer;
307 }
308 }
309
310 if (attached_by_consumer) {
311 return_flags |= Status::BufferNeedsReallocation;
312 }
313
314 LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
315 slots[*out_slot].frame_number, return_flags);
316
317 return return_flags;
318}
319
320Status BufferQueueProducer::DetachBuffer(s32 slot) {
321 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
322
323 std::scoped_lock lock{core->mutex};
324
325 if (core->is_abandoned) {
326 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
327 return Status::NoInit;
328 }
329
330 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
331 LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot,
332 BufferQueueDefs::NUM_BUFFER_SLOTS);
333 return Status::BadValue;
334 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
335 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
336 slots[slot].buffer_state);
337 return Status::BadValue;
338 } else if (!slots[slot].request_buffer_called) {
339 LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot);
340 return Status::BadValue;
341 }
342
343 core->FreeBufferLocked(slot);
344 core->SignalDequeueCondition();
345
346 return Status::NoError;
347}
348
349Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer,
350 Fence* out_fence) {
351 if (out_buffer == nullptr) {
352 LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr");
353 return Status::BadValue;
354 } else if (out_fence == nullptr) {
355 LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr");
356 return Status::BadValue;
357 }
358
359 std::scoped_lock lock{core->mutex};
360 core->WaitWhileAllocatingLocked();
361
362 if (core->is_abandoned) {
363 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
364 return Status::NoInit;
365 }
366
367 // Find the oldest valid slot
368 int found = BufferQueueCore::INVALID_BUFFER_SLOT;
369 for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
370 if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) {
371 if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
372 slots[s].frame_number < slots[found].frame_number) {
373 found = s;
374 }
375 }
376 }
377
378 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
379 return Status::NoMemory;
380 }
381
382 LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
383
384 *out_buffer = slots[found].graphic_buffer;
385 *out_fence = slots[found].fence;
386
387 core->FreeBufferLocked(found);
388
389 return Status::NoError;
390}
391
392Status BufferQueueProducer::AttachBuffer(s32* out_slot,
393 const std::shared_ptr<GraphicBuffer>& buffer) {
394 if (out_slot == nullptr) {
395 LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr");
396 return Status::BadValue;
397 } else if (buffer == nullptr) {
398 LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer");
399 return Status::BadValue;
400 }
401
402 std::scoped_lock lock{core->mutex};
403 core->WaitWhileAllocatingLocked();
404
405 Status return_flags = Status::NoError;
406 s32 found{};
407
408 const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
409 if (status != Status::NoError) {
410 return status;
411 }
412
413 // This should not happen
414 if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
415 LOG_ERROR(Service_NVFlinger, "No available buffer slots");
416 return Status::Busy;
417 }
418
419 *out_slot = found;
420
421 LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags);
422
423 slots[*out_slot].graphic_buffer = buffer;
424 slots[*out_slot].buffer_state = BufferState::Dequeued;
425 slots[*out_slot].fence = Fence::NoFence();
426 slots[*out_slot].request_buffer_called = true;
427
428 return return_flags;
429}
430
431Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
432 QueueBufferOutput* output) {
433 s64 timestamp{};
434 bool is_auto_timestamp{};
435 Common::Rectangle<s32> crop;
436 NativeWindowScalingMode scaling_mode{};
437 NativeWindowTransform transform;
438 u32 sticky_transform_{};
439 bool async{};
440 s32 swap_interval{};
441 Fence fence{};
442
443 input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform,
444 &sticky_transform_, &async, &swap_interval, &fence);
445
446 switch (scaling_mode) {
447 case NativeWindowScalingMode::Freeze:
448 case NativeWindowScalingMode::ScaleToWindow:
449 case NativeWindowScalingMode::ScaleCrop:
450 case NativeWindowScalingMode::NoScaleCrop:
451 break;
452 default:
453 LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode);
454 return Status::BadValue;
455 }
456
457 std::shared_ptr<IConsumerListener> frame_available_listener;
458 std::shared_ptr<IConsumerListener> frame_replaced_listener;
459 s32 callback_ticket{};
460 BufferItem item;
461
462 {
463 std::scoped_lock lock{core->mutex};
464
465 if (core->is_abandoned) {
466 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
467 return Status::NoInit;
468 }
469
470 const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
471 if (async && core->override_max_buffer_count) {
472 if (core->override_max_buffer_count < max_buffer_count) {
473 LOG_ERROR(Service_NVFlinger, "async mode is invalid with "
474 "buffer count override");
475 return Status::BadValue;
476 }
477 }
478
479 if (slot < 0 || slot >= max_buffer_count) {
480 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
481 max_buffer_count);
482 return Status::BadValue;
483 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
484 LOG_ERROR(Service_NVFlinger,
485 "slot {} is not owned by the producer "
486 "(state = {})",
487 slot, slots[slot].buffer_state);
488 return Status::BadValue;
489 } else if (!slots[slot].request_buffer_called) {
490 LOG_ERROR(Service_NVFlinger,
491 "slot {} was queued without requesting "
492 "a buffer",
493 slot);
494 return Status::BadValue;
495 }
496
497 LOG_DEBUG(Service_NVFlinger,
498 "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot,
499 core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(),
500 crop.Bottom(), transform, scaling_mode);
501
502 const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer);
503 Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
504 Common::Rectangle<s32> cropped_rect;
505 [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect);
506
507 if (cropped_rect != crop) {
508 LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}",
509 slot);
510 return Status::BadValue;
511 }
512
513 slots[slot].fence = fence;
514 slots[slot].buffer_state = BufferState::Queued;
515 ++core->frame_counter;
516 slots[slot].frame_number = core->frame_counter;
517
518 item.acquire_called = slots[slot].acquire_called;
519 item.graphic_buffer = slots[slot].graphic_buffer;
520 item.crop = crop;
521 item.transform = transform & ~NativeWindowTransform::InverseDisplay;
522 item.transform_to_display_inverse =
523 (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
524 item.scaling_mode = static_cast<u32>(scaling_mode);
525 item.timestamp = timestamp;
526 item.is_auto_timestamp = is_auto_timestamp;
527 item.frame_number = core->frame_counter;
528 item.slot = slot;
529 item.fence = fence;
530 item.is_droppable = core->dequeue_buffer_cannot_block || async;
531 item.swap_interval = swap_interval;
532
533 sticky_transform = sticky_transform_;
534
535 if (core->queue.empty()) {
536 // When the queue is empty, we can simply queue this buffer
537 core->queue.push_back(item);
538 frame_available_listener = core->consumer_listener;
539 } else {
540 // When the queue is not empty, we need to look at the front buffer
541 // state to see if we need to replace it
542 auto front(core->queue.begin());
543
544 if (front->is_droppable) {
545 // If the front queued buffer is still being tracked, we first
546 // mark it as freed
547 if (core->StillTracking(*front)) {
548 slots[front->slot].buffer_state = BufferState::Free;
549 // Reset the frame number of the freed buffer so that it is the first in line to
550 // be dequeued again
551 slots[front->slot].frame_number = 0;
552 }
553 // Overwrite the droppable buffer with the incoming one
554 *front = item;
555 frame_replaced_listener = core->consumer_listener;
556 } else {
557 core->queue.push_back(item);
558 frame_available_listener = core->consumer_listener;
559 }
560 }
561
562 core->buffer_has_been_queued = true;
563 core->SignalDequeueCondition();
564 output->Inflate(core->default_width, core->default_height, core->transform_hint,
565 static_cast<u32>(core->queue.size()));
566
567 // Take a ticket for the callback functions
568 callback_ticket = next_callback_ticket++;
569 }
570
571 // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the
572 // consumer shouldn't need it
573 item.graphic_buffer.reset();
574 item.slot = BufferItem::INVALID_BUFFER_SLOT;
575
576 // Call back without the main BufferQueue lock held, but with the callback lock held so we can
577 // ensure that callbacks occur in order
578 {
579 std::scoped_lock lock{callback_mutex};
580 while (callback_ticket != current_callback_ticket) {
581 callback_condition.wait(callback_mutex);
582 }
583
584 if (frame_available_listener != nullptr) {
585 frame_available_listener->OnFrameAvailable(item);
586 } else if (frame_replaced_listener != nullptr) {
587 frame_replaced_listener->OnFrameReplaced(item);
588 }
589
590 ++current_callback_ticket;
591 callback_condition.notify_all();
592 }
593
594 return Status::NoError;
595}
596
597void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
598 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
599
600 std::scoped_lock lock{core->mutex};
601
602 if (core->is_abandoned) {
603 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
604 return;
605 }
606
607 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
608 LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
609 BufferQueueDefs::NUM_BUFFER_SLOTS);
610 return;
611 } else if (slots[slot].buffer_state != BufferState::Dequeued) {
612 LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
613 slots[slot].buffer_state);
614 return;
615 }
616
617 slots[slot].buffer_state = BufferState::Free;
618 slots[slot].frame_number = 0;
619 slots[slot].fence = fence;
620
621 core->SignalDequeueCondition();
622 buffer_wait_event->GetWritableEvent().Signal();
623}
624
625Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
626 std::scoped_lock lock{core->mutex};
627
628 if (out_value == nullptr) {
629 LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
630 return Status::BadValue;
631 }
632
633 if (core->is_abandoned) {
634 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
635 return Status::NoInit;
636 }
637
638 u32 value{};
639 switch (what) {
640 case NativeWindow::Width:
641 value = core->default_width;
642 break;
643 case NativeWindow::Height:
644 value = core->default_height;
645 break;
646 case NativeWindow::Format:
647 value = static_cast<u32>(core->default_buffer_format);
648 break;
649 case NativeWindow::MinUndequeedBuffers:
650 value = core->GetMinUndequeuedBufferCountLocked(false);
651 break;
652 case NativeWindow::StickyTransform:
653 value = sticky_transform;
654 break;
655 case NativeWindow::ConsumerRunningBehind:
656 value = (core->queue.size() > 1);
657 break;
658 case NativeWindow::ConsumerUsageBits:
659 value = core->consumer_usage_bit;
660 break;
661 default:
662 UNREACHABLE();
663 return Status::BadValue;
664 }
665
666 LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value);
667
668 *out_value = static_cast<s32>(value);
669
670 return Status::NoError;
671}
672
673Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
674 NativeWindowApi api, bool producer_controlled_by_app,
675 QueueBufferOutput* output) {
676 std::scoped_lock lock{core->mutex};
677
678 LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
679 producer_controlled_by_app);
680
681 if (core->is_abandoned) {
682 LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
683 return Status::NoInit;
684 }
685
686 if (core->consumer_listener == nullptr) {
687 LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer");
688 return Status::NoInit;
689 }
690
691 if (output == nullptr) {
692 LOG_ERROR(Service_NVFlinger, "output was nullptr");
693 return Status::BadValue;
694 }
695
696 if (core->connected_api != NativeWindowApi::NoConnectedApi) {
697 LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api,
698 api);
699 return Status::BadValue;
700 }
701
702 Status status = Status::NoError;
703 switch (api) {
704 case NativeWindowApi::Egl:
705 case NativeWindowApi::Cpu:
706 case NativeWindowApi::Media:
707 case NativeWindowApi::Camera:
708 core->connected_api = api;
709 output->Inflate(core->default_width, core->default_height, core->transform_hint,
710 static_cast<u32>(core->queue.size()));
711 core->connected_producer_listener = listener;
712 break;
713 default:
714 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
715 status = Status::BadValue;
716 break;
717 }
718
719 core->buffer_has_been_queued = false;
720 core->dequeue_buffer_cannot_block =
721 core->consumer_controlled_by_app && producer_controlled_by_app;
722
723 return status;
724}
725
726Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
727 LOG_DEBUG(Service_NVFlinger, "api = {}", api);
728
729 Status status = Status::NoError;
730 std::shared_ptr<IConsumerListener> listener;
731
732 {
733 std::scoped_lock lock{core->mutex};
734
735 core->WaitWhileAllocatingLocked();
736
737 if (core->is_abandoned) {
738 // Disconnecting after the surface has been abandoned is a no-op.
739 return Status::NoError;
740 }
741
742 switch (api) {
743 case NativeWindowApi::Egl:
744 case NativeWindowApi::Cpu:
745 case NativeWindowApi::Media:
746 case NativeWindowApi::Camera:
747 if (core->connected_api == api) {
748 core->FreeAllBuffersLocked();
749 core->connected_producer_listener = nullptr;
750 core->connected_api = NativeWindowApi::NoConnectedApi;
751 core->SignalDequeueCondition();
752 buffer_wait_event->GetWritableEvent().Signal();
753 listener = core->consumer_listener;
754 } else {
755 LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
756 core->connected_api, api);
757 status = Status::BadValue;
758 }
759 break;
760 default:
761 LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
762 status = Status::BadValue;
763 break;
764 }
765 }
766
767 // Call back without lock held
768 if (listener != nullptr) {
769 listener->OnBuffersReleased();
770 }
771
772 return status;
773}
774
775Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
776 const std::shared_ptr<GraphicBuffer>& buffer) {
777 LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
778
779 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
780 return Status::BadValue;
781 }
782
783 std::scoped_lock lock{core->mutex};
784
785 slots[slot] = {};
786 slots[slot].graphic_buffer = buffer;
787 slots[slot].frame_number = 0;
788
789 // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
790 // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this.
791 if (buffer) {
792 slots[slot].is_preallocated = true;
793
794 core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked();
795 core->default_width = buffer->Width();
796 core->default_height = buffer->Height();
797 core->default_buffer_format = buffer->Format();
798 }
799
800 core->SignalDequeueCondition();
801 buffer_wait_event->GetWritableEvent().Signal();
802
803 return Status::NoError;
804}
805
806void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
807 Status status{Status::NoError};
808 Parcel parcel_in{ctx.ReadBuffer()};
809 Parcel parcel_out{};
810
811 switch (code) {
812 case TransactionId::Connect: {
813 const auto enable_listener = parcel_in.Read<bool>();
814 const auto api = parcel_in.Read<NativeWindowApi>();
815 const auto producer_controlled_by_app = parcel_in.Read<bool>();
816
817 UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!");
818
819 std::shared_ptr<IProducerListener> listener;
820 QueueBufferOutput output{};
821
822 status = Connect(listener, api, producer_controlled_by_app, &output);
823
824 parcel_out.Write(output);
825 break;
826 }
827 case TransactionId::SetPreallocatedBuffer: {
828 const auto slot = parcel_in.Read<s32>();
829 const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
830
831 status = SetPreallocatedBuffer(slot, buffer);
832 break;
833 }
834 case TransactionId::DequeueBuffer: {
835 const auto is_async = parcel_in.Read<bool>();
836 const auto width = parcel_in.Read<u32>();
837 const auto height = parcel_in.Read<u32>();
838 const auto pixel_format = parcel_in.Read<PixelFormat>();
839 const auto usage = parcel_in.Read<u32>();
840
841 s32 slot{};
842 Fence fence{};
843
844 status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
845
846 parcel_out.Write(slot);
847 parcel_out.WriteObject(&fence);
848 break;
849 }
850 case TransactionId::RequestBuffer: {
851 const auto slot = parcel_in.Read<s32>();
852
853 std::shared_ptr<GraphicBuffer> buf;
854
855 status = RequestBuffer(slot, &buf);
856
857 parcel_out.WriteObject(buf);
858 break;
859 }
860 case TransactionId::QueueBuffer: {
861 const auto slot = parcel_in.Read<s32>();
862
863 QueueBufferInput input{parcel_in};
864 QueueBufferOutput output;
865
866 status = QueueBuffer(slot, input, &output);
867
868 parcel_out.Write(output);
869 break;
870 }
871 case TransactionId::Query: {
872 const auto what = parcel_in.Read<NativeWindow>();
873
874 s32 value{};
875
876 status = Query(what, &value);
877
878 parcel_out.Write(value);
879 break;
880 }
881 case TransactionId::CancelBuffer: {
882 const auto slot = parcel_in.Read<s32>();
883 const auto fence = parcel_in.ReadFlattened<Fence>();
884
885 CancelBuffer(slot, fence);
886 break;
887 }
888 case TransactionId::Disconnect: {
889 const auto api = parcel_in.Read<NativeWindowApi>();
890
891 status = Disconnect(api);
892 break;
893 }
894 case TransactionId::DetachBuffer: {
895 const auto slot = parcel_in.Read<s32>();
896
897 status = DetachBuffer(slot);
898 break;
899 }
900 case TransactionId::SetBufferCount: {
901 const auto buffer_count = parcel_in.Read<s32>();
902
903 status = SetBufferCount(buffer_count);
904 break;
905 }
906 case TransactionId::GetBufferHistory:
907 LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory");
908 break;
909 default:
910 ASSERT_MSG(false, "Unimplemented TransactionId {}", code);
911 break;
912 }
913
914 parcel_out.Write(status);
915
916 ctx.WriteBuffer(parcel_out.Serialize());
917}
918
919Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
920 return buffer_wait_event->GetReadableEvent();
921}
922
923} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h
new file mode 100644
index 000000000..c4ca68fd3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -0,0 +1,83 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h
6
7#pragma once
8
9#include <condition_variable>
10#include <memory>
11#include <mutex>
12
13#include "common/common_funcs.h"
14#include "core/hle/service/nvdrv/nvdata.h"
15#include "core/hle/service/nvflinger/binder.h"
16#include "core/hle/service/nvflinger/buffer_queue_defs.h"
17#include "core/hle/service/nvflinger/buffer_slot.h"
18#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
19#include "core/hle/service/nvflinger/pixel_format.h"
20#include "core/hle/service/nvflinger/status.h"
21#include "core/hle/service/nvflinger/window.h"
22
23namespace Kernel {
24class KernelCore;
25class KEvent;
26class KReadableEvent;
27class KWritableEvent;
28} // namespace Kernel
29
30namespace Service::KernelHelpers {
31class ServiceContext;
32} // namespace Service::KernelHelpers
33
34namespace Service::android {
35
36class BufferQueueCore;
37class IProducerListener;
38
39class BufferQueueProducer final : public IBinder {
40public:
41 explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
42 std::shared_ptr<BufferQueueCore> buffer_queue_core_);
43 ~BufferQueueProducer();
44
45 void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
46
47 Kernel::KReadableEvent& GetNativeHandle() override;
48
49public:
50 Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
51 Status SetBufferCount(s32 buffer_count);
52 Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
53 u32 height, PixelFormat format, u32 usage);
54 Status DetachBuffer(s32 slot);
55 Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
56 Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
57 Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
58 void CancelBuffer(s32 slot, const Fence& fence);
59 Status Query(NativeWindow what, s32* out_value);
60 Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
61 bool producer_controlled_by_app, QueueBufferOutput* output);
62
63 Status Disconnect(NativeWindowApi api);
64 Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
65
66private:
67 BufferQueueProducer(const BufferQueueProducer&) = delete;
68
69 Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
70
71 Kernel::KEvent* buffer_wait_event{};
72 Service::KernelHelpers::ServiceContext& service_context;
73
74 std::shared_ptr<BufferQueueCore> core;
75 BufferQueueDefs::SlotsType& slots;
76 u32 sticky_transform{};
77 std::mutex callback_mutex;
78 s32 next_callback_ticket{};
79 s32 current_callback_ticket{};
80 std::condition_variable_any callback_condition;
81};
82
83} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h
new file mode 100644
index 000000000..e3ea58910
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_slot.h
@@ -0,0 +1,39 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h
6
7#pragma once
8
9#include <memory>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvflinger/ui/fence.h"
13
14namespace Service::android {
15
16class GraphicBuffer;
17
18enum class BufferState : u32 {
19 Free = 0,
20 Dequeued = 1,
21 Queued = 2,
22 Acquired = 3,
23};
24
25struct BufferSlot final {
26 constexpr BufferSlot() = default;
27
28 std::shared_ptr<GraphicBuffer> graphic_buffer;
29 BufferState buffer_state{BufferState::Free};
30 bool request_buffer_called{};
31 u64 frame_number{};
32 Fence fence;
33 bool acquire_called{};
34 bool needs_cleanup_on_release{};
35 bool attached_by_consumer{};
36 bool is_preallocated{};
37};
38
39} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/buffer_transform_flags.h b/src/core/hle/service/nvflinger/buffer_transform_flags.h
new file mode 100644
index 000000000..e8e6300e3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_transform_flags.h
@@ -0,0 +1,25 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::android {
9
10enum class BufferTransformFlags : u32 {
11 /// No transform flags are set
12 Unset = 0x00,
13 /// Flip source image horizontally (around the vertical axis)
14 FlipH = 0x01,
15 /// Flip source image vertically (around the horizontal axis)
16 FlipV = 0x02,
17 /// Rotate source image 90 degrees clockwise
18 Rotate90 = 0x04,
19 /// Rotate source image 180 degrees
20 Rotate180 = 0x03,
21 /// Rotate source image 270 degrees clockwise
22 Rotate270 = 0x07,
23};
24
25} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.cpp b/src/core/hle/service/nvflinger/consumer_base.cpp
new file mode 100644
index 000000000..30fc21acc
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -0,0 +1,133 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp
6
7#include "common/assert.h"
8#include "common/logging/log.h"
9#include "core/hle/service/nvflinger/buffer_item.h"
10#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
11#include "core/hle/service/nvflinger/buffer_queue_core.h"
12#include "core/hle/service/nvflinger/consumer_base.h"
13#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
14
15namespace Service::android {
16
17ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
18 : consumer{std::move(consumer_)} {}
19
20ConsumerBase::~ConsumerBase() {
21 std::scoped_lock lock{mutex};
22
23 ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
24}
25
26void ConsumerBase::Connect(bool controlled_by_app) {
27 consumer->Connect(shared_from_this(), controlled_by_app);
28}
29
30void ConsumerBase::FreeBufferLocked(s32 slot_index) {
31 LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index);
32
33 slots[slot_index].graphic_buffer = nullptr;
34 slots[slot_index].fence = Fence::NoFence();
35 slots[slot_index].frame_number = 0;
36}
37
38void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
39 LOG_DEBUG(Service_NVFlinger, "called");
40}
41
42void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
43 LOG_DEBUG(Service_NVFlinger, "called");
44}
45
46void ConsumerBase::OnBuffersReleased() {
47 std::scoped_lock lock{mutex};
48
49 LOG_DEBUG(Service_NVFlinger, "called");
50
51 if (is_abandoned) {
52 // Nothing to do if we're already abandoned.
53 return;
54 }
55
56 u64 mask = 0;
57 consumer->GetReleasedBuffers(&mask);
58 for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
59 if (mask & (1ULL << i)) {
60 FreeBufferLocked(i);
61 }
62 }
63}
64
65void ConsumerBase::OnSidebandStreamChanged() {}
66
67Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
68 Status err = consumer->AcquireBuffer(item, present_when);
69 if (err != Status::NoError) {
70 return err;
71 }
72
73 if (item->graphic_buffer != nullptr) {
74 slots[item->slot].graphic_buffer = item->graphic_buffer;
75 }
76
77 slots[item->slot].frame_number = item->frame_number;
78 slots[item->slot].fence = item->fence;
79
80 LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot);
81
82 return Status::NoError;
83}
84
85Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
86 const std::shared_ptr<GraphicBuffer> graphic_buffer,
87 const Fence& fence) {
88 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
89
90 // If consumer no longer tracks this graphic_buffer, we can safely
91 // drop this fence, as it will never be received by the producer.
92
93 if (!StillTracking(slot, graphic_buffer)) {
94 return Status::NoError;
95 }
96
97 slots[slot].fence = fence;
98
99 return Status::NoError;
100}
101
102Status ConsumerBase::ReleaseBufferLocked(s32 slot,
103 const std::shared_ptr<GraphicBuffer> graphic_buffer) {
104 // If consumer no longer tracks this graphic_buffer (we received a new
105 // buffer on the same slot), the buffer producer is definitely no longer
106 // tracking it.
107
108 if (!StillTracking(slot, graphic_buffer)) {
109 return Status::NoError;
110 }
111
112 LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
113 Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence);
114 if (err == Status::StaleBufferSlot) {
115 FreeBufferLocked(slot);
116 }
117
118 slots[slot].fence = Fence::NoFence();
119
120 return err;
121}
122
123bool ConsumerBase::StillTracking(s32 slot,
124 const std::shared_ptr<GraphicBuffer> graphic_buffer) const {
125 if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
126 return false;
127 }
128
129 return (slots[slot].graphic_buffer != nullptr &&
130 slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle());
131}
132
133} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_base.h b/src/core/hle/service/nvflinger/consumer_base.h
new file mode 100644
index 000000000..736080e3a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.h
@@ -0,0 +1,60 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h
6
7#pragma once
8
9#include <array>
10#include <chrono>
11#include <memory>
12#include <mutex>
13
14#include "common/common_types.h"
15#include "core/hle/service/nvflinger/buffer_queue_defs.h"
16#include "core/hle/service/nvflinger/consumer_listener.h"
17#include "core/hle/service/nvflinger/status.h"
18
19namespace Service::android {
20
21class BufferItem;
22class BufferQueueConsumer;
23
24class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
25public:
26 void Connect(bool controlled_by_app);
27
28protected:
29 explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
30 virtual ~ConsumerBase();
31
32 virtual void OnFrameAvailable(const BufferItem& item) override;
33 virtual void OnFrameReplaced(const BufferItem& item) override;
34 virtual void OnBuffersReleased() override;
35 virtual void OnSidebandStreamChanged() override;
36
37 void FreeBufferLocked(s32 slot_index);
38 Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
39 Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
40 bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
41 Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
42 const Fence& fence);
43
44 struct Slot final {
45 std::shared_ptr<GraphicBuffer> graphic_buffer;
46 Fence fence;
47 u64 frame_number{};
48 };
49
50protected:
51 std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots;
52
53 bool is_abandoned{};
54
55 std::unique_ptr<BufferQueueConsumer> consumer;
56
57 mutable std::mutex mutex;
58};
59
60} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/consumer_listener.h b/src/core/hle/service/nvflinger/consumer_listener.h
new file mode 100644
index 000000000..b6d1c3e9a
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_listener.h
@@ -0,0 +1,26 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h
6
7#pragma once
8
9namespace Service::android {
10
11class BufferItem;
12
13/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
14/// that the consumer may wish to react to.
15class IConsumerListener {
16public:
17 IConsumerListener() = default;
18 virtual ~IConsumerListener() = default;
19
20 virtual void OnFrameAvailable(const BufferItem& item) = 0;
21 virtual void OnFrameReplaced(const BufferItem& item) = 0;
22 virtual void OnBuffersReleased() = 0;
23 virtual void OnSidebandStreamChanged() = 0;
24};
25
26}; // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
new file mode 100644
index 000000000..04068827e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
@@ -0,0 +1,18 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
6
7#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
8#include "core/hle/service/nvflinger/parcel.h"
9
10namespace Service::android {
11
12QueueBufferInput::QueueBufferInput(Parcel& parcel) {
13 parcel.ReadFlattened(*this);
14}
15
16QueueBufferOutput::QueueBufferOutput() = default;
17
18} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/graphic_buffer_producer.h b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
new file mode 100644
index 000000000..98d27871c
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
@@ -0,0 +1,76 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2010 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
6
7#pragma once
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "common/math_util.h"
12#include "core/hle/service/nvflinger/ui/fence.h"
13#include "core/hle/service/nvflinger/window.h"
14
15namespace Service::android {
16
17class Parcel;
18
19#pragma pack(push, 1)
20struct QueueBufferInput final {
21 explicit QueueBufferInput(Parcel& parcel);
22
23 void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
24 NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
25 u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const {
26 *timestamp_ = timestamp;
27 *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp);
28 *crop_ = crop;
29 *scaling_mode_ = scaling_mode;
30 *transform_ = transform;
31 *sticky_transform_ = sticky_transform;
32 *async_ = static_cast<bool>(async);
33 *swap_interval_ = swap_interval;
34 *fence_ = fence;
35 }
36
37private:
38 s64 timestamp{};
39 s32 is_auto_timestamp{};
40 Common::Rectangle<s32> crop{};
41 NativeWindowScalingMode scaling_mode{};
42 NativeWindowTransform transform{};
43 u32 sticky_transform{};
44 s32 async{};
45 s32 swap_interval{};
46 Fence fence{};
47};
48#pragma pack(pop)
49static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size");
50
51struct QueueBufferOutput final {
52 QueueBufferOutput();
53
54 void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const {
55 *width_ = width;
56 *height_ = height;
57 *transform_hint_ = transform_hint;
58 *num_pending_buffers_ = num_pending_buffers;
59 }
60
61 void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) {
62 width = width_;
63 height = height_;
64 transform_hint = transform_hint_;
65 num_pending_buffers = num_pending_buffers_;
66 }
67
68private:
69 u32 width{};
70 u32 height{};
71 u32 transform_hint{};
72 u32 num_pending_buffers{};
73};
74static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size");
75
76} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
new file mode 100644
index 000000000..094ba2542
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
@@ -0,0 +1,36 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#include <mutex>
5
6#include "common/common_types.h"
7#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
8
9namespace Service::NVFlinger {
10
11HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
12 : service_context(system_, "HosBinderDriverServer") {}
13
14HosBinderDriverServer::~HosBinderDriverServer() {}
15
16u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
17 std::scoped_lock lk{lock};
18
19 last_id++;
20
21 producers[last_id] = std::move(binder);
22
23 return last_id;
24}
25
26android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
27 std::scoped_lock lk{lock};
28
29 if (auto search = producers.find(id); search != producers.end()) {
30 return search->second.get();
31 }
32
33 return {};
34}
35
36} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/hos_binder_driver_server.h b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
new file mode 100644
index 000000000..cbca87fa0
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
@@ -0,0 +1,37 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include <memory>
7#include <mutex>
8#include <unordered_map>
9
10#include "common/common_types.h"
11#include "core/hle/service/kernel_helpers.h"
12#include "core/hle/service/nvflinger/binder.h"
13
14namespace Core {
15class System;
16}
17
18namespace Service::NVFlinger {
19
20class HosBinderDriverServer final {
21public:
22 explicit HosBinderDriverServer(Core::System& system_);
23 ~HosBinderDriverServer();
24
25 u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
26
27 android::IBinder* TryGetProducer(u64 id);
28
29private:
30 KernelHelpers::ServiceContext service_context;
31
32 std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
33 std::mutex lock;
34 u64 last_id{};
35};
36
37} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 01e69de30..6fb2cdff1 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-License-Identifier: GPL-3.0-or-later
2// Licensed under GPLv2 or any later version 2// Copyright 2021 yuzu Emulator Project
3// Refer to the license.txt file included.
4 3
5#include <algorithm> 4#include <algorithm>
6#include <optional> 5#include <optional>
@@ -16,8 +15,11 @@
16#include "core/hle/kernel/k_readable_event.h" 15#include "core/hle/kernel/k_readable_event.h"
17#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" 16#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
18#include "core/hle/service/nvdrv/nvdrv.h" 17#include "core/hle/service/nvdrv/nvdrv.h"
19#include "core/hle/service/nvflinger/buffer_queue.h" 18#include "core/hle/service/nvflinger/buffer_item_consumer.h"
19#include "core/hle/service/nvflinger/buffer_queue_core.h"
20#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
20#include "core/hle/service/nvflinger/nvflinger.h" 21#include "core/hle/service/nvflinger/nvflinger.h"
22#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
21#include "core/hle/service/vi/display/vi_display.h" 23#include "core/hle/service/vi/display/vi_display.h"
22#include "core/hle/service/vi/layer/vi_layer.h" 24#include "core/hle/service/vi/layer/vi_layer.h"
23#include "video_core/gpu.h" 25#include "video_core/gpu.h"
@@ -53,13 +55,14 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) {
53 } 55 }
54} 56}
55 57
56NVFlinger::NVFlinger(Core::System& system_) 58NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
57 : system(system_), service_context(system_, "nvflinger") { 59 : system(system_), service_context(system_, "nvflinger"),
58 displays.emplace_back(0, "Default", service_context, system); 60 hos_binder_driver_server(hos_binder_driver_server_) {
59 displays.emplace_back(1, "External", service_context, system); 61 displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
60 displays.emplace_back(2, "Edid", service_context, system); 62 displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
61 displays.emplace_back(3, "Internal", service_context, system); 63 displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
62 displays.emplace_back(4, "Null", service_context, system); 64 displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
65 displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
63 guard = std::make_shared<std::mutex>(); 66 guard = std::make_shared<std::mutex>();
64 67
65 // Schedule the screen composition events 68 // Schedule the screen composition events
@@ -83,12 +86,15 @@ NVFlinger::NVFlinger(Core::System& system_)
83} 86}
84 87
85NVFlinger::~NVFlinger() { 88NVFlinger::~NVFlinger() {
86 for (auto& buffer_queue : buffer_queues) {
87 buffer_queue->Disconnect();
88 }
89 if (!system.IsMulticore()) { 89 if (!system.IsMulticore()) {
90 system.CoreTiming().UnscheduleEvent(composition_event, 0); 90 system.CoreTiming().UnscheduleEvent(composition_event, 0);
91 } 91 }
92
93 for (auto& display : displays) {
94 for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
95 display.GetLayer(layer).Core().NotifyShutdown();
96 }
97 }
92} 98}
93 99
94void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 100void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -98,7 +104,7 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
98std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { 104std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
99 const auto lock_guard = Lock(); 105 const auto lock_guard = Lock();
100 106
101 LOG_DEBUG(Service, "Opening \"{}\" display", name); 107 LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
102 108
103 const auto itr = 109 const auto itr =
104 std::find_if(displays.begin(), displays.end(), 110 std::find_if(displays.begin(), displays.end(),
@@ -125,10 +131,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
125} 131}
126 132
127void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { 133void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
128 const u32 buffer_queue_id = next_buffer_queue_id++; 134 const auto buffer_id = next_buffer_queue_id++;
129 buffer_queues.emplace_back( 135 display.CreateLayer(layer_id, buffer_id);
130 std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
131 display.CreateLayer(layer_id, *buffer_queues.back());
132} 136}
133 137
134void NVFlinger::CloseLayer(u64 layer_id) { 138void NVFlinger::CloseLayer(u64 layer_id) {
@@ -147,7 +151,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
147 return std::nullopt; 151 return std::nullopt;
148 } 152 }
149 153
150 return layer->GetBufferQueue().GetId(); 154 return layer->GetBinderId();
151} 155}
152 156
153Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) { 157Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
@@ -161,18 +165,6 @@ Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
161 return &display->GetVSyncEvent(); 165 return &display->GetVSyncEvent();
162} 166}
163 167
164BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
165 const auto lock_guard = Lock();
166 const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
167 [id](const auto& queue) { return queue->GetId() == id; });
168
169 if (itr == buffer_queues.end()) {
170 return nullptr;
171 }
172
173 return itr->get();
174}
175
176VI::Display* NVFlinger::FindDisplay(u64 display_id) { 168VI::Display* NVFlinger::FindDisplay(u64 display_id) {
177 const auto itr = 169 const auto itr =
178 std::find_if(displays.begin(), displays.end(), 170 std::find_if(displays.begin(), displays.end(),
@@ -227,7 +219,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
227 auto* layer = display->FindLayer(layer_id); 219 auto* layer = display->FindLayer(layer_id);
228 220
229 if (layer == nullptr) { 221 if (layer == nullptr) {
230 LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id); 222 LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
231 CreateLayerAtId(*display, layer_id); 223 CreateLayerAtId(*display, layer_id);
232 return display->FindLayer(layer_id); 224 return display->FindLayer(layer_id);
233 } 225 }
@@ -246,23 +238,22 @@ void NVFlinger::Compose() {
246 238
247 // TODO(Subv): Support more than 1 layer. 239 // TODO(Subv): Support more than 1 layer.
248 VI::Layer& layer = display.GetLayer(0); 240 VI::Layer& layer = display.GetLayer(0);
249 auto& buffer_queue = layer.GetBufferQueue();
250 241
251 // Search for a queued buffer and acquire it 242 android::BufferItem buffer{};
252 auto buffer = buffer_queue.AcquireBuffer(); 243 const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
253 244
254 if (!buffer) { 245 if (status != android::Status::NoError) {
255 continue; 246 continue;
256 } 247 }
257 248
258 const auto& igbp_buffer = buffer->get().igbp_buffer; 249 const auto& igbp_buffer = *buffer.graphic_buffer;
259 250
260 if (!system.IsPoweredOn()) { 251 if (!system.IsPoweredOn()) {
261 return; // We are likely shutting down 252 return; // We are likely shutting down
262 } 253 }
263 254
264 auto& gpu = system.GPU(); 255 auto& gpu = system.GPU();
265 const auto& multi_fence = buffer->get().multi_fence; 256 const auto& multi_fence = buffer.fence;
266 guard->unlock(); 257 guard->unlock();
267 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) { 258 for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
268 const auto& fence = multi_fence.fences[fence_id]; 259 const auto& fence = multi_fence.fences[fence_id];
@@ -278,12 +269,18 @@ void NVFlinger::Compose() {
278 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0"); 269 auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
279 ASSERT(nvdisp); 270 ASSERT(nvdisp);
280 271
281 nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format, 272 Common::Rectangle<int> crop_rect{
282 igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, 273 static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
283 buffer->get().transform, buffer->get().crop_rect); 274 static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
275
276 nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
277 igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
278 static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect);
279
280 swap_interval = buffer.swap_interval;
284 281
285 swap_interval = buffer->get().swap_interval; 282 auto fence = android::Fence::NoFence();
286 buffer_queue.ReleaseBuffer(buffer->get().slot); 283 layer.GetConsumer().ReleaseBuffer(buffer, fence);
287 } 284 }
288} 285}
289 286
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 7935cf773..ed160f6f9 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -1,6 +1,5 @@
1// Copyright 2018 yuzu emulator team 1// SPDX-License-Identifier: GPL-3.0-or-later
2// Licensed under GPLv2 or any later version 2// Copyright 2021 yuzu Emulator Project
3// Refer to the license.txt file included.
4 3
5#pragma once 4#pragma once
6 5
@@ -37,13 +36,16 @@ class Display;
37class Layer; 36class Layer;
38} // namespace Service::VI 37} // namespace Service::VI
39 38
40namespace Service::NVFlinger { 39namespace Service::android {
40class BufferQueueCore;
41class BufferQueueProducer;
42} // namespace Service::android
41 43
42class BufferQueue; 44namespace Service::NVFlinger {
43 45
44class NVFlinger final { 46class NVFlinger final {
45public: 47public:
46 explicit NVFlinger(Core::System& system_); 48 explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
47 ~NVFlinger(); 49 ~NVFlinger();
48 50
49 /// Sets the NVDrv module instance to use to send buffers to the GPU. 51 /// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -72,9 +74,6 @@ public:
72 /// If an invalid display ID is provided, then nullptr is returned. 74 /// If an invalid display ID is provided, then nullptr is returned.
73 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id); 75 [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id);
74 76
75 /// Obtains a buffer queue identified by the ID.
76 [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
77
78 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when 77 /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
79 /// finished. 78 /// finished.
80 void Compose(); 79 void Compose();
@@ -82,6 +81,12 @@ public:
82 [[nodiscard]] s64 GetNextTicks() const; 81 [[nodiscard]] s64 GetNextTicks() const;
83 82
84private: 83private:
84 struct Layer {
85 std::unique_ptr<android::BufferQueueCore> core;
86 std::unique_ptr<android::BufferQueueProducer> producer;
87 };
88
89private:
85 [[nodiscard]] std::unique_lock<std::mutex> Lock() const { 90 [[nodiscard]] std::unique_lock<std::mutex> Lock() const {
86 return std::unique_lock{*guard}; 91 return std::unique_lock{*guard};
87 } 92 }
@@ -111,7 +116,6 @@ private:
111 std::shared_ptr<Nvidia::Module> nvdrv; 116 std::shared_ptr<Nvidia::Module> nvdrv;
112 117
113 std::list<VI::Display> displays; 118 std::list<VI::Display> displays;
114 std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
115 119
116 /// Id to use for the next layer that is created, this counter is shared among all displays. 120 /// Id to use for the next layer that is created, this counter is shared among all displays.
117 u64 next_layer_id = 1; 121 u64 next_layer_id = 1;
@@ -131,6 +135,8 @@ private:
131 std::jthread vsync_thread; 135 std::jthread vsync_thread;
132 136
133 KernelHelpers::ServiceContext service_context; 137 KernelHelpers::ServiceContext service_context;
138
139 HosBinderDriverServer& hos_binder_driver_server;
134}; 140};
135 141
136} // namespace Service::NVFlinger 142} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/parcel.h b/src/core/hle/service/nvflinger/parcel.h
new file mode 100644
index 000000000..aa36e6479
--- /dev/null
+++ b/src/core/hle/service/nvflinger/parcel.h
@@ -0,0 +1,172 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include <memory>
7#include <vector>
8
9#include "common/alignment.h"
10#include "common/assert.h"
11#include "common/common_types.h"
12
13namespace Service::android {
14
15class Parcel final {
16public:
17 static constexpr std::size_t DefaultBufferSize = 0x40;
18
19 Parcel() : buffer(DefaultBufferSize) {}
20
21 template <typename T>
22 explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
23 Write(out_data);
24 }
25
26 explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
27 DeserializeHeader();
28 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
29 }
30
31 template <typename T>
32 void Read(T& val) {
33 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
34 ASSERT(read_index + sizeof(T) <= buffer.size());
35
36 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
37 read_index += sizeof(T);
38 read_index = Common::AlignUp(read_index, 4);
39 }
40
41 template <typename T>
42 T Read() {
43 T val;
44 Read(val);
45 return val;
46 }
47
48 template <typename T>
49 void ReadFlattened(T& val) {
50 const auto flattened_size = Read<s64>();
51 ASSERT(sizeof(T) == flattened_size);
52 Read(val);
53 }
54
55 template <typename T>
56 T ReadFlattened() {
57 T val;
58 ReadFlattened(val);
59 return val;
60 }
61
62 template <typename T>
63 T ReadUnaligned() {
64 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
65 ASSERT(read_index + sizeof(T) <= buffer.size());
66
67 T val;
68 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
69 read_index += sizeof(T);
70 return val;
71 }
72
73 template <typename T>
74 const std::shared_ptr<T> ReadObject() {
75 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
76
77 const auto is_valid{Read<bool>()};
78
79 if (is_valid) {
80 auto result = std::make_shared<T>();
81 ReadFlattened(*result);
82 return result;
83 }
84
85 return {};
86 }
87
88 std::u16string ReadInterfaceToken() {
89 [[maybe_unused]] const u32 unknown = Read<u32>();
90 const u32 length = Read<u32>();
91
92 std::u16string token;
93 token.reserve(length + 1);
94
95 for (u32 ch = 0; ch < length + 1; ++ch) {
96 token.push_back(ReadUnaligned<u16>());
97 }
98
99 read_index = Common::AlignUp(read_index, 4);
100
101 return token;
102 }
103
104 template <typename T>
105 void Write(const T& val) {
106 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
107
108 if (buffer.size() < write_index + sizeof(T)) {
109 buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
110 }
111
112 std::memcpy(buffer.data() + write_index, &val, sizeof(T));
113 write_index += sizeof(T);
114 write_index = Common::AlignUp(write_index, 4);
115 }
116
117 template <typename T>
118 void WriteObject(const T* ptr) {
119 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
120
121 if (!ptr) {
122 Write<u32>(0);
123 return;
124 }
125
126 Write<u32>(1);
127 Write<s64>(sizeof(T));
128 Write(*ptr);
129 }
130
131 template <typename T>
132 void WriteObject(const std::shared_ptr<T> ptr) {
133 WriteObject(ptr.get());
134 }
135
136 void DeserializeHeader() {
137 ASSERT(buffer.size() > sizeof(Header));
138
139 Header header{};
140 std::memcpy(&header, buffer.data(), sizeof(Header));
141
142 read_index = header.data_offset;
143 }
144
145 std::vector<u8> Serialize() const {
146 ASSERT(read_index == 0);
147
148 Header header{};
149 header.data_size = static_cast<u32>(write_index - sizeof(Header));
150 header.data_offset = sizeof(Header);
151 header.objects_size = 4;
152 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
153 std::memcpy(buffer.data(), &header, sizeof(Header));
154
155 return buffer;
156 }
157
158private:
159 struct Header {
160 u32 data_size;
161 u32 data_offset;
162 u32 objects_size;
163 u32 objects_offset;
164 };
165 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
166
167 mutable std::vector<u8> buffer;
168 std::size_t read_index = 0;
169 std::size_t write_index = sizeof(Header);
170};
171
172} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/pixel_format.h b/src/core/hle/service/nvflinger/pixel_format.h
new file mode 100644
index 000000000..8a77d8bea
--- /dev/null
+++ b/src/core/hle/service/nvflinger/pixel_format.h
@@ -0,0 +1,21 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_types.h"
7
8namespace Service::android {
9
10enum class PixelFormat : u32 {
11 NoFormat = 0,
12 Rgba8888 = 1,
13 Rgbx8888 = 2,
14 Rgb888 = 3,
15 Rgb565 = 4,
16 Bgra8888 = 5,
17 Rgba5551 = 6,
18 Rgba4444 = 7,
19};
20
21} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/producer_listener.h b/src/core/hle/service/nvflinger/producer_listener.h
new file mode 100644
index 000000000..468e06431
--- /dev/null
+++ b/src/core/hle/service/nvflinger/producer_listener.h
@@ -0,0 +1,16 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2014 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h
6
7#pragma once
8
9namespace Service::android {
10
11class IProducerListener {
12public:
13 virtual void OnBufferReleased() = 0;
14};
15
16} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/status.h b/src/core/hle/service/nvflinger/status.h
new file mode 100644
index 000000000..a003eda89
--- /dev/null
+++ b/src/core/hle/service/nvflinger/status.h
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::android {
10
11enum class Status : s32 {
12 None = 0,
13 NoError = 0,
14 StaleBufferSlot = 1,
15 NoBufferAvailable = 2,
16 PresentLater = 3,
17 WouldBlock = -11,
18 NoMemory = -12,
19 Busy = -16,
20 NoInit = -19,
21 BadValue = -22,
22 InvalidOperation = -37,
23 BufferNeedsReallocation = 1,
24 ReleaseAllBuffers = 2,
25};
26DECLARE_ENUM_FLAG_OPERATORS(Status);
27
28} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/ui/fence.h b/src/core/hle/service/nvflinger/ui/fence.h
new file mode 100644
index 000000000..4a74e26a3
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/fence.h
@@ -0,0 +1,32 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2012 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h
6
7#pragma once
8
9#include <array>
10
11#include "common/common_types.h"
12#include "core/hle/service/nvdrv/nvdata.h"
13
14namespace Service::android {
15
16class Fence {
17public:
18 constexpr Fence() = default;
19
20 static constexpr Fence NoFence() {
21 Fence fence;
22 fence.fences[0].id = -1;
23 return fence;
24 }
25
26public:
27 u32 num_fences{};
28 std::array<Service::Nvidia::NvFence, 4> fences{};
29};
30static_assert(sizeof(Fence) == 36, "Fence has wrong size");
31
32} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/ui/graphic_buffer.h b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
new file mode 100644
index 000000000..7abbf78ba
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
@@ -0,0 +1,100 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3// Copyright 2007 The Android Open Source Project
4// Parts of this implementation were base on:
5// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
6
7#pragma once
8
9#include "common/common_funcs.h"
10#include "common/common_types.h"
11#include "core/hle/service/nvflinger/pixel_format.h"
12
13namespace Service::android {
14
15class GraphicBuffer final {
16public:
17 constexpr GraphicBuffer() = default;
18
19 constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
20 : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
21 usage{static_cast<s32>(usage_)} {}
22
23 constexpr u32 Width() const {
24 return static_cast<u32>(width);
25 }
26
27 constexpr u32 Height() const {
28 return static_cast<u32>(height);
29 }
30
31 constexpr u32 Stride() const {
32 return static_cast<u32>(stride);
33 }
34
35 constexpr u32 Usage() const {
36 return static_cast<u32>(usage);
37 }
38
39 constexpr PixelFormat Format() const {
40 return format;
41 }
42
43 constexpr u32 BufferId() const {
44 return buffer_id;
45 }
46
47 constexpr PixelFormat ExternalFormat() const {
48 return external_format;
49 }
50
51 constexpr u32 Handle() const {
52 return handle;
53 }
54
55 constexpr u32 Offset() const {
56 return offset;
57 }
58
59 constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_,
60 u32 usage_) const {
61 if (static_cast<s32>(width_) != width) {
62 return true;
63 }
64
65 if (static_cast<s32>(height_) != height) {
66 return true;
67 }
68
69 if (format_ != format) {
70 return true;
71 }
72
73 if ((static_cast<u32>(usage) & usage_) != usage_) {
74 return true;
75 }
76
77 return false;
78 }
79
80private:
81 u32 magic{};
82 s32 width{};
83 s32 height{};
84 s32 stride{};
85 PixelFormat format{};
86 s32 usage{};
87 INSERT_PADDING_WORDS(1);
88 u32 index{};
89 INSERT_PADDING_WORDS(3);
90 u32 buffer_id{};
91 INSERT_PADDING_WORDS(6);
92 PixelFormat external_format{};
93 INSERT_PADDING_WORDS(10);
94 u32 handle{};
95 u32 offset{};
96 INSERT_PADDING_WORDS(60);
97};
98static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
99
100} // namespace Service::android
diff --git a/src/core/hle/service/nvflinger/window.h b/src/core/hle/service/nvflinger/window.h
new file mode 100644
index 000000000..e26f8160e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/window.h
@@ -0,0 +1,53 @@
1// SPDX-License-Identifier: GPL-3.0-or-later
2// Copyright 2021 yuzu Emulator Project
3
4#pragma once
5
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8
9namespace Service::android {
10
11/// Attributes queryable with Query
12enum class NativeWindow : s32 {
13 Width = 0,
14 Height = 1,
15 Format = 2,
16 MinUndequeedBuffers = 3,
17 QueuesToWindowComposer = 4,
18 ConcreteType = 5,
19 DefaultWidth = 6,
20 DefaultHeight = 7,
21 TransformHint = 8,
22 ConsumerRunningBehind = 9,
23 ConsumerUsageBits = 10,
24 StickyTransform = 11,
25 DefaultDataSpace = 12,
26 BufferAge = 13,
27};
28
29/// Parameter for Connect/Disconnect
30enum class NativeWindowApi : s32 {
31 NoConnectedApi = 0,
32 Egl = 1,
33 Cpu = 2,
34 Media = 3,
35 Camera = 4,
36};
37
38/// Scaling mode parameter for QueueBuffer
39enum class NativeWindowScalingMode : s32 {
40 Freeze = 0,
41 ScaleToWindow = 1,
42 ScaleCrop = 2,
43 NoScaleCrop = 3,
44};
45
46/// Transform parameter for QueueBuffer
47enum class NativeWindowTransform : u32 {
48 None = 0x0,
49 InverseDisplay = 0x08,
50};
51DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform);
52
53} // namespace Service::android
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index eb1138313..0f59a03c5 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -32,6 +32,7 @@
32#include "core/hle/service/glue/glue.h" 32#include "core/hle/service/glue/glue.h"
33#include "core/hle/service/grc/grc.h" 33#include "core/hle/service/grc/grc.h"
34#include "core/hle/service/hid/hid.h" 34#include "core/hle/service/hid/hid.h"
35#include "core/hle/service/jit/jit.h"
35#include "core/hle/service/lbl/lbl.h" 36#include "core/hle/service/lbl/lbl.h"
36#include "core/hle/service/ldn/ldn.h" 37#include "core/hle/service/ldn/ldn.h"
37#include "core/hle/service/ldr/ldr.h" 38#include "core/hle/service/ldr/ldr.h"
@@ -49,6 +50,7 @@
49#include "core/hle/service/npns/npns.h" 50#include "core/hle/service/npns/npns.h"
50#include "core/hle/service/ns/ns.h" 51#include "core/hle/service/ns/ns.h"
51#include "core/hle/service/nvdrv/nvdrv.h" 52#include "core/hle/service/nvdrv/nvdrv.h"
53#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
52#include "core/hle/service/nvflinger/nvflinger.h" 54#include "core/hle/service/nvflinger/nvflinger.h"
53#include "core/hle/service/olsc/olsc.h" 55#include "core/hle/service/olsc/olsc.h"
54#include "core/hle/service/pcie/pcie.h" 56#include "core/hle/service/pcie/pcie.h"
@@ -90,8 +92,9 @@ namespace Service {
90} 92}
91 93
92ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_, 94ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
93 u32 max_sessions_, InvokerFn* handler_invoker_) 95 ServiceThreadType thread_type, u32 max_sessions_,
94 : SessionRequestHandler(system_.Kernel(), service_name_), system{system_}, 96 InvokerFn* handler_invoker_)
97 : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_},
95 service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {} 98 service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
96 99
97ServiceFrameworkBase::~ServiceFrameworkBase() { 100ServiceFrameworkBase::~ServiceFrameworkBase() {
@@ -230,7 +233,8 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi
230 233
231/// Initialize Services 234/// Initialize Services
232Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) 235Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
233 : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} { 236 : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)},
237 nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} {
234 238
235 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 239 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
236 // here and pass it into the respective InstallInterfaces functions. 240 // here and pass it into the respective InstallInterfaces functions.
@@ -259,6 +263,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
259 Glue::InstallInterfaces(system); 263 Glue::InstallInterfaces(system);
260 GRC::InstallInterfaces(*sm, system); 264 GRC::InstallInterfaces(*sm, system);
261 HID::InstallInterfaces(*sm, system); 265 HID::InstallInterfaces(*sm, system);
266 JIT::InstallInterfaces(*sm, system);
262 LBL::InstallInterfaces(*sm, system); 267 LBL::InstallInterfaces(*sm, system);
263 LDN::InstallInterfaces(*sm, system); 268 LDN::InstallInterfaces(*sm, system);
264 LDR::InstallInterfaces(*sm, system); 269 LDR::InstallInterfaces(*sm, system);
@@ -290,7 +295,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
290 SSL::InstallInterfaces(*sm, system); 295 SSL::InstallInterfaces(*sm, system);
291 Time::InstallInterfaces(system); 296 Time::InstallInterfaces(system);
292 USB::InstallInterfaces(*sm, system); 297 USB::InstallInterfaces(*sm, system);
293 VI::InstallInterfaces(*sm, system, *nv_flinger); 298 VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server);
294 WLAN::InstallInterfaces(*sm, system); 299 WLAN::InstallInterfaces(*sm, system);
295} 300}
296 301
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c9d6b879d..c78b2baeb 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -33,8 +33,9 @@ class FileSystemController;
33} 33}
34 34
35namespace NVFlinger { 35namespace NVFlinger {
36class HosBinderDriverServer;
36class NVFlinger; 37class NVFlinger;
37} 38} // namespace NVFlinger
38 39
39namespace SM { 40namespace SM {
40class ServiceManager; 41class ServiceManager;
@@ -113,7 +114,8 @@ private:
113 Kernel::HLERequestContext& ctx); 114 Kernel::HLERequestContext& ctx);
114 115
115 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, 116 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
116 u32 max_sessions_, InvokerFn* handler_invoker_); 117 ServiceThreadType thread_type, u32 max_sessions_,
118 InvokerFn* handler_invoker_);
117 ~ServiceFrameworkBase() override; 119 ~ServiceFrameworkBase() override;
118 120
119 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); 121 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
@@ -175,14 +177,17 @@ protected:
175 /** 177 /**
176 * Initializes the handler with no functions installed. 178 * Initializes the handler with no functions installed.
177 * 179 *
178 * @param system_ The system context to construct this service under. 180 * @param system_ The system context to construct this service under.
179 * @param service_name_ Name of the service. 181 * @param service_name_ Name of the service.
180 * @param max_sessions_ Maximum number of sessions that can be 182 * @param thread_type Specifies the thread type for this service. If this is set to CreateNew,
181 * connected to this service at the same time. 183 * it creates a new thread for it, otherwise this uses the default thread.
184 * @param max_sessions_ Maximum number of sessions that can be connected to this service at the
185 * same time.
182 */ 186 */
183 explicit ServiceFramework(Core::System& system_, const char* service_name_, 187 explicit ServiceFramework(Core::System& system_, const char* service_name_,
188 ServiceThreadType thread_type = ServiceThreadType::Default,
184 u32 max_sessions_ = ServerSessionCountMax) 189 u32 max_sessions_ = ServerSessionCountMax)
185 : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} 190 : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {}
186 191
187 /// Registers handlers in the service. 192 /// Registers handlers in the service.
188 template <std::size_t N> 193 template <std::size_t N>
@@ -236,6 +241,7 @@ public:
236 ~Services(); 241 ~Services();
237 242
238private: 243private:
244 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
239 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; 245 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
240}; 246};
241 247
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index eaa172595..13f5e08ec 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -81,6 +81,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
81 } 81 }
82 82
83 auto* port = Kernel::KPort::Create(kernel); 83 auto* port = Kernel::KPort::Create(kernel);
84 SCOPE_EXIT({ port->Close(); });
85
84 port->Initialize(ServerSessionCountMax, false, name); 86 port->Initialize(ServerSessionCountMax, false, name);
85 auto handler = it->second; 87 auto handler = it->second;
86 port->GetServerPort().SetSessionHandler(std::move(handler)); 88 port->GetServerPort().SetSessionHandler(std::move(handler));
@@ -151,7 +153,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
151 auto& port = port_result.Unwrap(); 153 auto& port = port_result.Unwrap();
152 SCOPE_EXIT({ port->GetClientPort().Close(); }); 154 SCOPE_EXIT({ port->GetClientPort().Close(); });
153 155
154 server_ports.emplace_back(&port->GetServerPort()); 156 kernel.RegisterServerObject(&port->GetServerPort());
155 157
156 // Create a new session. 158 // Create a new session.
157 Kernel::KClientSession* session{}; 159 Kernel::KClientSession* session{};
@@ -204,7 +206,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
204} 206}
205 207
206SM::SM(ServiceManager& service_manager_, Core::System& system_) 208SM::SM(ServiceManager& service_manager_, Core::System& system_)
207 : ServiceFramework{system_, "sm:", 4}, 209 : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4},
208 service_manager{service_manager_}, kernel{system_.Kernel()} { 210 service_manager{service_manager_}, kernel{system_.Kernel()} {
209 RegisterHandlers({ 211 RegisterHandlers({
210 {0, &SM::Initialize, "Initialize"}, 212 {0, &SM::Initialize, "Initialize"},
@@ -222,10 +224,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
222 }); 224 });
223} 225}
224 226
225SM::~SM() { 227SM::~SM() = default;
226 for (auto& server_port : server_ports) {
227 server_port->Close();
228 }
229}
230 228
231} // namespace Service::SM 229} // namespace Service::SM
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 021eb51b4..f3ff7b27e 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -22,7 +22,6 @@ class KClientPort;
22class KClientSession; 22class KClientSession;
23class KernelCore; 23class KernelCore;
24class KPort; 24class KPort;
25class KServerPort;
26class SessionRequestHandler; 25class SessionRequestHandler;
27} // namespace Kernel 26} // namespace Kernel
28 27
@@ -48,7 +47,6 @@ private:
48 ServiceManager& service_manager; 47 ServiceManager& service_manager;
49 bool is_initialized{}; 48 bool is_initialized{};
50 Kernel::KernelCore& kernel; 49 Kernel::KernelCore& kernel;
51 std::vector<Kernel::KServerPort*> server_ports;
52}; 50};
53 51
54class ServiceManager { 52class ServiceManager {
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index f83272633..d25b050e2 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -10,7 +10,6 @@
10#include <fmt/format.h> 10#include <fmt/format.h>
11 11
12#include "common/microprofile.h" 12#include "common/microprofile.h"
13#include "common/thread.h"
14#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
15#include "core/hle/kernel/k_thread.h" 14#include "core/hle/kernel/k_thread.h"
16#include "core/hle/service/sockets/bsd.h" 15#include "core/hle/service/sockets/bsd.h"
@@ -569,9 +568,9 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
569 new_descriptor.socket = std::move(result.socket); 568 new_descriptor.socket = std::move(result.socket);
570 new_descriptor.is_connection_based = descriptor.is_connection_based; 569 new_descriptor.is_connection_based = descriptor.is_connection_based;
571 570
572 ASSERT(write_buffer.size() == sizeof(SockAddrIn));
573 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); 571 const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
574 std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in)); 572 const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size());
573 std::memcpy(write_buffer.data(), &guest_addr_in, length);
575 574
576 return {new_fd, Errno::SUCCESS}; 575 return {new_fd, Errno::SUCCESS};
577} 576}
@@ -690,6 +689,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
690 case OptName::REUSEADDR: 689 case OptName::REUSEADDR:
691 ASSERT(value == 0 || value == 1); 690 ASSERT(value == 0 || value == 1);
692 return Translate(socket->SetReuseAddr(value != 0)); 691 return Translate(socket->SetReuseAddr(value != 0));
692 case OptName::KEEPALIVE:
693 ASSERT(value == 0 || value == 1);
694 return Translate(socket->SetKeepAlive(value != 0));
693 case OptName::BROADCAST: 695 case OptName::BROADCAST:
694 ASSERT(value == 0 || value == 1); 696 ASSERT(value == 0 || value == 1);
695 return Translate(socket->SetBroadcast(value != 0)); 697 return Translate(socket->SetBroadcast(value != 0));
@@ -838,7 +840,8 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
838 rb.PushEnum(bsd_errno); 840 rb.PushEnum(bsd_errno);
839} 841}
840 842
841BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { 843BSD::BSD(Core::System& system_, const char* name)
844 : ServiceFramework{system_, name, ServiceThreadType::CreateNew} {
842 // clang-format off 845 // clang-format off
843 static const FunctionInfo functions[] = { 846 static const FunctionInfo functions[] = {
844 {0, &BSD::RegisterClient, "RegisterClient"}, 847 {0, &BSD::RegisterClient, "RegisterClient"},
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index fb6142c49..a193fb578 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -2,8 +2,28 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string_view>
6#include <utility>
7#include <vector>
8
9#include "common/string_util.h"
10#include "common/swap.h"
11#include "core/core.h"
5#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
6#include "core/hle/service/sockets/sfdnsres.h" 13#include "core/hle/service/sockets/sfdnsres.h"
14#include "core/memory.h"
15
16#ifdef _WIN32
17#include <ws2tcpip.h>
18#elif YUZU_UNIX
19#include <arpa/inet.h>
20#include <netdb.h>
21#include <netinet/in.h>
22#include <sys/socket.h>
23#ifndef EAI_NODATA
24#define EAI_NODATA EAI_NONAME
25#endif
26#endif
7 27
8namespace Service::Sockets { 28namespace Service::Sockets {
9 29
@@ -21,7 +41,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
21 {9, nullptr, "CancelRequest"}, 41 {9, nullptr, "CancelRequest"},
22 {10, nullptr, "GetHostByNameRequestWithOptions"}, 42 {10, nullptr, "GetHostByNameRequestWithOptions"},
23 {11, nullptr, "GetHostByAddrRequestWithOptions"}, 43 {11, nullptr, "GetHostByAddrRequestWithOptions"},
24 {12, nullptr, "GetAddrInfoRequestWithOptions"}, 44 {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
25 {13, nullptr, "GetNameInfoRequestWithOptions"}, 45 {13, nullptr, "GetNameInfoRequestWithOptions"},
26 {14, nullptr, "ResolverSetOptionRequest"}, 46 {14, nullptr, "ResolverSetOptionRequest"},
27 {15, nullptr, "ResolverGetOptionRequest"}, 47 {15, nullptr, "ResolverGetOptionRequest"},
@@ -31,7 +51,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
31 51
32SFDNSRES::~SFDNSRES() = default; 52SFDNSRES::~SFDNSRES() = default;
33 53
34void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) { 54enum class NetDbError : s32 {
55 Internal = -1,
56 Success = 0,
57 HostNotFound = 1,
58 TryAgain = 2,
59 NoRecovery = 3,
60 NoData = 4,
61};
62
63static NetDbError AddrInfoErrorToNetDbError(s32 result) {
64 // Best effort guess to map errors
65 switch (result) {
66 case 0:
67 return NetDbError::Success;
68 case EAI_AGAIN:
69 return NetDbError::TryAgain;
70 case EAI_NODATA:
71 return NetDbError::NoData;
72 default:
73 return NetDbError::HostNotFound;
74 }
75}
76
77static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code,
78 std::string_view host) {
79 // Adapted from
80 // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
81 std::vector<u8> data;
82
83 auto* current = addrinfo;
84 while (current != nullptr) {
85 struct SerializedResponseHeader {
86 u32 magic;
87 s32 flags;
88 s32 family;
89 s32 socket_type;
90 s32 protocol;
91 u32 address_length;
92 };
93 static_assert(sizeof(SerializedResponseHeader) == 0x18,
94 "Response header size must be 0x18 bytes");
95
96 constexpr auto header_size = sizeof(SerializedResponseHeader);
97 const auto addr_size =
98 current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4;
99 const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1;
100
101 const auto last_size = data.size();
102 data.resize(last_size + header_size + addr_size + canonname_size);
103
104 // Header in network byte order
105 SerializedResponseHeader header{};
106
107 constexpr auto HEADER_MAGIC = 0xBEEFCAFE;
108 header.magic = htonl(HEADER_MAGIC);
109 header.family = htonl(current->ai_family);
110 header.flags = htonl(current->ai_flags);
111 header.socket_type = htonl(current->ai_socktype);
112 header.protocol = htonl(current->ai_protocol);
113 header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
114
115 auto* header_ptr = data.data() + last_size;
116 std::memcpy(header_ptr, &header, header_size);
117
118 if (header.address_length == 0) {
119 std::memset(header_ptr + header_size, 0, 4);
120 } else {
121 switch (current->ai_family) {
122 case AF_INET: {
123 struct SockAddrIn {
124 s16 sin_family;
125 u16 sin_port;
126 u32 sin_addr;
127 u8 sin_zero[8];
128 };
129
130 SockAddrIn serialized_addr{};
131 const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
132 serialized_addr.sin_port = htons(addr.sin_port);
133 serialized_addr.sin_family = htons(addr.sin_family);
134 serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr);
135 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn));
136
137 char addr_string_buf[64]{};
138 inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf));
139 LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf);
140 break;
141 }
142 case AF_INET6: {
143 struct SockAddrIn6 {
144 s16 sin6_family;
145 u16 sin6_port;
146 u32 sin6_flowinfo;
147 u8 sin6_addr[16];
148 u32 sin6_scope_id;
149 };
150
151 SockAddrIn6 serialized_addr{};
152 const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
153 serialized_addr.sin6_family = htons(addr.sin6_family);
154 serialized_addr.sin6_port = htons(addr.sin6_port);
155 serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
156 serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id);
157 std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr,
158 sizeof(SockAddrIn6::sin6_addr));
159 std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6));
160
161 char addr_string_buf[64]{};
162 inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf));
163 LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf);
164 break;
165 }
166 default:
167 std::memcpy(header_ptr + header_size, current->ai_addr, addr_size);
168 break;
169 }
170 }
171 if (current->ai_canonname) {
172 std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size);
173 } else {
174 *(header_ptr + header_size + addr_size) = 0;
175 }
176
177 current = current->ai_next;
178 }
179
180 // 4-byte sentinel value
181 data.push_back(0);
182 data.push_back(0);
183 data.push_back(0);
184 data.push_back(0);
185
186 return data;
187}
188
189static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) {
35 struct Parameters { 190 struct Parameters {
36 u8 use_nsd_resolve; 191 u8 use_nsd_resolve;
37 u32 unknown; 192 u32 unknown;
@@ -42,11 +197,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
42 const auto parameters = rp.PopRaw<Parameters>(); 197 const auto parameters = rp.PopRaw<Parameters>();
43 198
44 LOG_WARNING(Service, 199 LOG_WARNING(Service,
45 "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}", 200 "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}",
46 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); 201 parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
47 202
48 IPC::ResponseBuilder rb{ctx, 2}; 203 const auto host_buffer = ctx.ReadBuffer(0);
204 const std::string host = Common::StringFromBuffer(host_buffer);
205
206 const auto service_buffer = ctx.ReadBuffer(1);
207 const std::string service = Common::StringFromBuffer(service_buffer);
208
209 addrinfo* addrinfo;
210 // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now
211 s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo);
212
213 u32 data_size = 0;
214 if (result_code == 0 && addrinfo != nullptr) {
215 const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host);
216 data_size = static_cast<u32>(data.size());
217 freeaddrinfo(addrinfo);
218
219 ctx.WriteBuffer(data, 0);
220 }
221
222 return std::make_pair(data_size, result_code);
223}
224
225void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
226 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
227
228 IPC::ResponseBuilder rb{ctx, 4};
229 rb.Push(ResultSuccess);
230 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
231 rb.Push(result_code); // errno
232 rb.Push(data_size); // serialized size
233}
234
235void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) {
236 // Additional options are ignored
237 auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
238
239 IPC::ResponseBuilder rb{ctx, 5};
49 rb.Push(ResultSuccess); 240 rb.Push(ResultSuccess);
241 rb.Push(data_size); // serialized size
242 rb.Push(result_code); // errno
243 rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
244 rb.Push(0);
50} 245}
51 246
52} // namespace Service::Sockets 247} // namespace Service::Sockets \ No newline at end of file
diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h
index 5d3b4dc2d..f0c57377d 100644
--- a/src/core/hle/service/sockets/sfdnsres.h
+++ b/src/core/hle/service/sockets/sfdnsres.h
@@ -19,6 +19,7 @@ public:
19 19
20private: 20private:
21 void GetAddrInfoRequest(Kernel::HLERequestContext& ctx); 21 void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
22 void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx);
22}; 23};
23 24
24} // namespace Service::Sockets 25} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index 02dbbae40..d69c86431 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -46,6 +46,7 @@ enum class Protocol : u32 {
46 46
47enum class OptName : u32 { 47enum class OptName : u32 {
48 REUSEADDR = 0x4, 48 REUSEADDR = 0x4,
49 KEEPALIVE = 0x8,
49 BROADCAST = 0x20, 50 BROADCAST = 0x20,
50 LINGER = 0x80, 51 LINGER = 0x80,
51 SNDBUF = 0x1001, 52 SNDBUF = 0x1001,
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index b7705c02a..558022511 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -13,14 +13,34 @@
13#include "core/hle/kernel/k_readable_event.h" 13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_writable_event.h" 14#include "core/hle/kernel/k_writable_event.h"
15#include "core/hle/service/kernel_helpers.h" 15#include "core/hle/service/kernel_helpers.h"
16#include "core/hle/service/nvflinger/buffer_item_consumer.h"
17#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
18#include "core/hle/service/nvflinger/buffer_queue_core.h"
19#include "core/hle/service/nvflinger/buffer_queue_producer.h"
20#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
16#include "core/hle/service/vi/display/vi_display.h" 21#include "core/hle/service/vi/display/vi_display.h"
17#include "core/hle/service/vi/layer/vi_layer.h" 22#include "core/hle/service/vi/layer/vi_layer.h"
18 23
19namespace Service::VI { 24namespace Service::VI {
20 25
21Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, 26struct BufferQueue {
22 Core::System& system_) 27 std::shared_ptr<android::BufferQueueCore> core;
23 : display_id{id}, name{std::move(name_)}, service_context{service_context_} { 28 std::unique_ptr<android::BufferQueueProducer> producer;
29 std::unique_ptr<android::BufferQueueConsumer> consumer;
30};
31
32static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context) {
33 auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
34 return {buffer_queue_core,
35 std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core),
36 std::make_unique<android::BufferQueueConsumer>(buffer_queue_core)};
37}
38
39Display::Display(u64 id, std::string name_,
40 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
41 KernelHelpers::ServiceContext& service_context_, Core::System& system_)
42 : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
43 service_context{service_context_} {
24 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); 44 vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
25} 45}
26 46
@@ -44,21 +64,29 @@ void Display::SignalVSyncEvent() {
44 vsync_event->GetWritableEvent().Signal(); 64 vsync_event->GetWritableEvent().Signal();
45} 65}
46 66
47void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { 67void Display::CreateLayer(u64 layer_id, u32 binder_id) {
48 // TODO(Subv): Support more than 1 layer.
49 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); 68 ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
50 69
51 layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue)); 70 auto [core, producer, consumer] = CreateBufferQueue(service_context);
71
72 auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
73 buffer_item_consumer->Connect(false);
74
75 layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
76 std::move(buffer_item_consumer)));
77
78 hos_binder_driver_server.RegisterProducer(std::move(producer));
52} 79}
53 80
54void Display::CloseLayer(u64 layer_id) { 81void Display::CloseLayer(u64 layer_id) {
55 std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; }); 82 std::erase_if(layers,
83 [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
56} 84}
57 85
58Layer* Display::FindLayer(u64 layer_id) { 86Layer* Display::FindLayer(u64 layer_id) {
59 const auto itr = 87 const auto itr =
60 std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { 88 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
61 return layer->GetID() == layer_id; 89 return layer->GetLayerId() == layer_id;
62 }); 90 });
63 91
64 if (itr == layers.end()) { 92 if (itr == layers.end()) {
@@ -70,8 +98,8 @@ Layer* Display::FindLayer(u64 layer_id) {
70 98
71const Layer* Display::FindLayer(u64 layer_id) const { 99const Layer* Display::FindLayer(u64 layer_id) const {
72 const auto itr = 100 const auto itr =
73 std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) { 101 std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
74 return layer->GetID() == layer_id; 102 return layer->GetLayerId() == layer_id;
75 }); 103 });
76 104
77 if (itr == layers.end()) { 105 if (itr == layers.end()) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 329f4ba86..e93d084ee 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -15,12 +15,17 @@ namespace Kernel {
15class KEvent; 15class KEvent;
16} 16}
17 17
18namespace Service::NVFlinger { 18namespace Service::android {
19class BufferQueue; 19class BufferQueueProducer;
20} 20}
21
21namespace Service::KernelHelpers { 22namespace Service::KernelHelpers {
22class ServiceContext; 23class ServiceContext;
23} // namespace Service::KernelHelpers 24}
25
26namespace Service::NVFlinger {
27class HosBinderDriverServer;
28}
24 29
25namespace Service::VI { 30namespace Service::VI {
26 31
@@ -35,12 +40,13 @@ public:
35 /// Constructs a display with a given unique ID and name. 40 /// Constructs a display with a given unique ID and name.
36 /// 41 ///
37 /// @param id The unique ID for this display. 42 /// @param id The unique ID for this display.
43 /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance.
38 /// @param service_context_ The ServiceContext for the owning service. 44 /// @param service_context_ The ServiceContext for the owning service.
39 /// @param name_ The name for this display. 45 /// @param name_ The name for this display.
40 /// @param system_ The global system instance. 46 /// @param system_ The global system instance.
41 /// 47 ///
42 Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, 48 Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
43 Core::System& system_); 49 KernelHelpers::ServiceContext& service_context_, Core::System& system_);
44 ~Display(); 50 ~Display();
45 51
46 /// Gets the unique ID assigned to this display. 52 /// Gets the unique ID assigned to this display.
@@ -64,6 +70,10 @@ public:
64 /// Gets a layer for this display based off an index. 70 /// Gets a layer for this display based off an index.
65 const Layer& GetLayer(std::size_t index) const; 71 const Layer& GetLayer(std::size_t index) const;
66 72
73 std::size_t GetNumLayers() const {
74 return layers.size();
75 }
76
67 /// Gets the readable vsync event. 77 /// Gets the readable vsync event.
68 Kernel::KReadableEvent& GetVSyncEvent(); 78 Kernel::KReadableEvent& GetVSyncEvent();
69 79
@@ -72,10 +82,10 @@ public:
72 82
73 /// Creates and adds a layer to this display with the given ID. 83 /// Creates and adds a layer to this display with the given ID.
74 /// 84 ///
75 /// @param layer_id The ID to assign to the created layer. 85 /// @param layer_id The ID to assign to the created layer.
76 /// @param buffer_queue The buffer queue for the layer instance to use. 86 /// @param binder_id The ID assigned to the buffer queue.
77 /// 87 ///
78 void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue); 88 void CreateLayer(u64 layer_id, u32 binder_id);
79 89
80 /// Closes and removes a layer from this display with the given ID. 90 /// Closes and removes a layer from this display with the given ID.
81 /// 91 ///
@@ -104,9 +114,10 @@ public:
104private: 114private:
105 u64 display_id; 115 u64 display_id;
106 std::string name; 116 std::string name;
117 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
107 KernelHelpers::ServiceContext& service_context; 118 KernelHelpers::ServiceContext& service_context;
108 119
109 std::vector<std::shared_ptr<Layer>> layers; 120 std::vector<std::unique_ptr<Layer>> layers;
110 Kernel::KEvent* vsync_event{}; 121 Kernel::KEvent* vsync_event{};
111}; 122};
112 123
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
index 9bc382587..93858e91f 100644
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -6,7 +6,11 @@
6 6
7namespace Service::VI { 7namespace Service::VI {
8 8
9Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {} 9Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
10 android::BufferQueueProducer& binder_,
11 std::shared_ptr<android::BufferItemConsumer>&& consumer_)
12 : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
13 consumer_)} {}
10 14
11Layer::~Layer() = default; 15Layer::~Layer() = default;
12 16
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index ebdd85505..c28b14450 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -4,11 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
8
7#include "common/common_types.h" 9#include "common/common_types.h"
8 10
9namespace Service::NVFlinger { 11namespace Service::android {
10class BufferQueue; 12class BufferItemConsumer;
11} 13class BufferQueueCore;
14class BufferQueueProducer;
15} // namespace Service::android
12 16
13namespace Service::VI { 17namespace Service::VI {
14 18
@@ -17,10 +21,13 @@ class Layer {
17public: 21public:
18 /// Constructs a layer with a given ID and buffer queue. 22 /// Constructs a layer with a given ID and buffer queue.
19 /// 23 ///
20 /// @param id The ID to assign to this layer. 24 /// @param layer_id_ The ID to assign to this layer.
21 /// @param queue The buffer queue for this layer to use. 25 /// @param binder_id_ The binder ID to assign to this layer.
26 /// @param binder_ The buffer producer queue for this layer to use.
22 /// 27 ///
23 Layer(u64 id, NVFlinger::BufferQueue& queue); 28 Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
29 android::BufferQueueProducer& binder_,
30 std::shared_ptr<android::BufferItemConsumer>&& consumer_);
24 ~Layer(); 31 ~Layer();
25 32
26 Layer(const Layer&) = delete; 33 Layer(const Layer&) = delete;
@@ -30,23 +37,47 @@ public:
30 Layer& operator=(Layer&&) = delete; 37 Layer& operator=(Layer&&) = delete;
31 38
32 /// Gets the ID for this layer. 39 /// Gets the ID for this layer.
33 u64 GetID() const { 40 u64 GetLayerId() const {
34 return layer_id; 41 return layer_id;
35 } 42 }
36 43
44 /// Gets the binder ID for this layer.
45 u32 GetBinderId() const {
46 return binder_id;
47 }
48
37 /// Gets a reference to the buffer queue this layer is using. 49 /// Gets a reference to the buffer queue this layer is using.
38 NVFlinger::BufferQueue& GetBufferQueue() { 50 android::BufferQueueProducer& GetBufferQueue() {
39 return buffer_queue; 51 return binder;
40 } 52 }
41 53
42 /// Gets a const reference to the buffer queue this layer is using. 54 /// Gets a const reference to the buffer queue this layer is using.
43 const NVFlinger::BufferQueue& GetBufferQueue() const { 55 const android::BufferQueueProducer& GetBufferQueue() const {
44 return buffer_queue; 56 return binder;
57 }
58
59 android::BufferItemConsumer& GetConsumer() {
60 return *consumer;
61 }
62
63 const android::BufferItemConsumer& GetConsumer() const {
64 return *consumer;
65 }
66
67 android::BufferQueueCore& Core() {
68 return core;
69 }
70
71 const android::BufferQueueCore& Core() const {
72 return core;
45 } 73 }
46 74
47private: 75private:
48 u64 layer_id; 76 const u64 layer_id;
49 NVFlinger::BufferQueue& buffer_queue; 77 const u32 binder_id;
78 android::BufferQueueCore& core;
79 android::BufferQueueProducer& binder;
80 std::shared_ptr<android::BufferItemConsumer> consumer;
50}; 81};
51 82
52} // namespace Service::VI 83} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 75ee3e5e4..a3436c8ea 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -22,8 +22,11 @@
22#include "core/hle/kernel/k_readable_event.h" 22#include "core/hle/kernel/k_readable_event.h"
23#include "core/hle/kernel/k_thread.h" 23#include "core/hle/kernel/k_thread.h"
24#include "core/hle/service/nvdrv/nvdata.h" 24#include "core/hle/service/nvdrv/nvdata.h"
25#include "core/hle/service/nvflinger/buffer_queue.h" 25#include "core/hle/service/nvflinger/binder.h"
26#include "core/hle/service/nvflinger/buffer_queue_producer.h"
27#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
26#include "core/hle/service/nvflinger/nvflinger.h" 28#include "core/hle/service/nvflinger/nvflinger.h"
29#include "core/hle/service/nvflinger/parcel.h"
27#include "core/hle/service/service.h" 30#include "core/hle/service/service.h"
28#include "core/hle/service/vi/vi.h" 31#include "core/hle/service/vi/vi.h"
29#include "core/hle/service/vi/vi_m.h" 32#include "core/hle/service/vi/vi_m.h"
@@ -57,447 +60,25 @@ struct DisplayInfo {
57}; 60};
58static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); 61static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
59 62
60class Parcel { 63class NativeWindow final {
61public: 64public:
62 // This default size was chosen arbitrarily. 65 constexpr explicit NativeWindow(u32 id_) : id{id_} {}
63 static constexpr std::size_t DefaultBufferSize = 0x40;
64 Parcel() : buffer(DefaultBufferSize) {}
65 explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {}
66 virtual ~Parcel() = default;
67
68 template <typename T>
69 T Read() {
70 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
71 ASSERT(read_index + sizeof(T) <= buffer.size());
72
73 T val;
74 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
75 read_index += sizeof(T);
76 read_index = Common::AlignUp(read_index, 4);
77 return val;
78 }
79
80 template <typename T>
81 T ReadUnaligned() {
82 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
83 ASSERT(read_index + sizeof(T) <= buffer.size());
84
85 T val;
86 std::memcpy(&val, buffer.data() + read_index, sizeof(T));
87 read_index += sizeof(T);
88 return val;
89 }
90
91 std::vector<u8> ReadBlock(std::size_t length) {
92 ASSERT(read_index + length <= buffer.size());
93 const u8* const begin = buffer.data() + read_index;
94 const u8* const end = begin + length;
95 std::vector<u8> data(begin, end);
96 read_index += length;
97 read_index = Common::AlignUp(read_index, 4);
98 return data;
99 }
100
101 std::u16string ReadInterfaceToken() {
102 [[maybe_unused]] const u32 unknown = Read<u32_le>();
103 const u32 length = Read<u32_le>();
104
105 std::u16string token{};
106
107 for (u32 ch = 0; ch < length + 1; ++ch) {
108 token.push_back(ReadUnaligned<u16_le>());
109 }
110
111 read_index = Common::AlignUp(read_index, 4);
112
113 return token;
114 }
115
116 template <typename T>
117 void Write(const T& val) {
118 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
119
120 if (buffer.size() < write_index + sizeof(T)) {
121 buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
122 }
123
124 std::memcpy(buffer.data() + write_index, &val, sizeof(T));
125 write_index += sizeof(T);
126 write_index = Common::AlignUp(write_index, 4);
127 }
128
129 template <typename T>
130 void WriteObject(const T& val) {
131 static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
132
133 const u32_le size = static_cast<u32>(sizeof(val));
134 Write(size);
135 // TODO(Subv): Support file descriptors.
136 Write<u32_le>(0); // Fd count.
137 Write(val);
138 }
139
140 void Deserialize() {
141 ASSERT(buffer.size() > sizeof(Header));
142
143 Header header{};
144 std::memcpy(&header, buffer.data(), sizeof(Header));
145
146 read_index = header.data_offset;
147 DeserializeData();
148 }
149
150 std::vector<u8> Serialize() {
151 ASSERT(read_index == 0);
152 write_index = sizeof(Header);
153
154 SerializeData();
155
156 Header header{};
157 header.data_size = static_cast<u32_le>(write_index - sizeof(Header));
158 header.data_offset = sizeof(Header);
159 header.objects_size = 4;
160 header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
161 std::memcpy(buffer.data(), &header, sizeof(Header));
162
163 return buffer;
164 }
165
166protected:
167 virtual void SerializeData() {}
168
169 virtual void DeserializeData() {}
170
171private:
172 struct Header {
173 u32_le data_size;
174 u32_le data_offset;
175 u32_le objects_size;
176 u32_le objects_offset;
177 };
178 static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
179
180 std::vector<u8> buffer;
181 std::size_t read_index = 0;
182 std::size_t write_index = 0;
183};
184
185class NativeWindow : public Parcel {
186public:
187 explicit NativeWindow(u32 id) {
188 data.id = id;
189 }
190 ~NativeWindow() override = default;
191
192protected:
193 void SerializeData() override {
194 Write(data);
195 }
196
197private:
198 struct Data {
199 u32_le magic = 2;
200 u32_le process_id = 1;
201 u32_le id;
202 INSERT_PADDING_WORDS(3);
203 std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
204 INSERT_PADDING_WORDS(2);
205 };
206 static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size");
207
208 Data data{};
209};
210
211class IGBPConnectRequestParcel : public Parcel {
212public:
213 explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
214 Deserialize();
215 }
216
217 void DeserializeData() override {
218 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
219 data = Read<Data>();
220 }
221
222 struct Data {
223 u32_le unk;
224 u32_le api;
225 u32_le producer_controlled_by_app;
226 };
227
228 Data data;
229};
230
231class IGBPConnectResponseParcel : public Parcel {
232public:
233 explicit IGBPConnectResponseParcel(u32 width, u32 height) {
234 data.width = width;
235 data.height = height;
236 }
237 ~IGBPConnectResponseParcel() override = default;
238
239protected:
240 void SerializeData() override {
241 Write(data);
242 }
243
244private:
245 struct Data {
246 u32_le width;
247 u32_le height;
248 u32_le transform_hint;
249 u32_le num_pending_buffers;
250 u32_le status;
251 };
252 static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
253
254 Data data{};
255};
256
257/// Represents a parcel containing one int '0' as its data
258/// Used by DetachBuffer and Disconnect
259class IGBPEmptyResponseParcel : public Parcel {
260protected:
261 void SerializeData() override {
262 Write(data);
263 }
264
265private:
266 struct Data {
267 u32_le unk_0{};
268 };
269
270 Data data{};
271};
272
273class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
274public:
275 explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_)
276 : Parcel(std::move(buffer_)) {
277 Deserialize();
278 }
279
280 void DeserializeData() override {
281 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
282 data = Read<Data>();
283 if (data.contains_object != 0) {
284 buffer_container = Read<BufferContainer>();
285 }
286 }
287
288 struct Data {
289 u32_le slot;
290 u32_le contains_object;
291 };
292
293 struct BufferContainer {
294 u32_le graphic_buffer_length;
295 INSERT_PADDING_WORDS(1);
296 NVFlinger::IGBPBuffer buffer{};
297 };
298
299 Data data{};
300 BufferContainer buffer_container{};
301};
302
303class IGBPSetPreallocatedBufferResponseParcel : public Parcel {
304protected:
305 void SerializeData() override {
306 // TODO(Subv): Find out what this means
307 Write<u32>(0);
308 }
309};
310
311class IGBPCancelBufferRequestParcel : public Parcel {
312public:
313 explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
314 Deserialize();
315 }
316
317 void DeserializeData() override {
318 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
319 data = Read<Data>();
320 }
321
322 struct Data {
323 u32_le slot;
324 Service::Nvidia::MultiFence multi_fence;
325 };
326
327 Data data;
328};
329
330class IGBPCancelBufferResponseParcel : public Parcel {
331protected:
332 void SerializeData() override {
333 Write<u32>(0); // Success
334 }
335};
336
337class IGBPDequeueBufferRequestParcel : public Parcel {
338public:
339 explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
340 Deserialize();
341 }
342
343 void DeserializeData() override {
344 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
345 data = Read<Data>();
346 }
347
348 struct Data {
349 u32_le pixel_format;
350 u32_le width;
351 u32_le height;
352 u32_le get_frame_timestamps;
353 u32_le usage;
354 };
355
356 Data data;
357};
358
359class IGBPDequeueBufferResponseParcel : public Parcel {
360public:
361 explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_)
362 : slot(slot_), multi_fence(multi_fence_) {}
363
364protected:
365 void SerializeData() override {
366 Write(slot);
367 Write<u32_le>(1);
368 WriteObject(multi_fence);
369 Write<u32_le>(0);
370 }
371
372 u32_le slot;
373 Service::Nvidia::MultiFence multi_fence;
374};
375
376class IGBPRequestBufferRequestParcel : public Parcel {
377public:
378 explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
379 Deserialize();
380 }
381
382 void DeserializeData() override {
383 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
384 slot = Read<u32_le>();
385 }
386
387 u32_le slot;
388};
389
390class IGBPRequestBufferResponseParcel : public Parcel {
391public:
392 explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {}
393 ~IGBPRequestBufferResponseParcel() override = default;
394
395protected:
396 void SerializeData() override {
397 // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
398 // try to read an IGBPBuffer object from the parcel.
399 Write<u32_le>(1);
400 WriteObject(buffer);
401 Write<u32_le>(0);
402 }
403
404 NVFlinger::IGBPBuffer buffer;
405};
406
407class IGBPQueueBufferRequestParcel : public Parcel {
408public:
409 explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
410 Deserialize();
411 }
412
413 void DeserializeData() override {
414 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
415 data = Read<Data>();
416 }
417
418 struct Data {
419 u32_le slot;
420 INSERT_PADDING_WORDS(3);
421 u32_le timestamp;
422 s32_le is_auto_timestamp;
423 s32_le crop_top;
424 s32_le crop_left;
425 s32_le crop_right;
426 s32_le crop_bottom;
427 s32_le scaling_mode;
428 NVFlinger::BufferQueue::BufferTransformFlags transform;
429 u32_le sticky_transform;
430 INSERT_PADDING_WORDS(1);
431 u32_le swap_interval;
432 Service::Nvidia::MultiFence multi_fence;
433
434 Common::Rectangle<int> GetCropRect() const {
435 return {crop_left, crop_top, crop_right, crop_bottom};
436 }
437 };
438 static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
439
440 Data data;
441};
442
443class IGBPQueueBufferResponseParcel : public Parcel {
444public:
445 explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) {
446 data.width = width;
447 data.height = height;
448 }
449 ~IGBPQueueBufferResponseParcel() override = default;
450
451protected:
452 void SerializeData() override {
453 Write(data);
454 }
455
456private:
457 struct Data {
458 u32_le width;
459 u32_le height;
460 u32_le transform_hint;
461 u32_le num_pending_buffers;
462 u32_le status;
463 };
464 static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
465
466 Data data{};
467};
468
469class IGBPQueryRequestParcel : public Parcel {
470public:
471 explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
472 Deserialize();
473 }
474
475 void DeserializeData() override {
476 [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
477 type = Read<u32_le>();
478 }
479
480 u32 type;
481};
482
483class IGBPQueryResponseParcel : public Parcel {
484public:
485 explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {}
486 ~IGBPQueryResponseParcel() override = default;
487
488protected:
489 void SerializeData() override {
490 Write(value);
491 }
492 66
493private: 67private:
494 u32_le value; 68 const u32 magic = 2;
69 const u32 process_id = 1;
70 const u32 id;
71 INSERT_PADDING_WORDS(3);
72 std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
73 INSERT_PADDING_WORDS(2);
495}; 74};
75static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
496 76
497class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> { 77class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
498public: 78public:
499 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 79 explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_)
500 : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) { 80 : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew},
81 server(server_) {
501 static const FunctionInfo functions[] = { 82 static const FunctionInfo functions[] = {
502 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"}, 83 {0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
503 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"}, 84 {1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -508,147 +89,16 @@ public:
508 } 89 }
509 90
510private: 91private:
511 enum class TransactionId {
512 RequestBuffer = 1,
513 SetBufferCount = 2,
514 DequeueBuffer = 3,
515 DetachBuffer = 4,
516 DetachNextBuffer = 5,
517 AttachBuffer = 6,
518 QueueBuffer = 7,
519 CancelBuffer = 8,
520 Query = 9,
521 Connect = 10,
522 Disconnect = 11,
523
524 AllocateBuffers = 13,
525 SetPreallocatedBuffer = 14,
526
527 GetBufferHistory = 17
528 };
529
530 void TransactParcel(Kernel::HLERequestContext& ctx) { 92 void TransactParcel(Kernel::HLERequestContext& ctx) {
531 IPC::RequestParser rp{ctx}; 93 IPC::RequestParser rp{ctx};
532 const u32 id = rp.Pop<u32>(); 94 const u32 id = rp.Pop<u32>();
533 const auto transaction = static_cast<TransactionId>(rp.Pop<u32>()); 95 const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>());
534 const u32 flags = rp.Pop<u32>(); 96 const u32 flags = rp.Pop<u32>();
535 97
536 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, 98 LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
537 transaction, flags); 99 transaction, flags);
538 100
539 auto& buffer_queue = *nv_flinger.FindBufferQueue(id); 101 server.TryGetProducer(id)->Transact(ctx, transaction, flags);
540
541 switch (transaction) {
542 case TransactionId::Connect: {
543 IGBPConnectRequestParcel request{ctx.ReadBuffer()};
544 IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth),
545 static_cast<u32>(DisplayResolution::UndockedHeight)};
546
547 buffer_queue.Connect();
548
549 ctx.WriteBuffer(response.Serialize());
550 break;
551 }
552 case TransactionId::SetPreallocatedBuffer: {
553 IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
554
555 buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
556
557 IGBPSetPreallocatedBufferResponseParcel response{};
558 ctx.WriteBuffer(response.Serialize());
559 break;
560 }
561 case TransactionId::DequeueBuffer: {
562 IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
563 const u32 width{request.data.width};
564 const u32 height{request.data.height};
565
566 do {
567 if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
568 // Buffer is available
569 IGBPDequeueBufferResponseParcel response{result->first, *result->second};
570 ctx.WriteBuffer(response.Serialize());
571 break;
572 }
573 } while (buffer_queue.IsConnected());
574
575 break;
576 }
577 case TransactionId::RequestBuffer: {
578 IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
579
580 auto& buffer = buffer_queue.RequestBuffer(request.slot);
581 IGBPRequestBufferResponseParcel response{buffer};
582 ctx.WriteBuffer(response.Serialize());
583
584 break;
585 }
586 case TransactionId::QueueBuffer: {
587 IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
588
589 buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
590 request.data.GetCropRect(), request.data.swap_interval,
591 request.data.multi_fence);
592
593 IGBPQueueBufferResponseParcel response{1280, 720};
594 ctx.WriteBuffer(response.Serialize());
595 break;
596 }
597 case TransactionId::Query: {
598 IGBPQueryRequestParcel request{ctx.ReadBuffer()};
599
600 const u32 value =
601 buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
602
603 IGBPQueryResponseParcel response{value};
604 ctx.WriteBuffer(response.Serialize());
605 break;
606 }
607 case TransactionId::CancelBuffer: {
608 IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
609
610 buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
611
612 IGBPCancelBufferResponseParcel response{};
613 ctx.WriteBuffer(response.Serialize());
614 break;
615 }
616 case TransactionId::Disconnect: {
617 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
618 const auto buffer = ctx.ReadBuffer();
619
620 buffer_queue.Disconnect();
621
622 IGBPEmptyResponseParcel response{};
623 ctx.WriteBuffer(response.Serialize());
624 break;
625 }
626 case TransactionId::DetachBuffer: {
627 const auto buffer = ctx.ReadBuffer();
628
629 IGBPEmptyResponseParcel response{};
630 ctx.WriteBuffer(response.Serialize());
631 break;
632 }
633 case TransactionId::SetBufferCount: {
634 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount");
635 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
636
637 IGBPEmptyResponseParcel response{};
638 ctx.WriteBuffer(response.Serialize());
639 break;
640 }
641 case TransactionId::GetBufferHistory: {
642 LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory");
643 [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
644
645 IGBPEmptyResponseParcel response{};
646 ctx.WriteBuffer(response.Serialize());
647 break;
648 }
649 default:
650 ASSERT_MSG(false, "Unimplemented");
651 }
652 102
653 IPC::ResponseBuilder rb{ctx, 2}; 103 IPC::ResponseBuilder rb{ctx, 2};
654 rb.Push(ResultSuccess); 104 rb.Push(ResultSuccess);
@@ -674,13 +124,13 @@ private:
674 124
675 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown); 125 LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
676 126
677 // TODO(Subv): Find out what this actually is.
678 IPC::ResponseBuilder rb{ctx, 2, 1}; 127 IPC::ResponseBuilder rb{ctx, 2, 1};
679 rb.Push(ResultSuccess); 128 rb.Push(ResultSuccess);
680 rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent()); 129 rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle());
681 } 130 }
682 131
683 NVFlinger::NVFlinger& nv_flinger; 132private:
133 NVFlinger::HosBinderDriverServer& server;
684}; 134};
685 135
686class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> { 136class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
@@ -937,7 +387,40 @@ private:
937 387
938class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> { 388class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
939public: 389public:
940 explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 390 IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
391 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
392 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_},
393 hos_binder_driver_server{hos_binder_driver_server_} {
394
395 static const FunctionInfo functions[] = {
396 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
397 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
398 {102, &IApplicationDisplayService::GetManagerDisplayService,
399 "GetManagerDisplayService"},
400 {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
401 "GetIndirectDisplayTransactionService"},
402 {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
403 {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
404 {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
405 {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
406 {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
407 {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
408 {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
409 {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
410 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
411 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
412 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
413 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
414 {2450, &IApplicationDisplayService::GetIndirectLayerImageMap,
415 "GetIndirectLayerImageMap"},
416 {2451, nullptr, "GetIndirectLayerImageCropMap"},
417 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
418 "GetIndirectLayerImageRequiredMemoryInfo"},
419 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
420 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
421 };
422 RegisterHandlers(functions);
423 }
941 424
942private: 425private:
943 enum class ConvertedScaleMode : u64 { 426 enum class ConvertedScaleMode : u64 {
@@ -961,7 +444,7 @@ private:
961 444
962 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 445 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
963 rb.Push(ResultSuccess); 446 rb.Push(ResultSuccess);
964 rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); 447 rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
965 } 448 }
966 449
967 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) { 450 void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
@@ -985,7 +468,7 @@ private:
985 468
986 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 469 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
987 rb.Push(ResultSuccess); 470 rb.Push(ResultSuccess);
988 rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger); 471 rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
989 } 472 }
990 473
991 void OpenDisplay(Kernel::HLERequestContext& ctx) { 474 void OpenDisplay(Kernel::HLERequestContext& ctx) {
@@ -1089,7 +572,7 @@ private:
1089 void ListDisplays(Kernel::HLERequestContext& ctx) { 572 void ListDisplays(Kernel::HLERequestContext& ctx) {
1090 LOG_WARNING(Service_VI, "(STUBBED) called"); 573 LOG_WARNING(Service_VI, "(STUBBED) called");
1091 574
1092 DisplayInfo display_info; 575 const DisplayInfo display_info;
1093 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); 576 ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
1094 IPC::ResponseBuilder rb{ctx, 4}; 577 IPC::ResponseBuilder rb{ctx, 4};
1095 rb.Push(ResultSuccess); 578 rb.Push(ResultSuccess);
@@ -1124,8 +607,8 @@ private:
1124 return; 607 return;
1125 } 608 }
1126 609
1127 NativeWindow native_window{*buffer_queue_id}; 610 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
1128 const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); 611 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
1129 612
1130 IPC::ResponseBuilder rb{ctx, 4}; 613 IPC::ResponseBuilder rb{ctx, 4};
1131 rb.Push(ResultSuccess); 614 rb.Push(ResultSuccess);
@@ -1170,8 +653,8 @@ private:
1170 return; 653 return;
1171 } 654 }
1172 655
1173 NativeWindow native_window{*buffer_queue_id}; 656 const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
1174 const auto buffer_size = ctx.WriteBuffer(native_window.Serialize()); 657 const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
1175 658
1176 IPC::ResponseBuilder rb{ctx, 6}; 659 IPC::ResponseBuilder rb{ctx, 6};
1177 rb.Push(ResultSuccess); 660 rb.Push(ResultSuccess);
@@ -1287,39 +770,9 @@ private:
1287 } 770 }
1288 771
1289 NVFlinger::NVFlinger& nv_flinger; 772 NVFlinger::NVFlinger& nv_flinger;
773 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
1290}; 774};
1291 775
1292IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
1293 NVFlinger::NVFlinger& nv_flinger_)
1294 : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} {
1295 static const FunctionInfo functions[] = {
1296 {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
1297 {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
1298 {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"},
1299 {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
1300 "GetIndirectDisplayTransactionService"},
1301 {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
1302 {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
1303 {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
1304 {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
1305 {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
1306 {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
1307 {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
1308 {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
1309 {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
1310 {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
1311 {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
1312 {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
1313 {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"},
1314 {2451, nullptr, "GetIndirectLayerImageCropMap"},
1315 {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
1316 "GetIndirectLayerImageRequiredMemoryInfo"},
1317 {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
1318 {5203, nullptr, "GetDisplayVsyncEventForDebug"},
1319 };
1320 RegisterHandlers(functions);
1321}
1322
1323static bool IsValidServiceAccess(Permission permission, Policy policy) { 776static bool IsValidServiceAccess(Permission permission, Policy policy) {
1324 if (permission == Permission::User) { 777 if (permission == Permission::User) {
1325 return policy == Policy::User; 778 return policy == Policy::User;
@@ -1333,7 +786,9 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
1333} 786}
1334 787
1335void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, 788void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
1336 NVFlinger::NVFlinger& nv_flinger, Permission permission) { 789 NVFlinger::NVFlinger& nv_flinger,
790 NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
791 Permission permission) {
1337 IPC::RequestParser rp{ctx}; 792 IPC::RequestParser rp{ctx};
1338 const auto policy = rp.PopEnum<Policy>(); 793 const auto policy = rp.PopEnum<Policy>();
1339 794
@@ -1346,14 +801,18 @@ void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System&
1346 801
1347 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 802 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1348 rb.Push(ResultSuccess); 803 rb.Push(ResultSuccess);
1349 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger); 804 rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server);
1350} 805}
1351 806
1352void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, 807void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
1353 NVFlinger::NVFlinger& nv_flinger) { 808 NVFlinger::NVFlinger& nv_flinger,
1354 std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager); 809 NVFlinger::HosBinderDriverServer& hos_binder_driver_server) {
1355 std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager); 810 std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server)
1356 std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager); 811 ->InstallAsService(service_manager);
812 std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server)
813 ->InstallAsService(service_manager);
814 std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server)
815 ->InstallAsService(service_manager);
1357} 816}
1358 817
1359} // namespace Service::VI 818} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 2fd7f8e61..d68f2646b 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -15,8 +15,9 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::SM { 22namespace Service::SM {
22class ServiceManager; 23class ServiceManager;
@@ -47,11 +48,14 @@ enum class Policy {
47 48
48namespace detail { 49namespace detail {
49void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system, 50void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
50 NVFlinger::NVFlinger& nv_flinger, Permission permission); 51 NVFlinger::NVFlinger& nv_flinger,
52 NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
53 Permission permission);
51} // namespace detail 54} // namespace detail
52 55
53/// Registers all VI services with the specified service manager. 56/// Registers all VI services with the specified service manager.
54void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system, 57void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
55 NVFlinger::NVFlinger& nv_flinger); 58 NVFlinger::NVFlinger& nv_flinger,
59 NVFlinger::HosBinderDriverServer& hos_binder_driver_server);
56 60
57} // namespace Service::VI 61} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.cpp b/src/core/hle/service/vi/vi_m.cpp
index 87db1c416..be0255f3d 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {2, &VI_M::GetDisplayService, "GetDisplayService"}, 16 {2, &VI_M::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_M::~VI_M() = default;
22void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::Manager);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_m.h b/src/core/hle/service/vi/vi_m.h
index d79c41beb..efbd34e09 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_M final : public ServiceFramework<VI_M> { 24class VI_M final : public ServiceFramework<VI_M> {
24public: 25public:
25 explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_M() override; 28 ~VI_M() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.cpp b/src/core/hle/service/vi/vi_s.cpp
index 5cd22f7df..7996a6811 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {1, &VI_S::GetDisplayService, "GetDisplayService"}, 16 {1, &VI_S::GetDisplayService, "GetDisplayService"},
15 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_S::~VI_S() = default;
22void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::System);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_s.h b/src/core/hle/service/vi/vi_s.h
index 5f1f8f290..3812c5061 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_S final : public ServiceFramework<VI_S> { 24class VI_S final : public ServiceFramework<VI_S> {
24public: 25public:
25 explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_S() override; 28 ~VI_S() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.cpp b/src/core/hle/service/vi/vi_u.cpp
index 0079d51f0..57c888313 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -8,8 +8,10 @@
8 8
9namespace Service::VI { 9namespace Service::VI {
10 10
11VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_) 11VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
12 : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} { 12 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
13 : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
14 hos_binder_driver_server_} {
13 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
14 {0, &VI_U::GetDisplayService, "GetDisplayService"}, 16 {0, &VI_U::GetDisplayService, "GetDisplayService"},
15 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"}, 17 {1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +24,8 @@ VI_U::~VI_U() = default;
22void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) { 24void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
23 LOG_DEBUG(Service_VI, "called"); 25 LOG_DEBUG(Service_VI, "called");
24 26
25 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User); 27 detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
28 Permission::User);
26} 29}
27 30
28} // namespace Service::VI 31} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_u.h b/src/core/hle/service/vi/vi_u.h
index 8e3885c73..b08e56576 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -15,20 +15,23 @@ class HLERequestContext;
15} 15}
16 16
17namespace Service::NVFlinger { 17namespace Service::NVFlinger {
18class HosBinderDriverServer;
18class NVFlinger; 19class NVFlinger;
19} 20} // namespace Service::NVFlinger
20 21
21namespace Service::VI { 22namespace Service::VI {
22 23
23class VI_U final : public ServiceFramework<VI_U> { 24class VI_U final : public ServiceFramework<VI_U> {
24public: 25public:
25 explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_); 26 explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
27 NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
26 ~VI_U() override; 28 ~VI_U() override;
27 29
28private: 30private:
29 void GetDisplayService(Kernel::HLERequestContext& ctx); 31 void GetDisplayService(Kernel::HLERequestContext& ctx);
30 32
31 NVFlinger::NVFlinger& nv_flinger; 33 NVFlinger::NVFlinger& nv_flinger;
34 NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
32}; 35};
33 36
34} // namespace Service::VI 37} // namespace Service::VI
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index a3e0664b9..0784a165d 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -600,6 +600,10 @@ Errno Socket::SetReuseAddr(bool enable) {
600 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); 600 return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
601} 601}
602 602
603Errno Socket::SetKeepAlive(bool enable) {
604 return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0);
605}
606
603Errno Socket::SetBroadcast(bool enable) { 607Errno Socket::SetBroadcast(bool enable) {
604 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); 608 return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
605} 609}
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h
index a44393325..caaefce7c 100644
--- a/src/core/network/sockets.h
+++ b/src/core/network/sockets.h
@@ -8,7 +8,6 @@
8#include <utility> 8#include <utility>
9 9
10#if defined(_WIN32) 10#if defined(_WIN32)
11#include <winsock.h>
12#elif !YUZU_UNIX 11#elif !YUZU_UNIX
13#error "Platform not implemented" 12#error "Platform not implemented"
14#endif 13#endif
@@ -68,6 +67,8 @@ public:
68 67
69 Errno SetReuseAddr(bool enable); 68 Errno SetReuseAddr(bool enable);
70 69
70 Errno SetKeepAlive(bool enable);
71
71 Errno SetBroadcast(bool enable); 72 Errno SetBroadcast(bool enable);
72 73
73 Errno SetSndBuf(u32 value); 74 Errno SetSndBuf(u32 value);
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 52c43c857..6ef459b7a 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -53,13 +53,13 @@ PerfStats::~PerfStats() {
53} 53}
54 54
55void PerfStats::BeginSystemFrame() { 55void PerfStats::BeginSystemFrame() {
56 std::lock_guard lock{object_mutex}; 56 std::scoped_lock lock{object_mutex};
57 57
58 frame_begin = Clock::now(); 58 frame_begin = Clock::now();
59} 59}
60 60
61void PerfStats::EndSystemFrame() { 61void PerfStats::EndSystemFrame() {
62 std::lock_guard lock{object_mutex}; 62 std::scoped_lock lock{object_mutex};
63 63
64 auto frame_end = Clock::now(); 64 auto frame_end = Clock::now();
65 const auto frame_time = frame_end - frame_begin; 65 const auto frame_time = frame_end - frame_begin;
@@ -79,7 +79,7 @@ void PerfStats::EndGameFrame() {
79} 79}
80 80
81double PerfStats::GetMeanFrametime() const { 81double PerfStats::GetMeanFrametime() const {
82 std::lock_guard lock{object_mutex}; 82 std::scoped_lock lock{object_mutex};
83 83
84 if (current_index <= IgnoreFrames) { 84 if (current_index <= IgnoreFrames) {
85 return 0; 85 return 0;
@@ -91,7 +91,7 @@ double PerfStats::GetMeanFrametime() const {
91} 91}
92 92
93PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { 93PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
94 std::lock_guard lock{object_mutex}; 94 std::scoped_lock lock{object_mutex};
95 95
96 const auto now = Clock::now(); 96 const auto now = Clock::now();
97 // Walltime elapsed since stats were reset 97 // Walltime elapsed since stats were reset
@@ -120,7 +120,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
120} 120}
121 121
122double PerfStats::GetLastFrameTimeScale() const { 122double PerfStats::GetLastFrameTimeScale() const {
123 std::lock_guard lock{object_mutex}; 123 std::scoped_lock lock{object_mutex};
124 124
125 constexpr double FRAME_LENGTH = 1.0 / 60; 125 constexpr double FRAME_LENGTH = 1.0 / 60;
126 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; 126 return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index d4becdc0a..89faed6ee 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -8,7 +8,6 @@
8 8
9#include <fmt/chrono.h> 9#include <fmt/chrono.h>
10#include <fmt/format.h> 10#include <fmt/format.h>
11#include <fmt/ostream.h>
12#include <nlohmann/json.hpp> 11#include <nlohmann/json.hpp>
13 12
14#include "common/fs/file.h" 13#include "common/fs/file.h"
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 032c71aff..c81dc0e52 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -80,7 +80,7 @@ bool Freezer::IsActive() const {
80} 80}
81 81
82void Freezer::Clear() { 82void Freezer::Clear() {
83 std::lock_guard lock{entries_mutex}; 83 std::scoped_lock lock{entries_mutex};
84 84
85 LOG_DEBUG(Common_Memory, "Clearing all frozen memory values."); 85 LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
86 86
@@ -88,7 +88,7 @@ void Freezer::Clear() {
88} 88}
89 89
90u64 Freezer::Freeze(VAddr address, u32 width) { 90u64 Freezer::Freeze(VAddr address, u32 width) {
91 std::lock_guard lock{entries_mutex}; 91 std::scoped_lock lock{entries_mutex};
92 92
93 const auto current_value = MemoryReadWidth(memory, width, address); 93 const auto current_value = MemoryReadWidth(memory, width, address);
94 entries.push_back({address, width, current_value}); 94 entries.push_back({address, width, current_value});
@@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) {
101} 101}
102 102
103void Freezer::Unfreeze(VAddr address) { 103void Freezer::Unfreeze(VAddr address) {
104 std::lock_guard lock{entries_mutex}; 104 std::scoped_lock lock{entries_mutex};
105 105
106 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); 106 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
107 107
@@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) {
109} 109}
110 110
111bool Freezer::IsFrozen(VAddr address) const { 111bool Freezer::IsFrozen(VAddr address) const {
112 std::lock_guard lock{entries_mutex}; 112 std::scoped_lock lock{entries_mutex};
113 113
114 return FindEntry(address) != entries.cend(); 114 return FindEntry(address) != entries.cend();
115} 115}
116 116
117void Freezer::SetFrozenValue(VAddr address, u64 value) { 117void Freezer::SetFrozenValue(VAddr address, u64 value) {
118 std::lock_guard lock{entries_mutex}; 118 std::scoped_lock lock{entries_mutex};
119 119
120 const auto iter = FindEntry(address); 120 const auto iter = FindEntry(address);
121 121
@@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
132} 132}
133 133
134std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { 134std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
135 std::lock_guard lock{entries_mutex}; 135 std::scoped_lock lock{entries_mutex};
136 136
137 const auto iter = FindEntry(address); 137 const auto iter = FindEntry(address);
138 138
@@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
144} 144}
145 145
146std::vector<Freezer::Entry> Freezer::GetEntries() const { 146std::vector<Freezer::Entry> Freezer::GetEntries() const {
147 std::lock_guard lock{entries_mutex}; 147 std::scoped_lock lock{entries_mutex};
148 148
149 return entries; 149 return entries;
150} 150}
@@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
165 return; 165 return;
166 } 166 }
167 167
168 std::lock_guard lock{entries_mutex}; 168 std::scoped_lock lock{entries_mutex};
169 169
170 for (const auto& entry : entries) { 170 for (const auto& entry : entries) {
171 LOG_DEBUG(Common_Memory, 171 LOG_DEBUG(Common_Memory,
@@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
178} 178}
179 179
180void Freezer::FillEntryReads() { 180void Freezer::FillEntryReads() {
181 std::lock_guard lock{entries_mutex}; 181 std::scoped_lock lock{entries_mutex};
182 182
183 LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values."); 183 LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
184 184