summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt54
-rw-r--r--src/core/arm/arm_interface.cpp228
-rw-r--r--src/core/arm/arm_interface.h21
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp148
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h19
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp202
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h18
-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.cpp5
-rw-r--r--src/core/core_timing.h6
-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/frontend/emu_window.h11
-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.cpp78
-rw-r--r--src/core/hid/emulated_devices.h35
-rw-r--r--src/core/hid/hid_types.h5
-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/global_scheduler_context.h3
-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.cpp530
-rw-r--r--src/core/hle/kernel/k_page_table.h65
-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.cpp9
-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_spin_lock.cpp39
-rw-r--r--src/core/hle/kernel/k_spin_lock.h4
-rw-r--r--src/core/hle/kernel/k_thread.cpp49
-rw-r--r--src/core/hle/kernel/k_thread.h22
-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.cpp166
-rw-r--r--src/core/hle/kernel/kernel.h49
-rw-r--r--src/core/hle/kernel/physical_core.cpp4
-rw-r--r--src/core/hle/kernel/physical_core.h7
-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.cpp307
-rw-r--r--src/core/hle/service/hid/controllers/npad.h152
-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/errors.h4
-rw-r--r--src/core/hle/service/hid/hid.cpp160
-rw-r--r--src/core/hle/service/hid/hid.h1
-rw-r--r--src/core/hle/service/hid/hidbus.cpp531
-rw-r--r--src/core/hle/service/hid/hidbus.h131
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.cpp72
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h179
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp286
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h254
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp51
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h39
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp52
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h39
-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.cpp112
-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.h23
-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
191 files changed, 9426 insertions, 3113 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5db6a1b3a..62230bae0 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
@@ -423,6 +434,8 @@ add_library(core STATIC
423 hle/service/grc/grc.h 434 hle/service/grc/grc.h
424 hle/service/hid/hid.cpp 435 hle/service/hid/hid.cpp
425 hle/service/hid/hid.h 436 hle/service/hid/hid.h
437 hle/service/hid/hidbus.cpp
438 hle/service/hid/hidbus.h
426 hle/service/hid/irs.cpp 439 hle/service/hid/irs.cpp
427 hle/service/hid/irs.h 440 hle/service/hid/irs.h
428 hle/service/hid/ring_lifo.h 441 hle/service/hid/ring_lifo.h
@@ -449,6 +462,18 @@ add_library(core STATIC
449 hle/service/hid/controllers/touchscreen.h 462 hle/service/hid/controllers/touchscreen.h
450 hle/service/hid/controllers/xpad.cpp 463 hle/service/hid/controllers/xpad.cpp
451 hle/service/hid/controllers/xpad.h 464 hle/service/hid/controllers/xpad.h
465 hle/service/hid/hidbus/hidbus_base.cpp
466 hle/service/hid/hidbus/hidbus_base.h
467 hle/service/hid/hidbus/ringcon.cpp
468 hle/service/hid/hidbus/ringcon.h
469 hle/service/hid/hidbus/starlink.cpp
470 hle/service/hid/hidbus/starlink.h
471 hle/service/hid/hidbus/stubbed.cpp
472 hle/service/hid/hidbus/stubbed.h
473 hle/service/jit/jit_context.cpp
474 hle/service/jit/jit_context.h
475 hle/service/jit/jit.cpp
476 hle/service/jit/jit.h
452 hle/service/lbl/lbl.cpp 477 hle/service/lbl/lbl.cpp
453 hle/service/lbl/lbl.h 478 hle/service/lbl/lbl.h
454 hle/service/ldn/errors.h 479 hle/service/ldn/errors.h
@@ -526,10 +551,35 @@ add_library(core STATIC
526 hle/service/nvdrv/nvmemp.h 551 hle/service/nvdrv/nvmemp.h
527 hle/service/nvdrv/syncpoint_manager.cpp 552 hle/service/nvdrv/syncpoint_manager.cpp
528 hle/service/nvdrv/syncpoint_manager.h 553 hle/service/nvdrv/syncpoint_manager.h
529 hle/service/nvflinger/buffer_queue.cpp 554 hle/service/nvflinger/binder.h
530 hle/service/nvflinger/buffer_queue.h 555 hle/service/nvflinger/buffer_item.h
556 hle/service/nvflinger/buffer_item_consumer.cpp
557 hle/service/nvflinger/buffer_item_consumer.h
558 hle/service/nvflinger/buffer_queue_consumer.cpp
559 hle/service/nvflinger/buffer_queue_consumer.h
560 hle/service/nvflinger/buffer_queue_core.cpp
561 hle/service/nvflinger/buffer_queue_core.h
562 hle/service/nvflinger/buffer_queue_defs.h
563 hle/service/nvflinger/buffer_queue_producer.cpp
564 hle/service/nvflinger/buffer_queue_producer.h
565 hle/service/nvflinger/buffer_slot.h
566 hle/service/nvflinger/buffer_transform_flags.h
567 hle/service/nvflinger/consumer_base.cpp
568 hle/service/nvflinger/consumer_base.h
569 hle/service/nvflinger/consumer_listener.h
570 hle/service/nvflinger/graphic_buffer_producer.cpp
571 hle/service/nvflinger/graphic_buffer_producer.h
572 hle/service/nvflinger/hos_binder_driver_server.cpp
573 hle/service/nvflinger/hos_binder_driver_server.h
531 hle/service/nvflinger/nvflinger.cpp 574 hle/service/nvflinger/nvflinger.cpp
532 hle/service/nvflinger/nvflinger.h 575 hle/service/nvflinger/nvflinger.h
576 hle/service/nvflinger/parcel.h
577 hle/service/nvflinger/pixel_format.h
578 hle/service/nvflinger/producer_listener.h
579 hle/service/nvflinger/status.h
580 hle/service/nvflinger/ui/fence.h
581 hle/service/nvflinger/ui/graphic_buffer.h
582 hle/service/nvflinger/window.h
533 hle/service/olsc/olsc.cpp 583 hle/service/olsc/olsc.cpp
534 hle/service/olsc/olsc.h 584 hle/service/olsc/olsc.h
535 hle/service/pcie/pcie.cpp 585 hle/service/pcie/pcie.cpp
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 0951e1976..2a9390e26 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -8,226 +8,41 @@
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 { 17#include "core/arm/dynarmic/arm_dynarmic_32.h"
16namespace { 18#include "core/arm/dynarmic/arm_dynarmic_64.h"
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 19
100 const auto string_table_address = text_offset + string_table_offset; 20namespace Core {
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 21
140constexpr u64 SEGMENT_BASE = 0x7100000000ull; 22constexpr u64 SEGMENT_BASE = 0x7100000000ull;
141 23
142std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext( 24std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
143 System& system, const ThreadContext64& ctx) { 25 Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
144 std::vector<BacktraceEntry> out; 26 return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx);
145 auto& memory = system.Memory();
146
147 auto fp = ctx.cpu_registers[29];
148 auto lr = ctx.cpu_registers[30];
149 while (true) {
150 out.push_back({
151 .module = "",
152 .address = 0,
153 .original_address = lr,
154 .offset = 0,
155 .name = {},
156 });
157
158 if (fp == 0) {
159 break;
160 }
161
162 lr = memory.Read64(fp + 8) - 4;
163 fp = memory.Read64(fp);
164 }
165
166 std::map<VAddr, std::string> modules;
167 auto& loader{system.GetAppLoader()};
168 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
169 return {};
170 }
171
172 std::map<std::string, Symbols> symbols;
173 for (const auto& module : modules) {
174 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
175 }
176
177 for (auto& entry : out) {
178 VAddr base = 0;
179 for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
180 const auto& module{*iter};
181 if (entry.original_address >= module.first) {
182 entry.module = module.second;
183 base = module.first;
184 break;
185 }
186 }
187
188 entry.offset = entry.original_address - base;
189 entry.address = SEGMENT_BASE + entry.offset;
190
191 if (entry.module.empty())
192 entry.module = "unknown";
193
194 const auto symbol_set = symbols.find(entry.module);
195 if (symbol_set != symbols.end()) {
196 const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
197 if (symbol.has_value()) {
198 // TODO(DarkLordZach): Add demangling of symbol names.
199 entry.name = *symbol;
200 }
201 }
202 }
203
204 return out;
205} 27}
206 28
207std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const { 29std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
208 std::vector<BacktraceEntry> out; 30 Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
209 auto& memory = system.Memory(); 31 return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx);
210 32}
211 auto fp = GetReg(29);
212 auto lr = GetReg(30);
213 while (true) {
214 out.push_back({"", 0, lr, 0, ""});
215 if (!fp) {
216 break;
217 }
218 lr = memory.Read64(fp + 8) - 4;
219 fp = memory.Read64(fp);
220 }
221 33
34void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
222 std::map<VAddr, std::string> modules; 35 std::map<VAddr, std::string> modules;
223 auto& loader{system.GetAppLoader()}; 36 auto& loader{system.GetAppLoader()};
224 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) { 37 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
225 return {}; 38 return;
226 } 39 }
227 40
228 std::map<std::string, Symbols> symbols; 41 std::map<std::string, Symbols::Symbols> symbols;
229 for (const auto& module : modules) { 42 for (const auto& module : modules) {
230 symbols.insert_or_assign(module.second, GetSymbols(module.first, memory)); 43 symbols.insert_or_assign(module.second,
44 Symbols::GetSymbols(module.first, system.Memory(),
45 system.CurrentProcess()->Is64BitProcess()));
231 } 46 }
232 47
233 for (auto& entry : out) { 48 for (auto& entry : out) {
@@ -244,24 +59,23 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
244 entry.offset = entry.original_address - base; 59 entry.offset = entry.original_address - base;
245 entry.address = SEGMENT_BASE + entry.offset; 60 entry.address = SEGMENT_BASE + entry.offset;
246 61
247 if (entry.module.empty()) 62 if (entry.module.empty()) {
248 entry.module = "unknown"; 63 entry.module = "unknown";
64 }
249 65
250 const auto symbol_set = symbols.find(entry.module); 66 const auto symbol_set = symbols.find(entry.module);
251 if (symbol_set != symbols.end()) { 67 if (symbol_set != symbols.end()) {
252 const auto symbol = GetSymbolName(symbol_set->second, entry.offset); 68 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
253 if (symbol.has_value()) { 69 if (symbol.has_value()) {
254 // TODO(DarkLordZach): Add demangling of symbol names. 70 // TODO(DarkLordZach): Add demangling of symbol names.
255 entry.name = *symbol; 71 entry.name = *symbol;
256 } 72 }
257 } 73 }
258 } 74 }
259
260 return out;
261} 75}
262 76
263void ARM_Interface::LogBacktrace() const { 77void ARM_Interface::LogBacktrace() const {
264 const VAddr sp = GetReg(13); 78 const VAddr sp = GetSP();
265 const VAddr pc = GetPC(); 79 const VAddr pc = GetPC();
266 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); 80 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
267 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address", 81 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c60322442..bcec4b3b8 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -101,6 +101,12 @@ public:
101 virtual u64 GetPC() const = 0; 101 virtual u64 GetPC() const = 0;
102 102
103 /** 103 /**
104 * Get the current Stack Pointer
105 * @return Returns current SP
106 */
107 virtual u64 GetSP() const = 0;
108
109 /**
104 * Get an ARM register 110 * Get an ARM register
105 * @param index Register index 111 * @param index Register index
106 * @return Returns the value in the register 112 * @return Returns the value in the register
@@ -171,6 +177,9 @@ public:
171 /// Prepare core for thread reschedule (if needed to correctly handle state) 177 /// Prepare core for thread reschedule (if needed to correctly handle state)
172 virtual void PrepareReschedule() = 0; 178 virtual void PrepareReschedule() = 0;
173 179
180 /// Signal an interrupt and ask the core to halt as soon as possible.
181 virtual void SignalInterrupt() = 0;
182
174 struct BacktraceEntry { 183 struct BacktraceEntry {
175 std::string module; 184 std::string module;
176 u64 address; 185 u64 address;
@@ -180,16 +189,12 @@ public:
180 }; 189 };
181 190
182 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system, 191 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
192 const ThreadContext32& ctx);
193 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
183 const ThreadContext64& ctx); 194 const ThreadContext64& ctx);
184 195
185 std::vector<BacktraceEntry> GetBacktrace() const; 196 virtual std::vector<BacktraceEntry> GetBacktrace() const = 0;
186 197
187 /// fp (= r29) points to the last frame record.
188 /// Note that this is the frame record for the *previous* frame, not the current one.
189 /// Note we need to subtract 4 from our last read to get the proper address
190 /// Frame records are two words long:
191 /// fp+0 : pointer to previous frame record
192 /// fp+8 : value of lr for frame
193 void LogBacktrace() const; 198 void LogBacktrace() const;
194 199
195protected: 200protected:
@@ -197,6 +202,8 @@ protected:
197 System& system; 202 System& system;
198 CPUInterrupts& interrupt_handlers; 203 CPUInterrupts& interrupt_handlers;
199 bool uses_wall_clock; 204 bool uses_wall_clock;
205
206 static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
200}; 207};
201 208
202} // namespace Core 209} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 286976623..da5659046 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.load()->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,11 +145,19 @@ 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 = 512_MiB; 151 config.code_cache_size = 512_MiB;
152 config.far_code_offset = 400_MiB; 152 config.far_code_offset = 400_MiB;
153 153
154 // null_jit
155 if (!page_table) {
156 // Don't waste too much memory on null_jit
157 config.code_cache_size = 8_MiB;
158 config.far_code_offset = 4_MiB;
159 }
160
154 // Safe optimizations 161 // Safe optimizations
155 if (Settings::values.cpu_debug_mode) { 162 if (Settings::values.cpu_debug_mode) {
156 if (!Settings::values.cpuopt_page_tables) { 163 if (!Settings::values.cpuopt_page_tables) {
@@ -186,35 +193,41 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
186 if (!Settings::values.cpuopt_recompile_exclusives) { 193 if (!Settings::values.cpuopt_recompile_exclusives) {
187 config.recompile_on_exclusive_fastmem_failure = false; 194 config.recompile_on_exclusive_fastmem_failure = false;
188 } 195 }
189 } 196 } else {
197 // Unsafe optimizations
198 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
199 config.unsafe_optimizations = true;
200 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
201 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
202 }
203 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
205 }
206 if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
208 }
209 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
210 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
211 }
212 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
213 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
214 }
215 }
190 216
191 // Unsafe optimizations 217 // Curated optimizations
192 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 218 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
193 config.unsafe_optimizations = true; 219 config.unsafe_optimizations = true;
194 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
195 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 220 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; 221 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
202 }
203 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
204 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 222 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
205 }
206 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
207 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 223 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
208 } 224 }
209 }
210 225
211 // Curated optimizations 226 // Paranoia mode for debugging optimizations
212 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 227 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
213 config.unsafe_optimizations = true; 228 config.unsafe_optimizations = false;
214 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 229 config.optimizations = Dynarmic::no_optimizations;
215 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue; 230 }
216 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
217 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
218 } 231 }
219 232
220 return std::make_unique<Dynarmic::A32::Jit>(config); 233 return std::make_unique<Dynarmic::A32::Jit>(config);
@@ -222,20 +235,18 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
222 235
223void ARM_Dynarmic_32::Run() { 236void ARM_Dynarmic_32::Run() {
224 while (true) { 237 while (true) {
225 jit->Run(); 238 const auto hr = jit.load()->Run();
226 if (!svc_called) { 239 if (Has(hr, svc_call)) {
227 break; 240 Kernel::Svc::Call(system, svc_swi);
228 } 241 }
229 svc_called = false; 242 if (Has(hr, break_loop) || !uses_wall_clock) {
230 Kernel::Svc::Call(system, svc_swi);
231 if (shutdown) {
232 break; 243 break;
233 } 244 }
234 } 245 }
235} 246}
236 247
237void ARM_Dynarmic_32::Step() { 248void ARM_Dynarmic_32::Step() {
238 jit->Step(); 249 jit.load()->Step();
239} 250}
240 251
241ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, 252ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
@@ -245,24 +256,28 @@ ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handl
245 cb(std::make_unique<DynarmicCallbacks32>(*this)), 256 cb(std::make_unique<DynarmicCallbacks32>(*this)),
246 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, 257 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
247 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 258 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
248 jit(MakeJit(nullptr)) {} 259 null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
249 260
250ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; 261ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
251 262
252void ARM_Dynarmic_32::SetPC(u64 pc) { 263void ARM_Dynarmic_32::SetPC(u64 pc) {
253 jit->Regs()[15] = static_cast<u32>(pc); 264 jit.load()->Regs()[15] = static_cast<u32>(pc);
254} 265}
255 266
256u64 ARM_Dynarmic_32::GetPC() const { 267u64 ARM_Dynarmic_32::GetPC() const {
257 return jit->Regs()[15]; 268 return jit.load()->Regs()[15];
269}
270
271u64 ARM_Dynarmic_32::GetSP() const {
272 return jit.load()->Regs()[13];
258} 273}
259 274
260u64 ARM_Dynarmic_32::GetReg(int index) const { 275u64 ARM_Dynarmic_32::GetReg(int index) const {
261 return jit->Regs()[index]; 276 return jit.load()->Regs()[index];
262} 277}
263 278
264void ARM_Dynarmic_32::SetReg(int index, u64 value) { 279void ARM_Dynarmic_32::SetReg(int index, u64 value) {
265 jit->Regs()[index] = static_cast<u32>(value); 280 jit.load()->Regs()[index] = static_cast<u32>(value);
266} 281}
267 282
268u128 ARM_Dynarmic_32::GetVectorReg(int index) const { 283u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
@@ -272,11 +287,11 @@ u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
272void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {} 287void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
273 288
274u32 ARM_Dynarmic_32::GetPSTATE() const { 289u32 ARM_Dynarmic_32::GetPSTATE() const {
275 return jit->Cpsr(); 290 return jit.load()->Cpsr();
276} 291}
277 292
278void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) { 293void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
279 jit->SetCpsr(cpsr); 294 jit.load()->SetCpsr(cpsr);
280} 295}
281 296
282u64 ARM_Dynarmic_32::GetTlsAddress() const { 297u64 ARM_Dynarmic_32::GetTlsAddress() const {
@@ -297,7 +312,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
297 312
298void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { 313void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
299 Dynarmic::A32::Context context; 314 Dynarmic::A32::Context context;
300 jit->SaveContext(context); 315 jit.load()->SaveContext(context);
301 ctx.cpu_registers = context.Regs(); 316 ctx.cpu_registers = context.Regs();
302 ctx.extension_registers = context.ExtRegs(); 317 ctx.extension_registers = context.ExtRegs();
303 ctx.cpsr = context.Cpsr(); 318 ctx.cpsr = context.Cpsr();
@@ -310,24 +325,27 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
310 context.ExtRegs() = ctx.extension_registers; 325 context.ExtRegs() = ctx.extension_registers;
311 context.SetCpsr(ctx.cpsr); 326 context.SetCpsr(ctx.cpsr);
312 context.SetFpscr(ctx.fpscr); 327 context.SetFpscr(ctx.fpscr);
313 jit->LoadContext(context); 328 jit.load()->LoadContext(context);
314} 329}
315 330
316void ARM_Dynarmic_32::PrepareReschedule() { 331void ARM_Dynarmic_32::PrepareReschedule() {
317 jit->HaltExecution(); 332 jit.load()->HaltExecution(break_loop);
318 shutdown = true; 333}
334
335void ARM_Dynarmic_32::SignalInterrupt() {
336 jit.load()->HaltExecution(break_loop);
319} 337}
320 338
321void ARM_Dynarmic_32::ClearInstructionCache() { 339void ARM_Dynarmic_32::ClearInstructionCache() {
322 jit->ClearCache(); 340 jit.load()->ClearCache();
323} 341}
324 342
325void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) { 343void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
326 jit->InvalidateCacheRange(static_cast<u32>(addr), size); 344 jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size);
327} 345}
328 346
329void ARM_Dynarmic_32::ClearExclusiveState() { 347void ARM_Dynarmic_32::ClearExclusiveState() {
330 jit->ClearExclusiveState(); 348 jit.load()->ClearExclusiveState();
331} 349}
332 350
333void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table, 351void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
@@ -338,13 +356,29 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
338 auto key = std::make_pair(&page_table, new_address_space_size_in_bits); 356 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
339 auto iter = jit_cache.find(key); 357 auto iter = jit_cache.find(key);
340 if (iter != jit_cache.end()) { 358 if (iter != jit_cache.end()) {
341 jit = iter->second; 359 jit.store(iter->second.get());
342 LoadContext(ctx); 360 LoadContext(ctx);
343 return; 361 return;
344 } 362 }
345 jit = MakeJit(&page_table); 363 std::shared_ptr new_jit = MakeJit(&page_table);
364 jit.store(new_jit.get());
346 LoadContext(ctx); 365 LoadContext(ctx);
347 jit_cache.emplace(key, jit); 366 jit_cache.emplace(key, std::move(new_jit));
367}
368
369std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
370 u64 sp, u64 lr) {
371 // No way to get accurate stack traces in A32 yet
372 return {};
373}
374
375std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
376 System& system, const ThreadContext32& ctx) {
377 return GetBacktrace(system, ctx.cpu_registers[13], ctx.cpu_registers[14]);
378}
379
380std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
381 return GetBacktrace(system, GetReg(13), GetReg(14));
348} 382}
349 383
350} // namespace Core 384} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 5d47b600d..1b628f94d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -34,6 +35,7 @@ public:
34 35
35 void SetPC(u64 pc) override; 36 void SetPC(u64 pc) override;
36 u64 GetPC() const override; 37 u64 GetPC() const override;
38 u64 GetSP() const override;
37 u64 GetReg(int index) const override; 39 u64 GetReg(int index) const override;
38 void SetReg(int index, u64 value) override; 40 void SetReg(int index, u64 value) override;
39 u128 GetVectorReg(int index) const override; 41 u128 GetVectorReg(int index) const override;
@@ -57,6 +59,7 @@ public:
57 void LoadContext(const ThreadContext64& ctx) override {} 59 void LoadContext(const ThreadContext64& ctx) override {}
58 60
59 void PrepareReschedule() override; 61 void PrepareReschedule() override;
62 void SignalInterrupt() override;
60 void ClearExclusiveState() override; 63 void ClearExclusiveState() override;
61 64
62 void ClearInstructionCache() override; 65 void ClearInstructionCache() override;
@@ -64,9 +67,16 @@ public:
64 void PageTableChanged(Common::PageTable& new_page_table, 67 void PageTableChanged(Common::PageTable& new_page_table,
65 std::size_t new_address_space_size_in_bits) override; 68 std::size_t new_address_space_size_in_bits) override;
66 69
70 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
71 const ThreadContext32& ctx);
72
73 std::vector<BacktraceEntry> GetBacktrace() const override;
74
67private: 75private:
68 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const; 76 std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
69 77
78 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 sp, u64 lr);
79
70 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; 80 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
71 using JitCacheType = 81 using JitCacheType =
72 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>; 82 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
@@ -79,13 +89,14 @@ private:
79 std::shared_ptr<DynarmicCP15> cp15; 89 std::shared_ptr<DynarmicCP15> cp15;
80 std::size_t core_index; 90 std::size_t core_index;
81 DynarmicExclusiveMonitor& exclusive_monitor; 91 DynarmicExclusiveMonitor& exclusive_monitor;
82 std::shared_ptr<Dynarmic::A32::Jit> jit; 92
93 std::shared_ptr<Dynarmic::A32::Jit> null_jit;
94
95 // A raw pointer here is fine; we never delete Jit instances.
96 std::atomic<Dynarmic::A32::Jit*> jit;
83 97
84 // SVC callback 98 // SVC callback
85 u32 svc_swi{}; 99 u32 svc_swi{};
86 bool svc_called{};
87
88 bool shutdown{};
89}; 100};
90 101
91} // namespace Core 102} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index d96226c41..871d9d10e 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.load()->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.load()->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,11 +206,19 @@ 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 = 512_MiB; 212 config.code_cache_size = 512_MiB;
212 config.far_code_offset = 400_MiB; 213 config.far_code_offset = 400_MiB;
213 214
215 // null_jit
216 if (!page_table) {
217 // Don't waste too much memory on null_jit
218 config.code_cache_size = 8_MiB;
219 config.far_code_offset = 4_MiB;
220 }
221
214 // Safe optimizations 222 // Safe optimizations
215 if (Settings::values.cpu_debug_mode) { 223 if (Settings::values.cpu_debug_mode) {
216 if (!Settings::values.cpuopt_page_tables) { 224 if (!Settings::values.cpuopt_page_tables) {
@@ -246,35 +254,41 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
246 if (!Settings::values.cpuopt_recompile_exclusives) { 254 if (!Settings::values.cpuopt_recompile_exclusives) {
247 config.recompile_on_exclusive_fastmem_failure = false; 255 config.recompile_on_exclusive_fastmem_failure = false;
248 } 256 }
249 } 257 } else {
258 // Unsafe optimizations
259 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
260 config.unsafe_optimizations = true;
261 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
262 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
263 }
264 if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
265 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
266 }
267 if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
268 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
269 }
270 if (Settings::values.cpuopt_unsafe_fastmem_check) {
271 config.fastmem_address_space_bits = 64;
272 }
273 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
275 }
276 }
250 277
251 // Unsafe optimizations 278 // Curated optimizations
252 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) { 279 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
253 config.unsafe_optimizations = true; 280 config.unsafe_optimizations = true;
254 if (Settings::values.cpuopt_unsafe_unfuse_fma) {
255 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 281 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; 282 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
262 }
263 if (Settings::values.cpuopt_unsafe_fastmem_check) {
264 config.fastmem_address_space_bits = 64; 283 config.fastmem_address_space_bits = 64;
265 }
266 if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
267 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor; 284 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
268 } 285 }
269 }
270 286
271 // Curated optimizations 287 // Paranoia mode for debugging optimizations
272 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) { 288 if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
273 config.unsafe_optimizations = true; 289 config.unsafe_optimizations = false;
274 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; 290 config.optimizations = Dynarmic::no_optimizations;
275 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN; 291 }
276 config.fastmem_address_space_bits = 64;
277 config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
278 } 292 }
279 293
280 return std::make_shared<Dynarmic::A64::Jit>(config); 294 return std::make_shared<Dynarmic::A64::Jit>(config);
@@ -282,20 +296,18 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
282 296
283void ARM_Dynarmic_64::Run() { 297void ARM_Dynarmic_64::Run() {
284 while (true) { 298 while (true) {
285 jit->Run(); 299 const auto hr = jit.load()->Run();
286 if (!svc_called) { 300 if (Has(hr, svc_call)) {
287 break; 301 Kernel::Svc::Call(system, svc_swi);
288 } 302 }
289 svc_called = false; 303 if (Has(hr, break_loop) || !uses_wall_clock) {
290 Kernel::Svc::Call(system, svc_swi);
291 if (shutdown) {
292 break; 304 break;
293 } 305 }
294 } 306 }
295} 307}
296 308
297void ARM_Dynarmic_64::Step() { 309void ARM_Dynarmic_64::Step() {
298 jit->Step(); 310 jit.load()->Step();
299} 311}
300 312
301ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, 313ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
@@ -304,40 +316,44 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handl
304 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_}, 316 : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
305 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, 317 cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
306 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, 318 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
307 jit(MakeJit(nullptr, 48)) {} 319 null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
308 320
309ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; 321ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
310 322
311void ARM_Dynarmic_64::SetPC(u64 pc) { 323void ARM_Dynarmic_64::SetPC(u64 pc) {
312 jit->SetPC(pc); 324 jit.load()->SetPC(pc);
313} 325}
314 326
315u64 ARM_Dynarmic_64::GetPC() const { 327u64 ARM_Dynarmic_64::GetPC() const {
316 return jit->GetPC(); 328 return jit.load()->GetPC();
329}
330
331u64 ARM_Dynarmic_64::GetSP() const {
332 return jit.load()->GetSP();
317} 333}
318 334
319u64 ARM_Dynarmic_64::GetReg(int index) const { 335u64 ARM_Dynarmic_64::GetReg(int index) const {
320 return jit->GetRegister(index); 336 return jit.load()->GetRegister(index);
321} 337}
322 338
323void ARM_Dynarmic_64::SetReg(int index, u64 value) { 339void ARM_Dynarmic_64::SetReg(int index, u64 value) {
324 jit->SetRegister(index, value); 340 jit.load()->SetRegister(index, value);
325} 341}
326 342
327u128 ARM_Dynarmic_64::GetVectorReg(int index) const { 343u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
328 return jit->GetVector(index); 344 return jit.load()->GetVector(index);
329} 345}
330 346
331void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) { 347void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
332 jit->SetVector(index, value); 348 jit.load()->SetVector(index, value);
333} 349}
334 350
335u32 ARM_Dynarmic_64::GetPSTATE() const { 351u32 ARM_Dynarmic_64::GetPSTATE() const {
336 return jit->GetPstate(); 352 return jit.load()->GetPstate();
337} 353}
338 354
339void ARM_Dynarmic_64::SetPSTATE(u32 pstate) { 355void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
340 jit->SetPstate(pstate); 356 jit.load()->SetPstate(pstate);
341} 357}
342 358
343u64 ARM_Dynarmic_64::GetTlsAddress() const { 359u64 ARM_Dynarmic_64::GetTlsAddress() const {
@@ -357,42 +373,47 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
357} 373}
358 374
359void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { 375void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
360 ctx.cpu_registers = jit->GetRegisters(); 376 Dynarmic::A64::Jit* j = jit.load();
361 ctx.sp = jit->GetSP(); 377 ctx.cpu_registers = j->GetRegisters();
362 ctx.pc = jit->GetPC(); 378 ctx.sp = j->GetSP();
363 ctx.pstate = jit->GetPstate(); 379 ctx.pc = j->GetPC();
364 ctx.vector_registers = jit->GetVectors(); 380 ctx.pstate = j->GetPstate();
365 ctx.fpcr = jit->GetFpcr(); 381 ctx.vector_registers = j->GetVectors();
366 ctx.fpsr = jit->GetFpsr(); 382 ctx.fpcr = j->GetFpcr();
383 ctx.fpsr = j->GetFpsr();
367 ctx.tpidr = cb->tpidr_el0; 384 ctx.tpidr = cb->tpidr_el0;
368} 385}
369 386
370void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { 387void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
371 jit->SetRegisters(ctx.cpu_registers); 388 Dynarmic::A64::Jit* j = jit.load();
372 jit->SetSP(ctx.sp); 389 j->SetRegisters(ctx.cpu_registers);
373 jit->SetPC(ctx.pc); 390 j->SetSP(ctx.sp);
374 jit->SetPstate(ctx.pstate); 391 j->SetPC(ctx.pc);
375 jit->SetVectors(ctx.vector_registers); 392 j->SetPstate(ctx.pstate);
376 jit->SetFpcr(ctx.fpcr); 393 j->SetVectors(ctx.vector_registers);
377 jit->SetFpsr(ctx.fpsr); 394 j->SetFpcr(ctx.fpcr);
395 j->SetFpsr(ctx.fpsr);
378 SetTPIDR_EL0(ctx.tpidr); 396 SetTPIDR_EL0(ctx.tpidr);
379} 397}
380 398
381void ARM_Dynarmic_64::PrepareReschedule() { 399void ARM_Dynarmic_64::PrepareReschedule() {
382 jit->HaltExecution(); 400 jit.load()->HaltExecution(break_loop);
383 shutdown = true; 401}
402
403void ARM_Dynarmic_64::SignalInterrupt() {
404 jit.load()->HaltExecution(break_loop);
384} 405}
385 406
386void ARM_Dynarmic_64::ClearInstructionCache() { 407void ARM_Dynarmic_64::ClearInstructionCache() {
387 jit->ClearCache(); 408 jit.load()->ClearCache();
388} 409}
389 410
390void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) { 411void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
391 jit->InvalidateCacheRange(addr, size); 412 jit.load()->InvalidateCacheRange(addr, size);
392} 413}
393 414
394void ARM_Dynarmic_64::ClearExclusiveState() { 415void ARM_Dynarmic_64::ClearExclusiveState() {
395 jit->ClearExclusiveState(); 416 jit.load()->ClearExclusiveState();
396} 417}
397 418
398void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table, 419void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
@@ -403,13 +424,48 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
403 auto key = std::make_pair(&page_table, new_address_space_size_in_bits); 424 auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
404 auto iter = jit_cache.find(key); 425 auto iter = jit_cache.find(key);
405 if (iter != jit_cache.end()) { 426 if (iter != jit_cache.end()) {
406 jit = iter->second; 427 jit.store(iter->second.get());
407 LoadContext(ctx); 428 LoadContext(ctx);
408 return; 429 return;
409 } 430 }
410 jit = MakeJit(&page_table, new_address_space_size_in_bits); 431 std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits);
432 jit.store(new_jit.get());
411 LoadContext(ctx); 433 LoadContext(ctx);
412 jit_cache.emplace(key, jit); 434 jit_cache.emplace(key, std::move(new_jit));
435}
436
437std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
438 u64 fp, u64 lr) {
439 std::vector<BacktraceEntry> out;
440 auto& memory = system.Memory();
441
442 // fp (= r29) points to the last frame record.
443 // Note that this is the frame record for the *previous* frame, not the current one.
444 // Note we need to subtract 4 from our last read to get the proper address
445 // Frame records are two words long:
446 // fp+0 : pointer to previous frame record
447 // fp+8 : value of lr for frame
448 while (true) {
449 out.push_back({"", 0, lr, 0, ""});
450 if (!fp) {
451 break;
452 }
453 lr = memory.Read64(fp + 8) - 4;
454 fp = memory.Read64(fp);
455 }
456
457 SymbolicateBacktrace(system, out);
458
459 return out;
460}
461
462std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
463 System& system, const ThreadContext64& ctx) {
464 return GetBacktrace(system, ctx.cpu_registers[29], ctx.cpu_registers[30]);
465}
466
467std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
468 return GetBacktrace(system, GetReg(29), GetReg(30));
413} 469}
414 470
415} // namespace Core 471} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 0c4e46c64..78773e293 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <memory> 8#include <memory>
8#include <unordered_map> 9#include <unordered_map>
9 10
@@ -32,6 +33,7 @@ public:
32 33
33 void SetPC(u64 pc) override; 34 void SetPC(u64 pc) override;
34 u64 GetPC() const override; 35 u64 GetPC() const override;
36 u64 GetSP() const override;
35 u64 GetReg(int index) const override; 37 u64 GetReg(int index) const override;
36 void SetReg(int index, u64 value) override; 38 void SetReg(int index, u64 value) override;
37 u128 GetVectorReg(int index) const override; 39 u128 GetVectorReg(int index) const override;
@@ -51,6 +53,7 @@ public:
51 void LoadContext(const ThreadContext64& ctx) override; 53 void LoadContext(const ThreadContext64& ctx) override;
52 54
53 void PrepareReschedule() override; 55 void PrepareReschedule() override;
56 void SignalInterrupt() override;
54 void ClearExclusiveState() override; 57 void ClearExclusiveState() override;
55 58
56 void ClearInstructionCache() override; 59 void ClearInstructionCache() override;
@@ -58,10 +61,17 @@ public:
58 void PageTableChanged(Common::PageTable& new_page_table, 61 void PageTableChanged(Common::PageTable& new_page_table,
59 std::size_t new_address_space_size_in_bits) override; 62 std::size_t new_address_space_size_in_bits) override;
60 63
64 static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
65 const ThreadContext64& ctx);
66
67 std::vector<BacktraceEntry> GetBacktrace() const override;
68
61private: 69private:
62 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table, 70 std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
63 std::size_t address_space_bits) const; 71 std::size_t address_space_bits) const;
64 72
73 static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr);
74
65 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>; 75 using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
66 using JitCacheType = 76 using JitCacheType =
67 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>; 77 std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
@@ -73,13 +83,13 @@ private:
73 std::size_t core_index; 83 std::size_t core_index;
74 DynarmicExclusiveMonitor& exclusive_monitor; 84 DynarmicExclusiveMonitor& exclusive_monitor;
75 85
76 std::shared_ptr<Dynarmic::A64::Jit> jit; 86 std::shared_ptr<Dynarmic::A64::Jit> null_jit;
87
88 // A raw pointer here is fine; we never delete Jit instances.
89 std::atomic<Dynarmic::A64::Jit*> jit;
77 90
78 // SVC callback 91 // SVC callback
79 u32 svc_swi{}; 92 u32 svc_swi{};
80 bool svc_called{};
81
82 bool shutdown{};
83}; 93};
84 94
85} // namespace Core 95} // 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 b0cfee3ee..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"
@@ -326,7 +325,9 @@ struct System::Impl {
326 is_powered_on = false; 325 is_powered_on = false;
327 exit_lock = false; 326 exit_lock = false;
328 327
329 gpu_core->NotifyShutdown(); 328 if (gpu_core != nullptr) {
329 gpu_core->NotifyShutdown();
330 }
330 331
331 services.reset(); 332 services.reset();
332 service_manager.reset(); 333 service_manager.reset();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 888828fd0..28b63be43 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -8,13 +8,13 @@
8#include <chrono> 8#include <chrono>
9#include <functional> 9#include <functional>
10#include <memory> 10#include <memory>
11#include <mutex>
11#include <optional> 12#include <optional>
12#include <string> 13#include <string>
13#include <thread> 14#include <thread>
14#include <vector> 15#include <vector>
15 16
16#include "common/common_types.h" 17#include "common/common_types.h"
17#include "common/spin_lock.h"
18#include "common/thread.h" 18#include "common/thread.h"
19#include "common/wall_clock.h" 19#include "common/wall_clock.h"
20 20
@@ -149,8 +149,8 @@ private:
149 std::shared_ptr<EventType> ev_lost; 149 std::shared_ptr<EventType> ev_lost;
150 Common::Event event{}; 150 Common::Event event{};
151 Common::Event pause_event{}; 151 Common::Event pause_event{};
152 Common::SpinLock basic_lock{}; 152 std::mutex basic_lock;
153 Common::SpinLock advance_lock{}; 153 std::mutex advance_lock;
154 std::unique_ptr<std::thread> timer_thread; 154 std::unique_ptr<std::thread> timer_thread;
155 std::atomic<bool> paused{}; 155 std::atomic<bool> paused{};
156 std::atomic<bool> paused_set{}; 156 std::atomic<bool> paused_set{};
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/frontend/emu_window.h b/src/core/frontend/emu_window.h
index e413a520a..b3bffecb2 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -42,11 +42,20 @@ public:
42 context.MakeCurrent(); 42 context.MakeCurrent();
43 } 43 }
44 ~Scoped() { 44 ~Scoped() {
45 context.DoneCurrent(); 45 if (active) {
46 context.DoneCurrent();
47 }
48 }
49
50 /// In the event that context was destroyed before the Scoped is destroyed, this provides a
51 /// mechanism to prevent calling a destroyed object's method during the deconstructor
52 void Cancel() {
53 active = false;
46 } 54 }
47 55
48 private: 56 private:
49 GraphicsContext& context; 57 GraphicsContext& context;
58 bool active{true};
50 }; 59 };
51 60
52 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value 61 /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
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..2f84d2b52 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -15,6 +15,7 @@ EmulatedDevices::EmulatedDevices() = default;
15EmulatedDevices::~EmulatedDevices() = default; 15EmulatedDevices::~EmulatedDevices() = default;
16 16
17void EmulatedDevices::ReloadFromSettings() { 17void EmulatedDevices::ReloadFromSettings() {
18 ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
18 ReloadInput(); 19 ReloadInput();
19} 20}
20 21
@@ -66,6 +67,8 @@ void EmulatedDevices::ReloadInput() {
66 key_index++; 67 key_index++;
67 } 68 }
68 69
70 ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
71
69 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) { 72 for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
70 if (!mouse_button_devices[index]) { 73 if (!mouse_button_devices[index]) {
71 continue; 74 continue;
@@ -120,6 +123,13 @@ void EmulatedDevices::ReloadInput() {
120 }, 123 },
121 }); 124 });
122 } 125 }
126
127 if (ring_analog_device) {
128 ring_analog_device->SetCallback({
129 .on_change =
130 [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
131 });
132 }
123} 133}
124 134
125void EmulatedDevices::UnloadInput() { 135void EmulatedDevices::UnloadInput() {
@@ -155,6 +165,7 @@ void EmulatedDevices::SaveCurrentConfig() {
155 if (!is_configuring) { 165 if (!is_configuring) {
156 return; 166 return;
157 } 167 }
168 Settings::values.ringcon_analogs = ring_params.Serialize();
158} 169}
159 170
160void EmulatedDevices::RestoreConfig() { 171void EmulatedDevices::RestoreConfig() {
@@ -164,12 +175,21 @@ void EmulatedDevices::RestoreConfig() {
164 ReloadFromSettings(); 175 ReloadFromSettings();
165} 176}
166 177
178Common::ParamPackage EmulatedDevices::GetRingParam() const {
179 return ring_params;
180}
181
182void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
183 ring_params = std::move(param);
184 ReloadInput();
185}
186
167void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback, 187void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
168 std::size_t index) { 188 std::size_t index) {
169 if (index >= device_status.keyboard_values.size()) { 189 if (index >= device_status.keyboard_values.size()) {
170 return; 190 return;
171 } 191 }
172 std::lock_guard lock{mutex}; 192 std::unique_lock lock{mutex};
173 bool value_changed = false; 193 bool value_changed = false;
174 const auto new_status = TransformToButton(callback); 194 const auto new_status = TransformToButton(callback);
175 auto& current_status = device_status.keyboard_values[index]; 195 auto& current_status = device_status.keyboard_values[index];
@@ -201,6 +221,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
201 } 221 }
202 222
203 if (is_configuring) { 223 if (is_configuring) {
224 lock.unlock();
204 TriggerOnChange(DeviceTriggerType::Keyboard); 225 TriggerOnChange(DeviceTriggerType::Keyboard);
205 return; 226 return;
206 } 227 }
@@ -208,6 +229,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
208 // Index should be converted from NativeKeyboard to KeyboardKeyIndex 229 // Index should be converted from NativeKeyboard to KeyboardKeyIndex
209 UpdateKey(index, current_status.value); 230 UpdateKey(index, current_status.value);
210 231
232 lock.unlock();
211 TriggerOnChange(DeviceTriggerType::Keyboard); 233 TriggerOnChange(DeviceTriggerType::Keyboard);
212} 234}
213 235
@@ -227,7 +249,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
227 if (index >= device_status.keyboard_moddifier_values.size()) { 249 if (index >= device_status.keyboard_moddifier_values.size()) {
228 return; 250 return;
229 } 251 }
230 std::lock_guard lock{mutex}; 252 std::unique_lock lock{mutex};
231 bool value_changed = false; 253 bool value_changed = false;
232 const auto new_status = TransformToButton(callback); 254 const auto new_status = TransformToButton(callback);
233 auto& current_status = device_status.keyboard_moddifier_values[index]; 255 auto& current_status = device_status.keyboard_moddifier_values[index];
@@ -259,6 +281,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
259 } 281 }
260 282
261 if (is_configuring) { 283 if (is_configuring) {
284 lock.unlock();
262 TriggerOnChange(DeviceTriggerType::KeyboardModdifier); 285 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
263 return; 286 return;
264 } 287 }
@@ -289,6 +312,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
289 break; 312 break;
290 } 313 }
291 314
315 lock.unlock();
292 TriggerOnChange(DeviceTriggerType::KeyboardModdifier); 316 TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
293} 317}
294 318
@@ -297,7 +321,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
297 if (index >= device_status.mouse_button_values.size()) { 321 if (index >= device_status.mouse_button_values.size()) {
298 return; 322 return;
299 } 323 }
300 std::lock_guard lock{mutex}; 324 std::unique_lock lock{mutex};
301 bool value_changed = false; 325 bool value_changed = false;
302 const auto new_status = TransformToButton(callback); 326 const auto new_status = TransformToButton(callback);
303 auto& current_status = device_status.mouse_button_values[index]; 327 auto& current_status = device_status.mouse_button_values[index];
@@ -329,6 +353,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
329 } 353 }
330 354
331 if (is_configuring) { 355 if (is_configuring) {
356 lock.unlock();
332 TriggerOnChange(DeviceTriggerType::Mouse); 357 TriggerOnChange(DeviceTriggerType::Mouse);
333 return; 358 return;
334 } 359 }
@@ -351,6 +376,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
351 break; 376 break;
352 } 377 }
353 378
379 lock.unlock();
354 TriggerOnChange(DeviceTriggerType::Mouse); 380 TriggerOnChange(DeviceTriggerType::Mouse);
355} 381}
356 382
@@ -359,13 +385,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
359 if (index >= device_status.mouse_analog_values.size()) { 385 if (index >= device_status.mouse_analog_values.size()) {
360 return; 386 return;
361 } 387 }
362 std::lock_guard lock{mutex}; 388 std::unique_lock lock{mutex};
363 const auto analog_value = TransformToAnalog(callback); 389 const auto analog_value = TransformToAnalog(callback);
364 390
365 device_status.mouse_analog_values[index] = analog_value; 391 device_status.mouse_analog_values[index] = analog_value;
366 392
367 if (is_configuring) { 393 if (is_configuring) {
368 device_status.mouse_position_state = {}; 394 device_status.mouse_position_state = {};
395 lock.unlock();
369 TriggerOnChange(DeviceTriggerType::Mouse); 396 TriggerOnChange(DeviceTriggerType::Mouse);
370 return; 397 return;
371 } 398 }
@@ -379,17 +406,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
379 break; 406 break;
380 } 407 }
381 408
409 lock.unlock();
382 TriggerOnChange(DeviceTriggerType::Mouse); 410 TriggerOnChange(DeviceTriggerType::Mouse);
383} 411}
384 412
385void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) { 413void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
386 std::lock_guard lock{mutex}; 414 std::unique_lock lock{mutex};
387 const auto touch_value = TransformToTouch(callback); 415 const auto touch_value = TransformToTouch(callback);
388 416
389 device_status.mouse_stick_value = touch_value; 417 device_status.mouse_stick_value = touch_value;
390 418
391 if (is_configuring) { 419 if (is_configuring) {
392 device_status.mouse_position_state = {}; 420 device_status.mouse_position_state = {};
421 lock.unlock();
393 TriggerOnChange(DeviceTriggerType::Mouse); 422 TriggerOnChange(DeviceTriggerType::Mouse);
394 return; 423 return;
395 } 424 }
@@ -397,42 +426,77 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
397 device_status.mouse_position_state.x = touch_value.x.value; 426 device_status.mouse_position_state.x = touch_value.x.value;
398 device_status.mouse_position_state.y = touch_value.y.value; 427 device_status.mouse_position_state.y = touch_value.y.value;
399 428
429 lock.unlock();
400 TriggerOnChange(DeviceTriggerType::Mouse); 430 TriggerOnChange(DeviceTriggerType::Mouse);
401} 431}
402 432
433void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
434 std::lock_guard lock{mutex};
435 const auto force_value = TransformToStick(callback);
436
437 device_status.ring_analog_value = force_value.x;
438
439 if (is_configuring) {
440 device_status.ring_analog_value = {};
441 TriggerOnChange(DeviceTriggerType::RingController);
442 return;
443 }
444
445 device_status.ring_analog_state.force = force_value.x.value;
446
447 TriggerOnChange(DeviceTriggerType::RingController);
448}
449
403KeyboardValues EmulatedDevices::GetKeyboardValues() const { 450KeyboardValues EmulatedDevices::GetKeyboardValues() const {
451 std::scoped_lock lock{mutex};
404 return device_status.keyboard_values; 452 return device_status.keyboard_values;
405} 453}
406 454
407KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const { 455KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
456 std::scoped_lock lock{mutex};
408 return device_status.keyboard_moddifier_values; 457 return device_status.keyboard_moddifier_values;
409} 458}
410 459
411MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const { 460MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
461 std::scoped_lock lock{mutex};
412 return device_status.mouse_button_values; 462 return device_status.mouse_button_values;
413} 463}
414 464
465RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
466 return device_status.ring_analog_value;
467}
468
415KeyboardKey EmulatedDevices::GetKeyboard() const { 469KeyboardKey EmulatedDevices::GetKeyboard() const {
470 std::scoped_lock lock{mutex};
416 return device_status.keyboard_state; 471 return device_status.keyboard_state;
417} 472}
418 473
419KeyboardModifier EmulatedDevices::GetKeyboardModifier() const { 474KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
475 std::scoped_lock lock{mutex};
420 return device_status.keyboard_moddifier_state; 476 return device_status.keyboard_moddifier_state;
421} 477}
422 478
423MouseButton EmulatedDevices::GetMouseButtons() const { 479MouseButton EmulatedDevices::GetMouseButtons() const {
480 std::scoped_lock lock{mutex};
424 return device_status.mouse_button_state; 481 return device_status.mouse_button_state;
425} 482}
426 483
427MousePosition EmulatedDevices::GetMousePosition() const { 484MousePosition EmulatedDevices::GetMousePosition() const {
485 std::scoped_lock lock{mutex};
428 return device_status.mouse_position_state; 486 return device_status.mouse_position_state;
429} 487}
430 488
431AnalogStickState EmulatedDevices::GetMouseWheel() const { 489AnalogStickState EmulatedDevices::GetMouseWheel() const {
490 std::scoped_lock lock{mutex};
432 return device_status.mouse_wheel_state; 491 return device_status.mouse_wheel_state;
433} 492}
434 493
494RingSensorForce EmulatedDevices::GetRingSensorForce() const {
495 return device_status.ring_analog_state;
496}
497
435void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) { 498void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
499 std::scoped_lock lock{callback_mutex};
436 for (const auto& poller_pair : callback_list) { 500 for (const auto& poller_pair : callback_list) {
437 const InterfaceUpdateCallback& poller = poller_pair.second; 501 const InterfaceUpdateCallback& poller = poller_pair.second;
438 if (poller.on_change) { 502 if (poller.on_change) {
@@ -442,13 +506,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
442} 506}
443 507
444int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) { 508int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
445 std::lock_guard lock{mutex}; 509 std::scoped_lock lock{callback_mutex};
446 callback_list.insert_or_assign(last_callback_key, std::move(update_callback)); 510 callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
447 return last_callback_key++; 511 return last_callback_key++;
448} 512}
449 513
450void EmulatedDevices::DeleteCallback(int key) { 514void EmulatedDevices::DeleteCallback(int key) {
451 std::lock_guard lock{mutex}; 515 std::scoped_lock lock{callback_mutex};
452 const auto& iterator = callback_list.find(key); 516 const auto& iterator = callback_list.find(key);
453 if (iterator == callback_list.end()) { 517 if (iterator == callback_list.end()) {
454 LOG_ERROR(Input, "Tried to delete non-existent callback {}", key); 518 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..fb6451e7a 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -26,9 +26,11 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice
26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 26using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
27 Settings::NativeMouseWheel::NumMouseWheels>; 27 Settings::NativeMouseWheel::NumMouseWheels>;
28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>; 28using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
29using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
29 30
30using MouseButtonParams = 31using MouseButtonParams =
31 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>; 32 std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
33using RingAnalogParams = Common::ParamPackage;
32 34
33using KeyboardValues = 35using KeyboardValues =
34 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>; 36 std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -39,12 +41,17 @@ using MouseButtonValues =
39using MouseAnalogValues = 41using MouseAnalogValues =
40 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>; 42 std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
41using MouseStickValue = Common::Input::TouchStatus; 43using MouseStickValue = Common::Input::TouchStatus;
44using RingAnalogValue = Common::Input::AnalogStatus;
42 45
43struct MousePosition { 46struct MousePosition {
44 f32 x; 47 f32 x;
45 f32 y; 48 f32 y;
46}; 49};
47 50
51struct RingSensorForce {
52 f32 force;
53};
54
48struct DeviceStatus { 55struct DeviceStatus {
49 // Data from input_common 56 // Data from input_common
50 KeyboardValues keyboard_values{}; 57 KeyboardValues keyboard_values{};
@@ -52,6 +59,7 @@ struct DeviceStatus {
52 MouseButtonValues mouse_button_values{}; 59 MouseButtonValues mouse_button_values{};
53 MouseAnalogValues mouse_analog_values{}; 60 MouseAnalogValues mouse_analog_values{};
54 MouseStickValue mouse_stick_value{}; 61 MouseStickValue mouse_stick_value{};
62 RingAnalogValue ring_analog_value{};
55 63
56 // Data for HID serices 64 // Data for HID serices
57 KeyboardKey keyboard_state{}; 65 KeyboardKey keyboard_state{};
@@ -59,12 +67,14 @@ struct DeviceStatus {
59 MouseButton mouse_button_state{}; 67 MouseButton mouse_button_state{};
60 MousePosition mouse_position_state{}; 68 MousePosition mouse_position_state{};
61 AnalogStickState mouse_wheel_state{}; 69 AnalogStickState mouse_wheel_state{};
70 RingSensorForce ring_analog_state{};
62}; 71};
63 72
64enum class DeviceTriggerType { 73enum class DeviceTriggerType {
65 Keyboard, 74 Keyboard,
66 KeyboardModdifier, 75 KeyboardModdifier,
67 Mouse, 76 Mouse,
77 RingController,
68}; 78};
69 79
70struct InterfaceUpdateCallback { 80struct InterfaceUpdateCallback {
@@ -110,6 +120,15 @@ public:
110 /// Reverts any mapped changes made that weren't saved 120 /// Reverts any mapped changes made that weren't saved
111 void RestoreConfig(); 121 void RestoreConfig();
112 122
123 // Returns the current mapped ring device
124 Common::ParamPackage GetRingParam() const;
125
126 /**
127 * Updates the current mapped ring device
128 * @param param ParamPackage with ring sensor data to be mapped
129 */
130 void SetRingParam(Common::ParamPackage param);
131
113 /// Returns the latest status of button input from the keyboard with parameters 132 /// Returns the latest status of button input from the keyboard with parameters
114 KeyboardValues GetKeyboardValues() const; 133 KeyboardValues GetKeyboardValues() const;
115 134
@@ -119,6 +138,9 @@ public:
119 /// Returns the latest status of button input from the mouse with parameters 138 /// Returns the latest status of button input from the mouse with parameters
120 MouseButtonValues GetMouseButtonsValues() const; 139 MouseButtonValues GetMouseButtonsValues() const;
121 140
141 /// Returns the latest status of analog input from the ring sensor with parameters
142 RingAnalogValue GetRingSensorValues() const;
143
122 /// Returns the latest status of button input from the keyboard 144 /// Returns the latest status of button input from the keyboard
123 KeyboardKey GetKeyboard() const; 145 KeyboardKey GetKeyboard() const;
124 146
@@ -134,6 +156,9 @@ public:
134 /// Returns the latest mouse wheel change 156 /// Returns the latest mouse wheel change
135 AnalogStickState GetMouseWheel() const; 157 AnalogStickState GetMouseWheel() const;
136 158
159 /// Returns the latest ringcon force sensor value
160 RingSensorForce GetRingSensorForce() const;
161
137 /** 162 /**
138 * Adds a callback to the list of events 163 * Adds a callback to the list of events
139 * @param update_callback InterfaceUpdateCallback that will be triggered 164 * @param update_callback InterfaceUpdateCallback that will be triggered
@@ -186,6 +211,12 @@ private:
186 void SetMouseStick(const Common::Input::CallbackStatus& callback); 211 void SetMouseStick(const Common::Input::CallbackStatus& callback);
187 212
188 /** 213 /**
214 * Updates the ring analog sensor status of the ring controller
215 * @param callback A CallbackStatus containing the force status
216 */
217 void SetRingAnalog(const Common::Input::CallbackStatus& callback);
218
219 /**
189 * Triggers a callback that something has changed on the device status 220 * Triggers a callback that something has changed on the device status
190 * @param type Input type of the event to trigger 221 * @param type Input type of the event to trigger
191 */ 222 */
@@ -193,13 +224,17 @@ private:
193 224
194 bool is_configuring{false}; 225 bool is_configuring{false};
195 226
227 RingAnalogParams ring_params;
228
196 KeyboardDevices keyboard_devices; 229 KeyboardDevices keyboard_devices;
197 KeyboardModifierDevices keyboard_modifier_devices; 230 KeyboardModifierDevices keyboard_modifier_devices;
198 MouseButtonDevices mouse_button_devices; 231 MouseButtonDevices mouse_button_devices;
199 MouseAnalogDevices mouse_analog_devices; 232 MouseAnalogDevices mouse_analog_devices;
200 MouseStickDevice mouse_stick_device; 233 MouseStickDevice mouse_stick_device;
234 RingAnalogDevice ring_analog_device;
201 235
202 mutable std::mutex mutex; 236 mutable std::mutex mutex;
237 mutable std::mutex callback_mutex;
203 std::unordered_map<int, InterfaceUpdateCallback> callback_list; 238 std::unordered_map<int, InterfaceUpdateCallback> callback_list;
204 int last_callback_key = 0; 239 int last_callback_key = 0;
205 240
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 778b328b9..422a9af33 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -491,9 +491,10 @@ struct SixAxisSensorHandle {
491}; 491};
492static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size"); 492static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
493 493
494// These parameters seem related to how much gyro/accelerometer is used
494struct SixAxisSensorFusionParameters { 495struct SixAxisSensorFusionParameters {
495 f32 parameter1; 496 f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
496 f32 parameter2; 497 f32 parameter2{0.4f}; // Default 0.4
497}; 498};
498static_assert(sizeof(SixAxisSensorFusionParameters) == 8, 499static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
499 "SixAxisSensorFusionParameters is an invalid size"); 500 "SixAxisSensorFusionParameters is an invalid size");
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/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 6f44b534f..47425a3a1 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -8,7 +8,6 @@
8#include <vector> 8#include <vector>
9 9
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/spin_lock.h"
12#include "core/hardware_properties.h" 11#include "core/hardware_properties.h"
13#include "core/hle/kernel/k_priority_queue.h" 12#include "core/hle/kernel/k_priority_queue.h"
14#include "core/hle/kernel/k_scheduler_lock.h" 13#include "core/hle/kernel/k_scheduler_lock.h"
@@ -80,7 +79,7 @@ private:
80 79
81 /// Lists all thread ids that aren't deleted/etc. 80 /// Lists all thread ids that aren't deleted/etc.
82 std::vector<KThread*> thread_list; 81 std::vector<KThread*> thread_list;
83 Common::SpinLock global_list_guard{}; 82 std::mutex global_list_guard;
84}; 83};
85 84
86} // namespace Kernel 85} // namespace Kernel
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 dfea0b6e2..47ea3c89c 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -285,72 +285,260 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
285 return ResultSuccess; 285 return ResultSuccess;
286} 286}
287 287
288ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 288ResultCode KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
289 // Validate the mapping request.
290 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
291 ResultInvalidMemoryRegion);
292
293 // Lock the table.
289 KScopedLightLock lk(general_lock); 294 KScopedLightLock lk(general_lock);
290 295
291 const std::size_t num_pages{size / PageSize}; 296 // Verify that the source memory is normal heap.
297 KMemoryState src_state{};
298 KMemoryPermission src_perm{};
299 std::size_t num_src_allocator_blocks{};
300 R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
301 src_address, size, KMemoryState::All, KMemoryState::Normal,
302 KMemoryPermission::All, KMemoryPermission::UserReadWrite,
303 KMemoryAttribute::All, KMemoryAttribute::None));
292 304
293 KMemoryState state{}; 305 // Verify that the destination memory is unmapped.
294 KMemoryPermission perm{}; 306 std::size_t num_dst_allocator_blocks{};
295 CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size, 307 R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
296 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All, 308 KMemoryState::Free, KMemoryPermission::None,
297 KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask, 309 KMemoryPermission::None, KMemoryAttribute::None,
298 KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped)); 310 KMemoryAttribute::None));
299 311
300 if (IsRegionMapped(dst_addr, size)) { 312 // Map the code memory.
301 return ResultInvalidCurrentMemory; 313 {
314 // Determine the number of pages being operated on.
315 const std::size_t num_pages = size / PageSize;
316
317 // Create page groups for the memory being mapped.
318 KPageLinkedList pg;
319 AddRegionToPages(src_address, num_pages, pg);
320
321 // Reprotect the source as kernel-read/not mapped.
322 const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
323 KMemoryPermission::NotMapped);
324 R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
325
326 // Ensure that we unprotect the source pages on failure.
327 auto unprot_guard = SCOPE_GUARD({
328 ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
329 .IsSuccess());
330 });
331
332 // Map the alias pages.
333 R_TRY(MapPages(dst_address, pg, new_perm));
334
335 // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
336 // failure.
337 unprot_guard.Cancel();
338
339 // Apply the memory block updates.
340 block_manager->Update(src_address, num_pages, src_state, new_perm,
341 KMemoryAttribute::Locked);
342 block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm,
343 KMemoryAttribute::None);
302 } 344 }
303 345
304 KPageLinkedList page_linked_list; 346 return ResultSuccess;
305 AddRegionToPages(src_addr, num_pages, page_linked_list); 347}
348
349ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
350 ICacheInvalidationStrategy icache_invalidation_strategy) {
351 // Validate the mapping request.
352 R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
353 ResultInvalidMemoryRegion);
306 354
355 // Lock the table.
356 KScopedLightLock lk(general_lock);
357
358 // Verify that the source memory is locked normal heap.
359 std::size_t num_src_allocator_blocks{};
360 R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
361 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
362 KMemoryPermission::None, KMemoryAttribute::All,
363 KMemoryAttribute::Locked));
364
365 // Verify that the destination memory is aliasable code.
366 std::size_t num_dst_allocator_blocks{};
367 R_TRY(this->CheckMemoryStateContiguous(
368 std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
369 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
370 KMemoryAttribute::All, KMemoryAttribute::None));
371
372 // Determine whether any pages being unmapped are code.
373 bool any_code_pages = false;
307 { 374 {
308 auto block_guard = detail::ScopeExit( 375 KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address);
309 [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); }); 376 while (true) {
377 // Get the memory info.
378 const KMemoryInfo info = it->GetMemoryInfo();
310 379
311 CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None, 380 // Check if the memory has code flag.
312 OperationType::ChangePermissions)); 381 if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
313 CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None)); 382 any_code_pages = true;
383 break;
384 }
314 385
315 block_guard.Cancel(); 386 // Check if we're done.
387 if (dst_address + size - 1 <= info.GetLastAddress()) {
388 break;
389 }
390
391 // Advance.
392 ++it;
393 }
316 } 394 }
317 395
318 block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None, 396 // Ensure that we maintain the instruction cache.
319 KMemoryAttribute::Locked); 397 bool reprotected_pages = false;
320 block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode); 398 SCOPE_EXIT({
399 if (reprotected_pages && any_code_pages) {
400 if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
401 system.InvalidateCpuInstructionCacheRange(dst_address, size);
402 } else {
403 system.InvalidateCpuInstructionCaches();
404 }
405 }
406 });
407
408 // Unmap.
409 {
410 // Determine the number of pages being operated on.
411 const std::size_t num_pages = size / PageSize;
412
413 // Unmap the aliased copy of the pages.
414 R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
415
416 // Try to set the permissions for the source pages back to what they should be.
417 R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
418 OperationType::ChangePermissions));
419
420 // Apply the memory block updates.
421 block_manager->Update(dst_address, num_pages, KMemoryState::None);
422 block_manager->Update(src_address, num_pages, KMemoryState::Normal,
423 KMemoryPermission::UserReadWrite);
424
425 // Note that we reprotected pages.
426 reprotected_pages = true;
427 }
321 428
322 return ResultSuccess; 429 return ResultSuccess;
323} 430}
324 431
325ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { 432VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
326 KScopedLightLock lk(general_lock); 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 }
327 459
328 if (!size) { 460 const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
329 return ResultSuccess; 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 }
330 } 489 }
331 490
332 const std::size_t num_pages{size / PageSize}; 491 return address;
492}
333 493
334 CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size, 494ResultCode KPageTable::MakePageGroup(KPageLinkedList& pg, VAddr addr, size_t num_pages) {
335 KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None, 495 ASSERT(this->IsLockedByCurrentThread());
336 KMemoryPermission::None, KMemoryAttribute::Mask,
337 KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
338 496
339 KMemoryState state{}; 497 const size_t size = num_pages * PageSize;
340 CASCADE_CODE(CheckMemoryState(
341 &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
342 KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
343 KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
344 CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
345 KMemoryPermission::None, KMemoryAttribute::Mask,
346 KMemoryAttribute::None));
347 CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
348 498
349 block_manager->Update(dst_addr, num_pages, KMemoryState::Free); 499 // We're making a new group, not adding to an existing one.
350 block_manager->Update(src_addr, num_pages, KMemoryState::Normal, 500 R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
351 KMemoryPermission::UserReadWrite); 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 }
352 537
353 system.InvalidateCpuInstructionCacheRange(dst_addr, size); 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));
354 542
355 return ResultSuccess; 543 return ResultSuccess;
356} 544}
@@ -380,6 +568,8 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
380 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None, 568 block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
381 KMemoryAttribute::None); 569 KMemoryAttribute::None);
382 570
571 system.InvalidateCpuInstructionCaches();
572
383 return ResultSuccess; 573 return ResultSuccess;
384} 574}
385 575
@@ -986,6 +1176,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
986 return ResultSuccess; 1176 return ResultSuccess;
987} 1177}
988 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
989ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { 1219ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
990 ASSERT(this->IsLockedByCurrentThread()); 1220 ASSERT(this->IsLockedByCurrentThread());
991 1221
@@ -1028,6 +1258,55 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
1028 return ResultSuccess; 1258 return ResultSuccess;
1029} 1259}
1030 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
1031ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, 1310ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
1032 Svc::MemoryPermission svc_perm) { 1311 Svc::MemoryPermission svc_perm) {
1033 const size_t num_pages = size / PageSize; 1312 const size_t num_pages = size / PageSize;
@@ -1410,57 +1689,21 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
1410} 1689}
1411 1690
1412ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) { 1691ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
1413 KScopedLightLock lk(general_lock); 1692 return this->LockMemoryAndOpen(
1414 1693 nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1415 KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite; 1694 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite,
1416 1695 KMemoryAttribute::All, KMemoryAttribute::None,
1417 KMemoryPermission old_perm{}; 1696 static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
1418 1697 KMemoryPermission::KernelReadWrite),
1419 if (const ResultCode result{CheckMemoryState( 1698 KMemoryAttribute::Locked);
1420 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1421 KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
1422 KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
1423 result.IsError()) {
1424 return result;
1425 }
1426
1427 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1428
1429 block_manager->UpdateLock(
1430 addr, size / PageSize,
1431 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1432 block->ShareToDevice(permission);
1433 },
1434 new_perm);
1435
1436 return ResultSuccess;
1437} 1699}
1438 1700
1439ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) { 1701ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
1440 KScopedLightLock lk(general_lock); 1702 return this->UnlockMemory(addr, size, KMemoryState::FlagCanCodeMemory,
1441 1703 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None,
1442 KMemoryPermission new_perm = KMemoryPermission::UserReadWrite; 1704 KMemoryPermission::None, KMemoryAttribute::All,
1443 1705 KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
1444 KMemoryPermission old_perm{}; 1706 KMemoryAttribute::Locked, nullptr);
1445
1446 if (const ResultCode result{CheckMemoryState(
1447 nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
1448 KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
1449 KMemoryAttribute::All, KMemoryAttribute::Locked)};
1450 result.IsError()) {
1451 return result;
1452 }
1453
1454 new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
1455
1456 block_manager->UpdateLock(
1457 addr, size / PageSize,
1458 [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
1459 block->UnshareToDevice(permission);
1460 },
1461 new_perm);
1462
1463 return ResultSuccess;
1464} 1707}
1465 1708
1466ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) { 1709ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
@@ -1796,4 +2039,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
1796 return ResultSuccess; 2039 return ResultSuccess;
1797} 2040}
1798 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
1799} // 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 194177332..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
@@ -36,8 +39,9 @@ public:
36 KMemoryManager::Pool pool); 39 KMemoryManager::Pool pool);
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_addr, VAddr src_addr, std::size_t size); 42 ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
40 ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, 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 }
@@ -253,7 +301,9 @@ public:
253 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { 301 constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
254 return !IsOutsideASLRRegion(address, size); 302 return !IsOutsideASLRRegion(address, size);
255 } 303 }
256 304 constexpr std::size_t GetNumGuardPages() const {
305 return IsKernel() ? 1 : 4;
306 }
257 PAddr GetPhysicalAddr(VAddr addr) const { 307 PAddr GetPhysicalAddr(VAddr addr) const {
258 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits]; 308 const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
259 ASSERT(backing_addr); 309 ASSERT(backing_addr);
@@ -275,10 +325,6 @@ private:
275 return is_aslr_enabled; 325 return is_aslr_enabled;
276 } 326 }
277 327
278 constexpr std::size_t GetNumGuardPages() const {
279 return IsKernel() ? 1 : 4;
280 }
281
282 constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { 328 constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const {
283 return (address_space_start <= addr) && 329 return (address_space_start <= addr) &&
284 (num_pages <= (address_space_end - address_space_start) / PageSize) && 330 (num_pages <= (address_space_end - address_space_start) / PageSize) &&
@@ -311,6 +357,7 @@ private:
311 bool is_aslr_enabled{}; 357 bool is_aslr_enabled{};
312 358
313 u32 heap_fill_value{}; 359 u32 heap_fill_value{};
360 const KMemoryRegion* cached_physical_heap_region{};
314 361
315 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application}; 362 KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
316 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..526eb4b70 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
@@ -706,7 +705,7 @@ void KScheduler::Unload(KThread* thread) {
706 prev_thread = nullptr; 705 prev_thread = nullptr;
707 } 706 }
708 707
709 thread->context_guard.Unlock(); 708 thread->context_guard.unlock();
710} 709}
711 710
712void KScheduler::Reload(KThread* thread) { 711void KScheduler::Reload(KThread* thread) {
@@ -795,13 +794,13 @@ void KScheduler::SwitchToCurrent() {
795 do { 794 do {
796 auto next_thread = current_thread.load(); 795 auto next_thread = current_thread.load();
797 if (next_thread != nullptr) { 796 if (next_thread != nullptr) {
798 const auto locked = next_thread->context_guard.TryLock(); 797 const auto locked = next_thread->context_guard.try_lock();
799 if (state.needs_scheduling.load()) { 798 if (state.needs_scheduling.load()) {
800 next_thread->context_guard.Unlock(); 799 next_thread->context_guard.unlock();
801 break; 800 break;
802 } 801 }
803 if (next_thread->GetActiveCore() != core_id) { 802 if (next_thread->GetActiveCore() != core_id) {
804 next_thread->context_guard.Unlock(); 803 next_thread->context_guard.unlock();
805 break; 804 break;
806 } 805 }
807 if (!locked) { 806 if (!locked) {
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_spin_lock.cpp b/src/core/hle/kernel/k_spin_lock.cpp
index 4412aa4bb..527ff0f9f 100644
--- a/src/core/hle/kernel/k_spin_lock.cpp
+++ b/src/core/hle/kernel/k_spin_lock.cpp
@@ -4,51 +4,18 @@
4 4
5#include "core/hle/kernel/k_spin_lock.h" 5#include "core/hle/kernel/k_spin_lock.h"
6 6
7#if _MSC_VER
8#include <intrin.h>
9#if _M_AMD64
10#define __x86_64__ 1
11#endif
12#if _M_ARM64
13#define __aarch64__ 1
14#endif
15#else
16#if __x86_64__
17#include <xmmintrin.h>
18#endif
19#endif
20
21namespace {
22
23void ThreadPause() {
24#if __x86_64__
25 _mm_pause();
26#elif __aarch64__ && _MSC_VER
27 __yield();
28#elif __aarch64__
29 asm("yield");
30#endif
31}
32
33} // namespace
34
35namespace Kernel { 7namespace Kernel {
36 8
37void KSpinLock::Lock() { 9void KSpinLock::Lock() {
38 while (lck.test_and_set(std::memory_order_acquire)) { 10 lck.lock();
39 ThreadPause();
40 }
41} 11}
42 12
43void KSpinLock::Unlock() { 13void KSpinLock::Unlock() {
44 lck.clear(std::memory_order_release); 14 lck.unlock();
45} 15}
46 16
47bool KSpinLock::TryLock() { 17bool KSpinLock::TryLock() {
48 if (lck.test_and_set(std::memory_order_acquire)) { 18 return lck.try_lock();
49 return false;
50 }
51 return true;
52} 19}
53 20
54} // namespace Kernel 21} // namespace Kernel
diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h
index 4d87d006a..7868b25a5 100644
--- a/src/core/hle/kernel/k_spin_lock.h
+++ b/src/core/hle/kernel/k_spin_lock.h
@@ -4,7 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic> 7#include <mutex>
8 8
9#include "core/hle/kernel/k_scoped_lock.h" 9#include "core/hle/kernel/k_scoped_lock.h"
10 10
@@ -25,7 +25,7 @@ public:
25 [[nodiscard]] bool TryLock(); 25 [[nodiscard]] bool TryLock();
26 26
27private: 27private:
28 std::atomic_flag lck = ATOMIC_FLAG_INIT; 28 std::mutex lck;
29}; 29};
30 30
31// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future. 31// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index de3ffe0c7..af71987e8 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,10 +723,10 @@ 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.load(std::memory_order_relaxed);
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.store(new_state, std::memory_order_relaxed);
730 730
731 // Note the state change in scheduler. 731 // Note the state change in scheduler.
732 if (new_state != old_state) { 732 if (new_state != old_state) {
@@ -738,8 +738,8 @@ 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.load(std::memory_order_relaxed);
742 thread_state = old_state & ThreadState::Mask; 742 thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed);
743 743
744 // Note the state change in scheduler. 744 // Note the state change in scheduler.
745 KScheduler::OnThreadStateChanged(kernel, this, old_state); 745 KScheduler::OnThreadStateChanged(kernel, this, old_state);
@@ -1079,17 +1079,10 @@ void KThread::IfDummyThreadTryWait() {
1079 return; 1079 return;
1080 } 1080 }
1081 1081
1082 // Block until we can grab the lock. 1082 // Block until we are no longer waiting.
1083 KScopedSpinLock lk{dummy_wait_lock}; 1083 std::unique_lock lk(dummy_wait_lock);
1084} 1084 dummy_wait_cv.wait(
1085 1085 lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
1086void KThread::IfDummyThreadBeginWait() {
1087 if (!IsDummyThread()) {
1088 return;
1089 }
1090
1091 // Ensure the thread will block when IfDummyThreadTryWait is called.
1092 dummy_wait_lock.Lock();
1093} 1086}
1094 1087
1095void KThread::IfDummyThreadEndWait() { 1088void KThread::IfDummyThreadEndWait() {
@@ -1097,8 +1090,8 @@ void KThread::IfDummyThreadEndWait() {
1097 return; 1090 return;
1098 } 1091 }
1099 1092
1100 // Ensure the thread will no longer block. 1093 // Wake up the waiting thread.
1101 dummy_wait_lock.Unlock(); 1094 dummy_wait_cv.notify_one();
1102} 1095}
1103 1096
1104void KThread::BeginWait(KThreadQueue* queue) { 1097void KThread::BeginWait(KThreadQueue* queue) {
@@ -1107,9 +1100,6 @@ void KThread::BeginWait(KThreadQueue* queue) {
1107 1100
1108 // Set our wait queue. 1101 // Set our wait queue.
1109 wait_queue = queue; 1102 wait_queue = queue;
1110
1111 // Special case for dummy threads to ensure they block.
1112 IfDummyThreadBeginWait();
1113} 1103}
1114 1104
1115void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) { 1105void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
@@ -1158,10 +1148,11 @@ void KThread::SetState(ThreadState state) {
1158 SetMutexWaitAddressForDebugging({}); 1148 SetMutexWaitAddressForDebugging({});
1159 SetWaitReasonForDebugging({}); 1149 SetWaitReasonForDebugging({});
1160 1150
1161 const ThreadState old_state = thread_state; 1151 const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
1162 thread_state = 1152 thread_state.store(
1163 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); 1153 static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)),
1164 if (thread_state != old_state) { 1154 std::memory_order_relaxed);
1155 if (thread_state.load(std::memory_order_relaxed) != old_state) {
1165 KScheduler::OnThreadStateChanged(kernel, this, old_state); 1156 KScheduler::OnThreadStateChanged(kernel, this, old_state);
1166 } 1157 }
1167} 1158}
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index d058db62c..4892fdf76 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -5,6 +5,9 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <atomic>
9#include <condition_variable>
10#include <mutex>
8#include <span> 11#include <span>
9#include <string> 12#include <string>
10#include <utility> 13#include <utility>
@@ -14,6 +17,7 @@
14 17
15#include "common/common_types.h" 18#include "common/common_types.h"
16#include "common/intrusive_red_black_tree.h" 19#include "common/intrusive_red_black_tree.h"
20#include "common/spin_lock.h"
17#include "core/arm/arm_interface.h" 21#include "core/arm/arm_interface.h"
18#include "core/hle/kernel/k_affinity_mask.h" 22#include "core/hle/kernel/k_affinity_mask.h"
19#include "core/hle/kernel/k_light_lock.h" 23#include "core/hle/kernel/k_light_lock.h"
@@ -255,11 +259,11 @@ public:
255 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); 259 [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
256 260
257 [[nodiscard]] ThreadState GetState() const { 261 [[nodiscard]] ThreadState GetState() const {
258 return thread_state & ThreadState::Mask; 262 return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask;
259 } 263 }
260 264
261 [[nodiscard]] ThreadState GetRawState() const { 265 [[nodiscard]] ThreadState GetRawState() const {
262 return thread_state; 266 return thread_state.load(std::memory_order_relaxed);
263 } 267 }
264 268
265 void SetState(ThreadState state); 269 void SetState(ThreadState state);
@@ -641,7 +645,6 @@ public:
641 // blocking as needed. 645 // blocking as needed.
642 646
643 void IfDummyThreadTryWait(); 647 void IfDummyThreadTryWait();
644 void IfDummyThreadBeginWait();
645 void IfDummyThreadEndWait(); 648 void IfDummyThreadEndWait();
646 649
647private: 650private:
@@ -656,7 +659,7 @@ private:
656 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); 659 static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
657 660
658 struct ConditionVariableComparator { 661 struct ConditionVariableComparator {
659 struct LightCompareType { 662 struct RedBlackKeyType {
660 u64 cv_key{}; 663 u64 cv_key{};
661 s32 priority{}; 664 s32 priority{};
662 665
@@ -672,8 +675,8 @@ private:
672 template <typename T> 675 template <typename T>
673 requires( 676 requires(
674 std::same_as<T, KThread> || 677 std::same_as<T, KThread> ||
675 std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, 678 std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
676 const KThread& rhs) { 679 const KThread& rhs) {
677 const u64 l_key = lhs.GetConditionVariableKey(); 680 const u64 l_key = lhs.GetConditionVariableKey();
678 const u64 r_key = rhs.GetConditionVariableKey(); 681 const u64 r_key = rhs.GetConditionVariableKey();
679 682
@@ -751,7 +754,7 @@ private:
751 KAffinityMask original_physical_affinity_mask{}; 754 KAffinityMask original_physical_affinity_mask{};
752 s32 original_physical_ideal_core_id{}; 755 s32 original_physical_ideal_core_id{};
753 s32 num_core_migration_disables{}; 756 s32 num_core_migration_disables{};
754 ThreadState thread_state{}; 757 std::atomic<ThreadState> thread_state{};
755 std::atomic<bool> termination_requested{}; 758 std::atomic<bool> termination_requested{};
756 bool wait_cancelled{}; 759 bool wait_cancelled{};
757 bool cancellable{}; 760 bool cancellable{};
@@ -761,13 +764,14 @@ private:
761 s8 priority_inheritance_count{}; 764 s8 priority_inheritance_count{};
762 bool resource_limit_release_hint{}; 765 bool resource_limit_release_hint{};
763 StackParameters stack_parameters{}; 766 StackParameters stack_parameters{};
764 KSpinLock context_guard{}; 767 Common::SpinLock context_guard{};
765 KSpinLock dummy_wait_lock{};
766 768
767 // For emulation 769 // For emulation
768 std::shared_ptr<Common::Fiber> host_context{}; 770 std::shared_ptr<Common::Fiber> host_context{};
769 bool is_single_core{}; 771 bool is_single_core{};
770 ThreadType thread_type{}; 772 ThreadType thread_type{};
773 std::mutex dummy_wait_lock;
774 std::condition_variable dummy_wait_cv;
771 775
772 // For debugging 776 // For debugging
773 std::vector<KSynchronizationObject*> wait_objects_for_debugging; 777 std::vector<KSynchronizationObject*> wait_objects_for_debugging;
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..5984afd7e 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
@@ -154,6 +140,7 @@ struct KernelCore::Impl {
154 CleanupObject(font_shared_mem); 140 CleanupObject(font_shared_mem);
155 CleanupObject(irs_shared_mem); 141 CleanupObject(irs_shared_mem);
156 CleanupObject(time_shared_mem); 142 CleanupObject(time_shared_mem);
143 CleanupObject(hidbus_shared_mem);
157 CleanupObject(system_resource_limit); 144 CleanupObject(system_resource_limit);
158 145
159 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 146 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
@@ -171,7 +158,7 @@ struct KernelCore::Impl {
171 158
172 // Close kernel objects that were not freed on shutdown 159 // Close kernel objects that were not freed on shutdown
173 { 160 {
174 std::lock_guard lk(registered_in_use_objects_lock); 161 std::scoped_lock lk{registered_in_use_objects_lock};
175 if (registered_in_use_objects.size()) { 162 if (registered_in_use_objects.size()) {
176 for (auto& object : registered_in_use_objects) { 163 for (auto& object : registered_in_use_objects) {
177 object->Close(); 164 object->Close();
@@ -182,23 +169,27 @@ struct KernelCore::Impl {
182 169
183 // Shutdown all processes. 170 // Shutdown all processes.
184 if (current_process) { 171 if (current_process) {
185 current_process->Finalize(); 172 (*current_process).Finalize();
186 // current_process->Close(); 173 // current_process->Close();
187 // TODO: The current process should be destroyed based on accurate ref counting after 174 // 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. 175 // calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
189 current_process->Destroy(); 176 (*current_process).Destroy();
190 current_process = nullptr; 177 current_process = nullptr;
191 } 178 }
192 179
193 // Track kernel objects that were not freed on shutdown 180 // Track kernel objects that were not freed on shutdown
194 { 181 {
195 std::lock_guard lk(registered_objects_lock); 182 std::scoped_lock lk{registered_objects_lock};
196 if (registered_objects.size()) { 183 if (registered_objects.size()) {
197 LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!", 184 LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
198 registered_objects.size()); 185 registered_objects.size());
199 registered_objects.clear(); 186 registered_objects.clear();
200 } 187 }
201 } 188 }
189
190 // Ensure that the object list container is finalized and properly shutdown.
191 global_object_list_container->Finalize();
192 global_object_list_container.reset();
202 } 193 }
203 194
204 void InitializePhysicalCores() { 195 void InitializePhysicalCores() {
@@ -291,15 +282,16 @@ struct KernelCore::Impl {
291 282
292 // Gets the dummy KThread for the caller, allocating a new one if this is the first time 283 // Gets the dummy KThread for the caller, allocating a new one if this is the first time
293 KThread* GetHostDummyThread() { 284 KThread* GetHostDummyThread() {
294 auto make_thread = [this]() { 285 auto initialize = [this](KThread* thread) {
295 KThread* thread = KThread::Create(system.Kernel());
296 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); 286 ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
297 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); 287 thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
298 return thread; 288 return thread;
299 }; 289 };
300 290
301 thread_local KThread* saved_thread = make_thread(); 291 thread_local auto raw_thread = KThread(system.Kernel());
302 return saved_thread; 292 thread_local auto thread = initialize(&raw_thread);
293
294 return thread;
303 } 295 }
304 296
305 /// Registers a CPU core thread by allocating a host thread ID for it 297 /// Registers a CPU core thread by allocating a host thread ID for it
@@ -631,16 +623,20 @@ struct KernelCore::Impl {
631 constexpr std::size_t font_size{0x1100000}; 623 constexpr std::size_t font_size{0x1100000};
632 constexpr std::size_t irs_size{0x8000}; 624 constexpr std::size_t irs_size{0x8000};
633 constexpr std::size_t time_size{0x1000}; 625 constexpr std::size_t time_size{0x1000};
626 constexpr std::size_t hidbus_size{0x1000};
634 627
635 const PAddr hid_phys_addr{system_pool.GetAddress()}; 628 const PAddr hid_phys_addr{system_pool.GetAddress()};
636 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; 629 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
637 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; 630 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
638 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; 631 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
632 const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size +
633 time_size};
639 634
640 hid_shared_mem = KSharedMemory::Create(system.Kernel()); 635 hid_shared_mem = KSharedMemory::Create(system.Kernel());
641 font_shared_mem = KSharedMemory::Create(system.Kernel()); 636 font_shared_mem = KSharedMemory::Create(system.Kernel());
642 irs_shared_mem = KSharedMemory::Create(system.Kernel()); 637 irs_shared_mem = KSharedMemory::Create(system.Kernel());
643 time_shared_mem = KSharedMemory::Create(system.Kernel()); 638 time_shared_mem = KSharedMemory::Create(system.Kernel());
639 hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
644 640
645 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, 641 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr,
646 {hid_phys_addr, hid_size / PageSize}, 642 {hid_phys_addr, hid_size / PageSize},
@@ -658,22 +654,10 @@ struct KernelCore::Impl {
658 {time_phys_addr, time_size / PageSize}, 654 {time_phys_addr, time_size / PageSize},
659 Svc::MemoryPermission::None, Svc::MemoryPermission::Read, 655 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
660 time_phys_addr, time_size, "Time:SharedMemory"); 656 time_phys_addr, time_size, "Time:SharedMemory");
661 } 657 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr,
662 658 {hidbus_phys_addr, hidbus_size / PageSize},
663 void InitializePageSlab() { 659 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
664 // Allocate slab heaps 660 hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");
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 } 661 }
678 662
679 KClientPort* CreateNamedServicePort(std::string name) { 663 KClientPort* CreateNamedServicePort(std::string name) {
@@ -684,13 +668,20 @@ struct KernelCore::Impl {
684 } 668 }
685 669
686 KClientPort* port = &search->second(system.ServiceManager(), system); 670 KClientPort* port = &search->second(system.ServiceManager(), system);
687 { 671 RegisterServerObject(&port->GetParent()->GetServerPort());
688 std::lock_guard lk(server_ports_lock);
689 server_ports.insert(&port->GetParent()->GetServerPort());
690 }
691 return port; 672 return port;
692 } 673 }
693 674
675 void RegisterServerObject(KAutoObject* server_object) {
676 std::scoped_lock lk(server_objects_lock);
677 server_objects.insert(server_object);
678 }
679
680 void UnregisterServerObject(KAutoObject* server_object) {
681 std::scoped_lock lk(server_objects_lock);
682 server_objects.erase(server_object);
683 }
684
694 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel, 685 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
695 const std::string& name) { 686 const std::string& name) {
696 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name); 687 auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
@@ -703,6 +694,12 @@ struct KernelCore::Impl {
703 694
704 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 695 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
705 if (auto strong_ptr = service_thread.lock()) { 696 if (auto strong_ptr = service_thread.lock()) {
697 if (strong_ptr == default_service_thread.lock()) {
698 // Nothing to do here, the service is using default_service_thread, which will be
699 // released on shutdown.
700 return;
701 }
702
706 service_threads_manager.QueueWork( 703 service_threads_manager.QueueWork(
707 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); }); 704 [this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
708 } 705 }
@@ -712,8 +709,7 @@ struct KernelCore::Impl {
712 service_threads_manager.QueueWork([this]() { service_threads.clear(); }); 709 service_threads_manager.QueueWork([this]() { service_threads.clear(); });
713 } 710 }
714 711
715 std::mutex server_ports_lock; 712 std::mutex server_objects_lock;
716 std::mutex server_sessions_lock;
717 std::mutex registered_objects_lock; 713 std::mutex registered_objects_lock;
718 std::mutex registered_in_use_objects_lock; 714 std::mutex registered_in_use_objects_lock;
719 715
@@ -724,7 +720,7 @@ struct KernelCore::Impl {
724 720
725 // Lists all processes that exist in the current session. 721 // Lists all processes that exist in the current session.
726 std::vector<KProcess*> process_list; 722 std::vector<KProcess*> process_list;
727 KProcess* current_process{}; 723 std::atomic<KProcess*> current_process{};
728 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; 724 std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
729 Kernel::TimeManager time_manager; 725 Kernel::TimeManager time_manager;
730 726
@@ -737,14 +733,13 @@ struct KernelCore::Impl {
737 // stores all the objects in place. 733 // stores all the objects in place.
738 std::unique_ptr<KHandleTable> global_handle_table; 734 std::unique_ptr<KHandleTable> global_handle_table;
739 735
740 KAutoObjectWithListContainer object_list_container; 736 std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
741 737
742 /// Map of named ports managed by the kernel, which can be retrieved using 738 /// Map of named ports managed by the kernel, which can be retrieved using
743 /// the ConnectToPort SVC. 739 /// the ConnectToPort SVC.
744 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory; 740 std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
745 NamedPortTable named_ports; 741 NamedPortTable named_ports;
746 std::unordered_set<KServerPort*> server_ports; 742 std::unordered_set<KAutoObject*> server_objects;
747 std::unordered_set<KServerSession*> server_sessions;
748 std::unordered_set<KAutoObject*> registered_objects; 743 std::unordered_set<KAutoObject*> registered_objects;
749 std::unordered_set<KAutoObject*> registered_in_use_objects; 744 std::unordered_set<KAutoObject*> registered_in_use_objects;
750 745
@@ -756,19 +751,20 @@ struct KernelCore::Impl {
756 751
757 // Kernel memory management 752 // Kernel memory management
758 std::unique_ptr<KMemoryManager> memory_manager; 753 std::unique_ptr<KMemoryManager> memory_manager;
759 std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
760 754
761 // Shared memory for services 755 // Shared memory for services
762 Kernel::KSharedMemory* hid_shared_mem{}; 756 Kernel::KSharedMemory* hid_shared_mem{};
763 Kernel::KSharedMemory* font_shared_mem{}; 757 Kernel::KSharedMemory* font_shared_mem{};
764 Kernel::KSharedMemory* irs_shared_mem{}; 758 Kernel::KSharedMemory* irs_shared_mem{};
765 Kernel::KSharedMemory* time_shared_mem{}; 759 Kernel::KSharedMemory* time_shared_mem{};
760 Kernel::KSharedMemory* hidbus_shared_mem{};
766 761
767 // Memory layout 762 // Memory layout
768 std::unique_ptr<KMemoryLayout> memory_layout; 763 std::unique_ptr<KMemoryLayout> memory_layout;
769 764
770 // Threads used for services 765 // Threads used for services
771 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads; 766 std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
767 std::weak_ptr<ServiceThread> default_service_thread;
772 Common::ThreadWorker service_threads_manager; 768 Common::ThreadWorker service_threads_manager;
773 769
774 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads; 770 std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
@@ -915,11 +911,11 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
915} 911}
916 912
917KAutoObjectWithListContainer& KernelCore::ObjectListContainer() { 913KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
918 return impl->object_list_container; 914 return *impl->global_object_list_container;
919} 915}
920 916
921const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const { 917const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
922 return impl->object_list_container; 918 return *impl->global_object_list_container;
923} 919}
924 920
925void KernelCore::InvalidateAllInstructionCaches() { 921void KernelCore::InvalidateAllInstructionCaches() {
@@ -949,33 +945,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
949 return impl->CreateNamedServicePort(std::move(name)); 945 return impl->CreateNamedServicePort(std::move(name));
950} 946}
951 947
952void KernelCore::RegisterServerSession(KServerSession* server_session) { 948void KernelCore::RegisterServerObject(KAutoObject* server_object) {
953 std::lock_guard lk(impl->server_sessions_lock); 949 impl->RegisterServerObject(server_object);
954 impl->server_sessions.insert(server_session);
955} 950}
956 951
957void KernelCore::UnregisterServerSession(KServerSession* server_session) { 952void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
958 std::lock_guard lk(impl->server_sessions_lock); 953 impl->UnregisterServerObject(server_object);
959 impl->server_sessions.erase(server_session);
960} 954}
961 955
962void KernelCore::RegisterKernelObject(KAutoObject* object) { 956void KernelCore::RegisterKernelObject(KAutoObject* object) {
963 std::lock_guard lk(impl->registered_objects_lock); 957 std::scoped_lock lk{impl->registered_objects_lock};
964 impl->registered_objects.insert(object); 958 impl->registered_objects.insert(object);
965} 959}
966 960
967void KernelCore::UnregisterKernelObject(KAutoObject* object) { 961void KernelCore::UnregisterKernelObject(KAutoObject* object) {
968 std::lock_guard lk(impl->registered_objects_lock); 962 std::scoped_lock lk{impl->registered_objects_lock};
969 impl->registered_objects.erase(object); 963 impl->registered_objects.erase(object);
970} 964}
971 965
972void KernelCore::RegisterInUseObject(KAutoObject* object) { 966void KernelCore::RegisterInUseObject(KAutoObject* object) {
973 std::lock_guard lk(impl->registered_in_use_objects_lock); 967 std::scoped_lock lk{impl->registered_in_use_objects_lock};
974 impl->registered_in_use_objects.insert(object); 968 impl->registered_in_use_objects.insert(object);
975} 969}
976 970
977void KernelCore::UnregisterInUseObject(KAutoObject* object) { 971void KernelCore::UnregisterInUseObject(KAutoObject* object) {
978 std::lock_guard lk(impl->registered_in_use_objects_lock); 972 std::scoped_lock lk{impl->registered_in_use_objects_lock};
979 impl->registered_in_use_objects.erase(object); 973 impl->registered_in_use_objects.erase(object);
980} 974}
981 975
@@ -1031,14 +1025,6 @@ const KMemoryManager& KernelCore::MemoryManager() const {
1031 return *impl->memory_manager; 1025 return *impl->memory_manager;
1032} 1026}
1033 1027
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() { 1028Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
1043 return *impl->hid_shared_mem; 1029 return *impl->hid_shared_mem;
1044} 1030}
@@ -1071,6 +1057,14 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
1071 return *impl->time_shared_mem; 1057 return *impl->time_shared_mem;
1072} 1058}
1073 1059
1060Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() {
1061 return *impl->hidbus_shared_mem;
1062}
1063
1064const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
1065 return *impl->hidbus_shared_mem;
1066}
1067
1074void KernelCore::Suspend(bool in_suspention) { 1068void KernelCore::Suspend(bool in_suspention) {
1075 const bool should_suspend = exception_exited || in_suspention; 1069 const bool should_suspend = exception_exited || in_suspention;
1076 { 1070 {
@@ -1112,6 +1106,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
1112 return impl->CreateServiceThread(*this, name); 1106 return impl->CreateServiceThread(*this, name);
1113} 1107}
1114 1108
1109std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
1110 return impl->default_service_thread;
1111}
1112
1115void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) { 1113void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
1116 impl->ReleaseServiceThread(service_thread); 1114 impl->ReleaseServiceThread(service_thread);
1117} 1115}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index c1254b18d..12e44b8a5 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
@@ -269,6 +264,12 @@ public:
269 /// Gets the shared memory object for Time services. 264 /// Gets the shared memory object for Time services.
270 const Kernel::KSharedMemory& GetTimeSharedMem() const; 265 const Kernel::KSharedMemory& GetTimeSharedMem() const;
271 266
267 /// Gets the shared memory object for HIDBus services.
268 Kernel::KSharedMemory& GetHidBusSharedMem();
269
270 /// Gets the shared memory object for HIDBus services.
271 const Kernel::KSharedMemory& GetHidBusSharedMem() const;
272
272 /// Suspend/unsuspend the OS. 273 /// Suspend/unsuspend the OS.
273 void Suspend(bool in_suspention); 274 void Suspend(bool in_suspention);
274 275
@@ -284,9 +285,11 @@ public:
284 void ExitSVCProfile(); 285 void ExitSVCProfile();
285 286
286 /** 287 /**
287 * Creates an HLE service thread, which are used to execute service routines asynchronously. 288 * 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 289 * routines asynchronously. While these are allocated per ServerSession, these need to be owned
289 * of ServerSession to avoid a circular dependency. 290 * and managed outside of ServerSession to avoid a circular dependency. In general, most
291 * services can just use the default service thread, and not need their own host service thread.
292 * See GetDefaultServiceThread.
290 * @param name String name for the ServerSession creating this thread, used for debug 293 * @param name String name for the ServerSession creating this thread, used for debug
291 * purposes. 294 * purposes.
292 * @returns The a weak pointer newly created service thread. 295 * @returns The a weak pointer newly created service thread.
@@ -294,6 +297,14 @@ public:
294 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name); 297 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
295 298
296 /** 299 /**
300 * Gets the default host service thread, which executes HLE service requests. Unless service
301 * requests need to block on the host, the default service thread should be used in favor of
302 * creating a new service thread.
303 * @returns The a weak pointer for the default service thread.
304 */
305 std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
306
307 /**
297 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when 308 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
298 * the ServerSession associated with the thread is destroyed. 309 * the ServerSession associated with the thread is destroyed.
299 * @param service_thread Service thread to release. 310 * @param service_thread Service thread to release.
@@ -336,6 +347,10 @@ public:
336 return slab_heap_container->writeable_event; 347 return slab_heap_container->writeable_event;
337 } else if constexpr (std::is_same_v<T, KCodeMemory>) { 348 } else if constexpr (std::is_same_v<T, KCodeMemory>) {
338 return slab_heap_container->code_memory; 349 return slab_heap_container->code_memory;
350 } else if constexpr (std::is_same_v<T, KPageBuffer>) {
351 return slab_heap_container->page_buffer;
352 } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
353 return slab_heap_container->thread_local_page;
339 } 354 }
340 } 355 }
341 356
@@ -397,6 +412,8 @@ private:
397 KSlabHeap<KTransferMemory> transfer_memory; 412 KSlabHeap<KTransferMemory> transfer_memory;
398 KSlabHeap<KWritableEvent> writeable_event; 413 KSlabHeap<KWritableEvent> writeable_event;
399 KSlabHeap<KCodeMemory> code_memory; 414 KSlabHeap<KCodeMemory> code_memory;
415 KSlabHeap<KPageBuffer> page_buffer;
416 KSlabHeap<KThreadLocalPage> thread_local_page;
400 }; 417 };
401 418
402 std::unique_ptr<SlabHeapContainer> slab_heap_container; 419 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..cc49e8c7e 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.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/spin_lock.h"
6#include "core/arm/cpu_interrupt_handler.h" 5#include "core/arm/cpu_interrupt_handler.h"
7#include "core/arm/dynarmic/arm_dynarmic_32.h" 6#include "core/arm/dynarmic/arm_dynarmic_32.h"
8#include "core/arm/dynarmic/arm_dynarmic_64.h" 7#include "core/arm/dynarmic/arm_dynarmic_64.h"
@@ -16,7 +15,7 @@ namespace Kernel {
16PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_, 15PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
17 Core::CPUInterrupts& interrupts_) 16 Core::CPUInterrupts& interrupts_)
18 : core_index{core_index_}, system{system_}, scheduler{scheduler_}, 17 : core_index{core_index_}, system{system_}, scheduler{scheduler_},
19 interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} { 18 interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
20#ifdef ARCHITECTURE_x86_64 19#ifdef ARCHITECTURE_x86_64
21 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with 20 // TODO(bunnei): Initialization relies on a core being available. We may later replace this with
22 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. 21 // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
@@ -58,6 +57,7 @@ bool PhysicalCore::IsInterrupted() const {
58void PhysicalCore::Interrupt() { 57void PhysicalCore::Interrupt() {
59 guard->lock(); 58 guard->lock();
60 interrupts[core_index].SetInterrupt(true); 59 interrupts[core_index].SetInterrupt(true);
60 arm_interface->SignalInterrupt();
61 guard->unlock(); 61 guard->unlock();
62} 62}
63 63
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 16a032e89..f2112fc1d 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -6,13 +6,10 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <memory> 8#include <memory>
9#include <mutex>
9 10
10#include "core/arm/arm_interface.h" 11#include "core/arm/arm_interface.h"
11 12
12namespace Common {
13class SpinLock;
14}
15
16namespace Kernel { 13namespace Kernel {
17class KScheduler; 14class KScheduler;
18} // namespace Kernel 15} // namespace Kernel
@@ -91,7 +88,7 @@ private:
91 Core::System& system; 88 Core::System& system;
92 Kernel::KScheduler& scheduler; 89 Kernel::KScheduler& scheduler;
93 Core::CPUInterrupts& interrupts; 90 Core::CPUInterrupts& interrupts;
94 std::unique_ptr<Common::SpinLock> guard; 91 std::unique_ptr<std::mutex> guard;
95 std::unique_ptr<Core::ARM_Interface> arm_interface; 92 std::unique_ptr<Core::ARM_Interface> arm_interface;
96}; 93};
97 94
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..68407e39a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -17,6 +17,7 @@
17#include "core/hle/kernel/k_readable_event.h" 17#include "core/hle/kernel/k_readable_event.h"
18#include "core/hle/kernel/k_writable_event.h" 18#include "core/hle/kernel/k_writable_event.h"
19#include "core/hle/service/hid/controllers/npad.h" 19#include "core/hle/service/hid/controllers/npad.h"
20#include "core/hle/service/hid/errors.h"
20#include "core/hle/service/kernel_helpers.h" 21#include "core/hle/service/kernel_helpers.h"
21 22
22namespace Service::HID { 23namespace Service::HID {
@@ -48,15 +49,17 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
48} 49}
49 50
50bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) { 51bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
51 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && 52 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
52 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && 53 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
53 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 54 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
55 return npad_id && npad_type && device_index;
54} 56}
55 57
56bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) { 58bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
57 return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) && 59 const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
58 device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType && 60 const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
59 device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex; 61 const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
62 return npad_id && npad_type && device_index;
60} 63}
61 64
62Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, 65Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
@@ -262,11 +265,6 @@ void Controller_NPad::OnInit() {
262 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i)); 265 service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
263 } 266 }
264 267
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()); 268 supported_npad_id_types.resize(npad_id_list.size());
271 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), 269 std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
272 npad_id_list.size() * sizeof(Core::HID::NpadIdType)); 270 npad_id_list.size() * sizeof(Core::HID::NpadIdType));
@@ -288,14 +286,6 @@ void Controller_NPad::OnInit() {
288 WriteEmptyEntry(npad); 286 WriteEmptyEntry(npad);
289 } 287 }
290 } 288 }
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} 289}
300 290
301void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) { 291void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
@@ -320,6 +310,7 @@ void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
320} 310}
321 311
322void Controller_NPad::OnRelease() { 312void Controller_NPad::OnRelease() {
313 is_controller_initialized = false;
323 for (std::size_t i = 0; i < controller_data.size(); ++i) { 314 for (std::size_t i = 0; i < controller_data.size(); ++i) {
324 auto& controller = controller_data[i]; 315 auto& controller = controller_data[i];
325 service_context.CloseEvent(controller.styleset_changed_event); 316 service_context.CloseEvent(controller.styleset_changed_event);
@@ -330,7 +321,7 @@ void Controller_NPad::OnRelease() {
330} 321}
331 322
332void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { 323void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
333 std::lock_guard lock{mutex}; 324 std::scoped_lock lock{mutex};
334 auto& controller = GetControllerFromNpadIdType(npad_id); 325 auto& controller = GetControllerFromNpadIdType(npad_id);
335 const auto controller_type = controller.device->GetNpadStyleIndex(); 326 const auto controller_type = controller.device->GetNpadStyleIndex();
336 if (!controller.device->IsConnected()) { 327 if (!controller.device->IsConnected()) {
@@ -651,9 +642,27 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
651 642
652void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) { 643void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
653 hid_core.SetSupportedStyleTag(style_set); 644 hid_core.SetSupportedStyleTag(style_set);
645
646 if (is_controller_initialized) {
647 return;
648 }
649
650 // Once SetSupportedStyleSet is called controllers are fully initialized
651 is_controller_initialized = true;
652
653 // Connect all active controllers
654 for (auto& controller : controller_data) {
655 const auto& device = controller.device;
656 if (device->IsConnected()) {
657 AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
658 }
659 }
654} 660}
655 661
656Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const { 662Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
663 if (!is_controller_initialized) {
664 return {Core::HID::NpadStyleSet::None};
665 }
657 return hid_core.GetSupportedStyleTag(); 666 return hid_core.GetSupportedStyleTag();
658} 667}
659 668
@@ -1001,87 +1010,271 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
1001 WriteEmptyEntry(controller.shared_memory_entry); 1010 WriteEmptyEntry(controller.shared_memory_entry);
1002} 1011}
1003 1012
1004void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 1013ResultCode Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
1005 GyroscopeZeroDriftMode drift_mode) { 1014 GyroscopeZeroDriftMode drift_mode) {
1006 if (!IsDeviceHandleValid(sixaxis_handle)) { 1015 if (!IsDeviceHandleValid(sixaxis_handle)) {
1007 LOG_ERROR(Service_HID, "Invalid handle"); 1016 LOG_ERROR(Service_HID, "Invalid handle");
1008 return; 1017 return NpadInvalidHandle;
1018 }
1019
1020 auto& controller = GetControllerFromHandle(sixaxis_handle);
1021 switch (sixaxis_handle.npad_type) {
1022 case Core::HID::NpadStyleIndex::ProController:
1023 controller.sixaxis_fullkey.gyroscope_zero_drift_mode = drift_mode;
1024 break;
1025 case Core::HID::NpadStyleIndex::Handheld:
1026 controller.sixaxis_handheld.gyroscope_zero_drift_mode = drift_mode;
1027 break;
1028 case Core::HID::NpadStyleIndex::JoyconDual:
1029 case Core::HID::NpadStyleIndex::GameCube:
1030 case Core::HID::NpadStyleIndex::Pokeball:
1031 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1032 controller.sixaxis_dual_left.gyroscope_zero_drift_mode = drift_mode;
1033 break;
1034 }
1035 controller.sixaxis_dual_right.gyroscope_zero_drift_mode = drift_mode;
1036 break;
1037 case Core::HID::NpadStyleIndex::JoyconLeft:
1038 controller.sixaxis_left.gyroscope_zero_drift_mode = drift_mode;
1039 break;
1040 case Core::HID::NpadStyleIndex::JoyconRight:
1041 controller.sixaxis_right.gyroscope_zero_drift_mode = drift_mode;
1042 break;
1043 default:
1044 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1045 return NpadInvalidHandle;
1046 }
1047
1048 return ResultSuccess;
1049}
1050
1051ResultCode Controller_NPad::GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
1052 GyroscopeZeroDriftMode& drift_mode) const {
1053 if (!IsDeviceHandleValid(sixaxis_handle)) {
1054 LOG_ERROR(Service_HID, "Invalid handle");
1055 return NpadInvalidHandle;
1009 } 1056 }
1057
1010 auto& controller = GetControllerFromHandle(sixaxis_handle); 1058 auto& controller = GetControllerFromHandle(sixaxis_handle);
1011 controller.gyroscope_zero_drift_mode = drift_mode; 1059 switch (sixaxis_handle.npad_type) {
1060 case Core::HID::NpadStyleIndex::ProController:
1061 drift_mode = controller.sixaxis_fullkey.gyroscope_zero_drift_mode;
1062 break;
1063 case Core::HID::NpadStyleIndex::Handheld:
1064 drift_mode = controller.sixaxis_handheld.gyroscope_zero_drift_mode;
1065 break;
1066 case Core::HID::NpadStyleIndex::JoyconDual:
1067 case Core::HID::NpadStyleIndex::GameCube:
1068 case Core::HID::NpadStyleIndex::Pokeball:
1069 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1070 drift_mode = controller.sixaxis_dual_left.gyroscope_zero_drift_mode;
1071 break;
1072 }
1073 drift_mode = controller.sixaxis_dual_right.gyroscope_zero_drift_mode;
1074 break;
1075 case Core::HID::NpadStyleIndex::JoyconLeft:
1076 drift_mode = controller.sixaxis_left.gyroscope_zero_drift_mode;
1077 break;
1078 case Core::HID::NpadStyleIndex::JoyconRight:
1079 drift_mode = controller.sixaxis_right.gyroscope_zero_drift_mode;
1080 break;
1081 default:
1082 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1083 return NpadInvalidHandle;
1084 }
1085
1086 return ResultSuccess;
1012} 1087}
1013 1088
1014Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode( 1089ResultCode Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
1015 Core::HID::SixAxisSensorHandle sixaxis_handle) const { 1090 bool& is_at_rest) const {
1016 if (!IsDeviceHandleValid(sixaxis_handle)) { 1091 if (!IsDeviceHandleValid(sixaxis_handle)) {
1017 LOG_ERROR(Service_HID, "Invalid handle"); 1092 LOG_ERROR(Service_HID, "Invalid handle");
1018 // Return the default value 1093 return NpadInvalidHandle;
1019 return GyroscopeZeroDriftMode::Standard;
1020 } 1094 }
1021 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1095 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1022 return controller.gyroscope_zero_drift_mode; 1096 is_at_rest = controller.sixaxis_at_rest;
1097 return ResultSuccess;
1023} 1098}
1024 1099
1025bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const { 1100ResultCode Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
1101 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const {
1026 if (!IsDeviceHandleValid(sixaxis_handle)) { 1102 if (!IsDeviceHandleValid(sixaxis_handle)) {
1027 LOG_ERROR(Service_HID, "Invalid handle"); 1103 LOG_ERROR(Service_HID, "Invalid handle");
1028 // Return the default value 1104 return NpadInvalidHandle;
1029 return true;
1030 } 1105 }
1031 const auto& controller = GetControllerFromHandle(sixaxis_handle); 1106
1032 return controller.sixaxis_at_rest; 1107 // We don't support joycon firmware updates
1108 is_firmware_available = false;
1109 return ResultSuccess;
1033} 1110}
1034 1111
1035void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1112ResultCode Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1036 bool sixaxis_status) { 1113 bool sixaxis_status) {
1037 if (!IsDeviceHandleValid(sixaxis_handle)) { 1114 if (!IsDeviceHandleValid(sixaxis_handle)) {
1038 LOG_ERROR(Service_HID, "Invalid handle"); 1115 LOG_ERROR(Service_HID, "Invalid handle");
1039 return; 1116 return NpadInvalidHandle;
1040 } 1117 }
1041 auto& controller = GetControllerFromHandle(sixaxis_handle); 1118 auto& controller = GetControllerFromHandle(sixaxis_handle);
1042 controller.sixaxis_sensor_enabled = sixaxis_status; 1119 controller.sixaxis_sensor_enabled = sixaxis_status;
1120 return ResultSuccess;
1043} 1121}
1044 1122
1045void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 1123ResultCode Controller_NPad::IsSixAxisSensorFusionEnabled(
1046 bool sixaxis_fusion_status) { 1124 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_fusion_enabled) const {
1047 if (!IsDeviceHandleValid(sixaxis_handle)) { 1125 if (!IsDeviceHandleValid(sixaxis_handle)) {
1048 LOG_ERROR(Service_HID, "Invalid handle"); 1126 LOG_ERROR(Service_HID, "Invalid handle");
1049 return; 1127 return NpadInvalidHandle;
1050 } 1128 }
1129
1051 auto& controller = GetControllerFromHandle(sixaxis_handle); 1130 auto& controller = GetControllerFromHandle(sixaxis_handle);
1052 controller.sixaxis_fusion_enabled = sixaxis_fusion_status; 1131 switch (sixaxis_handle.npad_type) {
1053} 1132 case Core::HID::NpadStyleIndex::ProController:
1133 is_fusion_enabled = controller.sixaxis_fullkey.is_fusion_enabled;
1134 break;
1135 case Core::HID::NpadStyleIndex::Handheld:
1136 is_fusion_enabled = controller.sixaxis_handheld.is_fusion_enabled;
1137 break;
1138 case Core::HID::NpadStyleIndex::JoyconDual:
1139 case Core::HID::NpadStyleIndex::GameCube:
1140 case Core::HID::NpadStyleIndex::Pokeball:
1141 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1142 is_fusion_enabled = controller.sixaxis_dual_left.is_fusion_enabled;
1143 break;
1144 }
1145 is_fusion_enabled = controller.sixaxis_dual_right.is_fusion_enabled;
1146 break;
1147 case Core::HID::NpadStyleIndex::JoyconLeft:
1148 is_fusion_enabled = controller.sixaxis_left.is_fusion_enabled;
1149 break;
1150 case Core::HID::NpadStyleIndex::JoyconRight:
1151 is_fusion_enabled = controller.sixaxis_right.is_fusion_enabled;
1152 break;
1153 default:
1154 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1155 return NpadInvalidHandle;
1156 }
1054 1157
1055void Controller_NPad::SetSixAxisFusionParameters( 1158 return ResultSuccess;
1056 Core::HID::SixAxisSensorHandle sixaxis_handle, 1159}
1057 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) { 1160ResultCode Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
1161 bool is_fusion_enabled) {
1058 if (!IsDeviceHandleValid(sixaxis_handle)) { 1162 if (!IsDeviceHandleValid(sixaxis_handle)) {
1059 LOG_ERROR(Service_HID, "Invalid handle"); 1163 LOG_ERROR(Service_HID, "Invalid handle");
1060 return; 1164 return NpadInvalidHandle;
1061 } 1165 }
1166
1062 auto& controller = GetControllerFromHandle(sixaxis_handle); 1167 auto& controller = GetControllerFromHandle(sixaxis_handle);
1063 controller.sixaxis_fusion = sixaxis_fusion_parameters; 1168 switch (sixaxis_handle.npad_type) {
1169 case Core::HID::NpadStyleIndex::ProController:
1170 controller.sixaxis_fullkey.is_fusion_enabled = is_fusion_enabled;
1171 break;
1172 case Core::HID::NpadStyleIndex::Handheld:
1173 controller.sixaxis_handheld.is_fusion_enabled = is_fusion_enabled;
1174 break;
1175 case Core::HID::NpadStyleIndex::JoyconDual:
1176 case Core::HID::NpadStyleIndex::GameCube:
1177 case Core::HID::NpadStyleIndex::Pokeball:
1178 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1179 controller.sixaxis_dual_left.is_fusion_enabled = is_fusion_enabled;
1180 break;
1181 }
1182 controller.sixaxis_dual_right.is_fusion_enabled = is_fusion_enabled;
1183 break;
1184 case Core::HID::NpadStyleIndex::JoyconLeft:
1185 controller.sixaxis_left.is_fusion_enabled = is_fusion_enabled;
1186 break;
1187 case Core::HID::NpadStyleIndex::JoyconRight:
1188 controller.sixaxis_right.is_fusion_enabled = is_fusion_enabled;
1189 break;
1190 default:
1191 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1192 return NpadInvalidHandle;
1193 }
1194
1195 return ResultSuccess;
1064} 1196}
1065 1197
1066Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters( 1198ResultCode Controller_NPad::SetSixAxisFusionParameters(
1067 Core::HID::SixAxisSensorHandle sixaxis_handle) { 1199 Core::HID::SixAxisSensorHandle sixaxis_handle,
1200 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
1068 if (!IsDeviceHandleValid(sixaxis_handle)) { 1201 if (!IsDeviceHandleValid(sixaxis_handle)) {
1069 LOG_ERROR(Service_HID, "Invalid handle"); 1202 LOG_ERROR(Service_HID, "Invalid handle");
1070 // Since these parameters are unknow just return zeros 1203 return NpadInvalidHandle;
1071 return {}; 1204 }
1205 const auto param1 = sixaxis_fusion_parameters.parameter1;
1206 if (param1 < 0.0f || param1 > 1.0f) {
1207 return InvalidSixAxisFusionRange;
1072 } 1208 }
1209
1073 auto& controller = GetControllerFromHandle(sixaxis_handle); 1210 auto& controller = GetControllerFromHandle(sixaxis_handle);
1074 return controller.sixaxis_fusion; 1211 switch (sixaxis_handle.npad_type) {
1212 case Core::HID::NpadStyleIndex::ProController:
1213 controller.sixaxis_fullkey.fusion = sixaxis_fusion_parameters;
1214 break;
1215 case Core::HID::NpadStyleIndex::Handheld:
1216 controller.sixaxis_handheld.fusion = sixaxis_fusion_parameters;
1217 break;
1218 case Core::HID::NpadStyleIndex::JoyconDual:
1219 case Core::HID::NpadStyleIndex::GameCube:
1220 case Core::HID::NpadStyleIndex::Pokeball:
1221 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1222 controller.sixaxis_dual_left.fusion = sixaxis_fusion_parameters;
1223 break;
1224 }
1225 controller.sixaxis_dual_right.fusion = sixaxis_fusion_parameters;
1226 break;
1227 case Core::HID::NpadStyleIndex::JoyconLeft:
1228 controller.sixaxis_left.fusion = sixaxis_fusion_parameters;
1229 break;
1230 case Core::HID::NpadStyleIndex::JoyconRight:
1231 controller.sixaxis_right.fusion = sixaxis_fusion_parameters;
1232 break;
1233 default:
1234 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1235 return NpadInvalidHandle;
1236 }
1237
1238 return ResultSuccess;
1075} 1239}
1076 1240
1077void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) { 1241ResultCode Controller_NPad::GetSixAxisFusionParameters(
1242 Core::HID::SixAxisSensorHandle sixaxis_handle,
1243 Core::HID::SixAxisSensorFusionParameters& parameters) const {
1078 if (!IsDeviceHandleValid(sixaxis_handle)) { 1244 if (!IsDeviceHandleValid(sixaxis_handle)) {
1079 LOG_ERROR(Service_HID, "Invalid handle"); 1245 LOG_ERROR(Service_HID, "Invalid handle");
1080 return; 1246 return NpadInvalidHandle;
1081 } 1247 }
1082 auto& controller = GetControllerFromHandle(sixaxis_handle); 1248
1083 // Since these parameters are unknow just fill with zeros 1249 const auto& controller = GetControllerFromHandle(sixaxis_handle);
1084 controller.sixaxis_fusion = {}; 1250 switch (sixaxis_handle.npad_type) {
1251 case Core::HID::NpadStyleIndex::ProController:
1252 parameters = controller.sixaxis_fullkey.fusion;
1253 break;
1254 case Core::HID::NpadStyleIndex::Handheld:
1255 parameters = controller.sixaxis_handheld.fusion;
1256 break;
1257 case Core::HID::NpadStyleIndex::JoyconDual:
1258 case Core::HID::NpadStyleIndex::GameCube:
1259 case Core::HID::NpadStyleIndex::Pokeball:
1260 if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
1261 parameters = controller.sixaxis_dual_left.fusion;
1262 break;
1263 }
1264 parameters = controller.sixaxis_dual_right.fusion;
1265 break;
1266 case Core::HID::NpadStyleIndex::JoyconLeft:
1267 parameters = controller.sixaxis_left.fusion;
1268 break;
1269 case Core::HID::NpadStyleIndex::JoyconRight:
1270 parameters = controller.sixaxis_right.fusion;
1271 break;
1272 default:
1273 LOG_ERROR(Service_HID, "Invalid Npad type {}", sixaxis_handle.npad_type);
1274 return NpadInvalidHandle;
1275 }
1276
1277 return ResultSuccess;
1085} 1278}
1086 1279
1087void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, 1280void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 6b2872bad..f7313711f 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"
@@ -27,7 +28,9 @@ class KReadableEvent;
27 28
28namespace Service::KernelHelpers { 29namespace Service::KernelHelpers {
29class ServiceContext; 30class ServiceContext;
30} 31} // namespace Service::KernelHelpers
32
33union ResultCode;
31 34
32namespace Service::HID { 35namespace Service::HID {
33 36
@@ -142,20 +145,26 @@ public:
142 145
143 void DisconnectNpad(Core::HID::NpadIdType npad_id); 146 void DisconnectNpad(Core::HID::NpadIdType npad_id);
144 147
145 void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle, 148 ResultCode SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
146 GyroscopeZeroDriftMode drift_mode); 149 GyroscopeZeroDriftMode drift_mode);
147 GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode( 150 ResultCode GetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
148 Core::HID::SixAxisSensorHandle sixaxis_handle) const; 151 GyroscopeZeroDriftMode& drift_mode) const;
149 bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const; 152 ResultCode IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle,
150 void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status); 153 bool& is_at_rest) const;
151 void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, 154 ResultCode IsFirmwareUpdateAvailableForSixAxisSensor(
152 bool sixaxis_fusion_status); 155 Core::HID::SixAxisSensorHandle sixaxis_handle, bool& is_firmware_available) const;
153 void SetSixAxisFusionParameters( 156 ResultCode SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
157 bool sixaxis_status);
158 ResultCode IsSixAxisSensorFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
159 bool& is_fusion_enabled) const;
160 ResultCode SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
161 bool is_fusion_enabled);
162 ResultCode SetSixAxisFusionParameters(
154 Core::HID::SixAxisSensorHandle sixaxis_handle, 163 Core::HID::SixAxisSensorHandle sixaxis_handle,
155 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters); 164 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
156 Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters( 165 ResultCode GetSixAxisFusionParameters(
157 Core::HID::SixAxisSensorHandle sixaxis_handle); 166 Core::HID::SixAxisSensorHandle sixaxis_handle,
158 void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle); 167 Core::HID::SixAxisSensorFusionParameters& parameters) const;
159 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id); 168 Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
160 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const; 169 bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
161 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, 170 void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
@@ -190,16 +199,16 @@ private:
190 199
191 // This is nn::hid::detail::NpadFullKeyColorState 200 // This is nn::hid::detail::NpadFullKeyColorState
192 struct NpadFullKeyColorState { 201 struct NpadFullKeyColorState {
193 ColorAttribute attribute; 202 ColorAttribute attribute{ColorAttribute::NoController};
194 Core::HID::NpadControllerColor fullkey; 203 Core::HID::NpadControllerColor fullkey{};
195 }; 204 };
196 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size"); 205 static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
197 206
198 // This is nn::hid::detail::NpadJoyColorState 207 // This is nn::hid::detail::NpadJoyColorState
199 struct NpadJoyColorState { 208 struct NpadJoyColorState {
200 ColorAttribute attribute; 209 ColorAttribute attribute{ColorAttribute::NoController};
201 Core::HID::NpadControllerColor left; 210 Core::HID::NpadControllerColor left{};
202 Core::HID::NpadControllerColor right; 211 Core::HID::NpadControllerColor right{};
203 }; 212 };
204 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size"); 213 static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
205 214
@@ -225,11 +234,11 @@ private:
225 // This is nn::hid::NpadPalmaState 234 // This is nn::hid::NpadPalmaState
226 // This is nn::hid::NpadSystemExtState 235 // This is nn::hid::NpadSystemExtState
227 struct NPadGenericState { 236 struct NPadGenericState {
228 s64_le sampling_number; 237 s64_le sampling_number{};
229 Core::HID::NpadButtonState npad_buttons; 238 Core::HID::NpadButtonState npad_buttons{};
230 Core::HID::AnalogStickState l_stick; 239 Core::HID::AnalogStickState l_stick{};
231 Core::HID::AnalogStickState r_stick; 240 Core::HID::AnalogStickState r_stick{};
232 NpadAttribute connection_status; 241 NpadAttribute connection_status{};
233 INSERT_PADDING_BYTES(4); // Reserved 242 INSERT_PADDING_BYTES(4); // Reserved
234 }; 243 };
235 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size"); 244 static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -252,7 +261,7 @@ private:
252 Common::Vec3f gyro{}; 261 Common::Vec3f gyro{};
253 Common::Vec3f rotation{}; 262 Common::Vec3f rotation{};
254 std::array<Common::Vec3f, 3> orientation{}; 263 std::array<Common::Vec3f, 3> orientation{};
255 SixAxisSensorAttribute attribute; 264 SixAxisSensorAttribute attribute{};
256 INSERT_PADDING_BYTES(4); // Reserved 265 INSERT_PADDING_BYTES(4); // Reserved
257 }; 266 };
258 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size"); 267 static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -324,11 +333,11 @@ private:
324 333
325 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl 334 // This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
326 struct NfcXcdDeviceHandleStateImpl { 335 struct NfcXcdDeviceHandleStateImpl {
327 u64 handle; 336 u64 handle{};
328 bool is_available; 337 bool is_available{};
329 bool is_activated; 338 bool is_activated{};
330 INSERT_PADDING_BYTES(0x6); // Reserved 339 INSERT_PADDING_BYTES(0x6); // Reserved
331 u64 sampling_number; 340 u64 sampling_number{};
332 }; 341 };
333 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, 342 static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
334 "NfcXcdDeviceHandleStateImpl is an invalid size"); 343 "NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -365,8 +374,8 @@ private:
365 }; 374 };
366 375
367 struct AppletFooterUi { 376 struct AppletFooterUi {
368 AppletFooterUiAttributes attributes; 377 AppletFooterUiAttributes attributes{};
369 AppletFooterUiType type; 378 AppletFooterUiType type{AppletFooterUiType::None};
370 INSERT_PADDING_BYTES(0x5B); // Reserved 379 INSERT_PADDING_BYTES(0x5B); // Reserved
371 }; 380 };
372 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size"); 381 static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -403,41 +412,41 @@ private:
403 412
404 // This is nn::hid::detail::NpadInternalState 413 // This is nn::hid::detail::NpadInternalState
405 struct NpadInternalState { 414 struct NpadInternalState {
406 Core::HID::NpadStyleTag style_tag; 415 Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
407 NpadJoyAssignmentMode assignment_mode; 416 NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
408 NpadFullKeyColorState fullkey_color; 417 NpadFullKeyColorState fullkey_color{};
409 NpadJoyColorState joycon_color; 418 NpadJoyColorState joycon_color{};
410 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo; 419 Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
411 Lifo<NPadGenericState, hid_entry_count> handheld_lifo; 420 Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
412 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo; 421 Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
413 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo; 422 Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
414 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo; 423 Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
415 Lifo<NPadGenericState, hid_entry_count> palma_lifo; 424 Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
416 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo; 425 Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
417 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo; 426 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
418 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo; 427 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
419 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo; 428 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
420 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo; 429 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
421 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo; 430 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
422 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo; 431 Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
423 DeviceType device_type; 432 DeviceType device_type{};
424 INSERT_PADDING_BYTES(0x4); // Reserved 433 INSERT_PADDING_BYTES(0x4); // Reserved
425 NPadSystemProperties system_properties; 434 NPadSystemProperties system_properties{};
426 NpadSystemButtonProperties button_properties; 435 NpadSystemButtonProperties button_properties{};
427 Core::HID::NpadBatteryLevel battery_level_dual; 436 Core::HID::NpadBatteryLevel battery_level_dual{};
428 Core::HID::NpadBatteryLevel battery_level_left; 437 Core::HID::NpadBatteryLevel battery_level_left{};
429 Core::HID::NpadBatteryLevel battery_level_right; 438 Core::HID::NpadBatteryLevel battery_level_right{};
430 union { 439 union {
431 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{}; 440 AppletFooterUi applet_footer{};
432 AppletFooterUi applet_footer; 441 Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
433 }; 442 };
434 INSERT_PADDING_BYTES(0x20); // Unknown 443 INSERT_PADDING_BYTES(0x20); // Unknown
435 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo; 444 Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
436 NpadLarkType lark_type_l_and_main; 445 NpadLarkType lark_type_l_and_main{};
437 NpadLarkType lark_type_r; 446 NpadLarkType lark_type_r{};
438 NpadLuciaType lucia_type; 447 NpadLuciaType lucia_type{};
439 NpadLagonType lagon_type; 448 NpadLagonType lagon_type{};
440 NpadLagerType lager_type; 449 NpadLagerType lager_type{};
441 // FW 13.x Investigate there is some sort of bitflag related to joycons 450 // FW 13.x Investigate there is some sort of bitflag related to joycons
442 INSERT_PADDING_BYTES(0x4); 451 INSERT_PADDING_BYTES(0x4);
443 INSERT_PADDING_BYTES(0xc08); // Unknown 452 INSERT_PADDING_BYTES(0xc08); // Unknown
@@ -450,6 +459,12 @@ private:
450 std::chrono::steady_clock::time_point last_vibration_timepoint{}; 459 std::chrono::steady_clock::time_point last_vibration_timepoint{};
451 }; 460 };
452 461
462 struct SixaxisParameters {
463 bool is_fusion_enabled{true};
464 Core::HID::SixAxisSensorFusionParameters fusion{};
465 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
466 };
467
453 struct NpadControllerData { 468 struct NpadControllerData {
454 Core::HID::EmulatedController* device; 469 Core::HID::EmulatedController* device;
455 Kernel::KEvent* styleset_changed_event{}; 470 Kernel::KEvent* styleset_changed_event{};
@@ -466,9 +481,12 @@ private:
466 // Motion parameters 481 // Motion parameters
467 bool sixaxis_at_rest{true}; 482 bool sixaxis_at_rest{true};
468 bool sixaxis_sensor_enabled{true}; 483 bool sixaxis_sensor_enabled{true};
469 bool sixaxis_fusion_enabled{false}; 484 SixaxisParameters sixaxis_fullkey{};
470 Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{}; 485 SixaxisParameters sixaxis_handheld{};
471 GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; 486 SixaxisParameters sixaxis_dual_left{};
487 SixaxisParameters sixaxis_dual_right{};
488 SixaxisParameters sixaxis_left{};
489 SixaxisParameters sixaxis_right{};
472 490
473 // Current pad state 491 // Current pad state
474 NPadGenericState npad_pad_state{}; 492 NPadGenericState npad_pad_state{};
@@ -480,6 +498,7 @@ private:
480 SixAxisSensorState sixaxis_dual_right_state{}; 498 SixAxisSensorState sixaxis_dual_right_state{};
481 SixAxisSensorState sixaxis_left_lifo_state{}; 499 SixAxisSensorState sixaxis_left_lifo_state{};
482 SixAxisSensorState sixaxis_right_lifo_state{}; 500 SixAxisSensorState sixaxis_right_lifo_state{};
501
483 int callback_key; 502 int callback_key;
484 }; 503 };
485 504
@@ -510,7 +529,8 @@ private:
510 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; 529 NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
511 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default}; 530 NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
512 bool permit_vibration_session_enabled{false}; 531 bool permit_vibration_session_enabled{false};
513 bool analog_stick_use_center_clamp{}; 532 bool analog_stick_use_center_clamp{false};
514 bool is_in_lr_assignment_mode{false}; 533 bool is_in_lr_assignment_mode{false};
534 bool is_controller_initialized{false};
515}; 535};
516} // namespace Service::HID 536} // 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/errors.h b/src/core/hle/service/hid/errors.h
index 3583642e7..7c5fb3e52 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -8,6 +8,8 @@
8 8
9namespace Service::HID { 9namespace Service::HID {
10 10
11constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710}; 11constexpr ResultCode NpadInvalidHandle{ErrorModule::HID, 100};
12constexpr ResultCode InvalidSixAxisFusionRange{ErrorModule::HID, 423};
13constexpr ResultCode NpadNotConnected{ErrorModule::HID, 710};
12 14
13} // namespace Service::HID 15} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d9202ea6c..baf21df62 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/service/hid/errors.h" 17#include "core/hle/service/hid/errors.h"
18#include "core/hle/service/hid/hid.h" 18#include "core/hle/service/hid/hid.h"
19#include "core/hle/service/hid/hidbus.h"
19#include "core/hle/service/hid/irs.h" 20#include "core/hle/service/hid/irs.h"
20#include "core/hle/service/hid/xcd.h" 21#include "core/hle/service/hid/xcd.h"
21#include "core/memory.h" 22#include "core/memory.h"
@@ -247,7 +248,7 @@ Hid::Hid(Core::System& system_)
247 {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, 248 {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
248 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, 249 {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
249 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, 250 {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
250 {68, nullptr, "IsSixAxisSensorFusionEnabled"}, 251 {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
251 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, 252 {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
252 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, 253 {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
253 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, 254 {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
@@ -527,8 +528,8 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
527 528
528 const auto parameters{rp.PopRaw<Parameters>()}; 529 const auto parameters{rp.PopRaw<Parameters>()};
529 530
530 applet_resource->GetController<Controller_NPad>(HidController::NPad) 531 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
531 .SetSixAxisEnabled(parameters.sixaxis_handle, true); 532 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true);
532 533
533 LOG_DEBUG(Service_HID, 534 LOG_DEBUG(Service_HID,
534 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 535 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -536,7 +537,7 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
536 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 537 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
537 538
538 IPC::ResponseBuilder rb{ctx, 2}; 539 IPC::ResponseBuilder rb{ctx, 2};
539 rb.Push(ResultSuccess); 540 rb.Push(result);
540} 541}
541 542
542void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { 543void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -550,8 +551,8 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
550 551
551 const auto parameters{rp.PopRaw<Parameters>()}; 552 const auto parameters{rp.PopRaw<Parameters>()};
552 553
553 applet_resource->GetController<Controller_NPad>(HidController::NPad) 554 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
554 .SetSixAxisEnabled(parameters.sixaxis_handle, false); 555 const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false);
555 556
556 LOG_DEBUG(Service_HID, 557 LOG_DEBUG(Service_HID,
557 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 558 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -559,7 +560,33 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
559 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 560 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
560 561
561 IPC::ResponseBuilder rb{ctx, 2}; 562 IPC::ResponseBuilder rb{ctx, 2};
562 rb.Push(ResultSuccess); 563 rb.Push(result);
564}
565
566void Hid::IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx) {
567 IPC::RequestParser rp{ctx};
568 struct Parameters {
569 Core::HID::SixAxisSensorHandle sixaxis_handle;
570 INSERT_PADDING_WORDS_NOINIT(1);
571 u64 applet_resource_user_id;
572 };
573 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
574
575 const auto parameters{rp.PopRaw<Parameters>()};
576
577 bool is_enabled{};
578 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
579 const auto result =
580 controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
581
582 LOG_DEBUG(Service_HID,
583 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
584 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
585 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
586
587 IPC::ResponseBuilder rb{ctx, 3};
588 rb.Push(result);
589 rb.Push(is_enabled);
563} 590}
564 591
565void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { 592void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
@@ -574,9 +601,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
574 601
575 const auto parameters{rp.PopRaw<Parameters>()}; 602 const auto parameters{rp.PopRaw<Parameters>()};
576 603
577 applet_resource->GetController<Controller_NPad>(HidController::NPad) 604 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
578 .SetSixAxisFusionEnabled(parameters.sixaxis_handle, 605 const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle,
579 parameters.enable_sixaxis_sensor_fusion); 606 parameters.enable_sixaxis_sensor_fusion);
580 607
581 LOG_DEBUG(Service_HID, 608 LOG_DEBUG(Service_HID,
582 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " 609 "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
@@ -586,7 +613,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
586 parameters.applet_resource_user_id); 613 parameters.applet_resource_user_id);
587 614
588 IPC::ResponseBuilder rb{ctx, 2}; 615 IPC::ResponseBuilder rb{ctx, 2};
589 rb.Push(ResultSuccess); 616 rb.Push(result);
590} 617}
591 618
592void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 619void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -601,8 +628,9 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
601 628
602 const auto parameters{rp.PopRaw<Parameters>()}; 629 const auto parameters{rp.PopRaw<Parameters>()};
603 630
604 applet_resource->GetController<Controller_NPad>(HidController::NPad) 631 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
605 .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); 632 const auto result =
633 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
606 634
607 LOG_DEBUG(Service_HID, 635 LOG_DEBUG(Service_HID,
608 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " 636 "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
@@ -612,7 +640,7 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
612 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); 640 parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
613 641
614 IPC::ResponseBuilder rb{ctx, 2}; 642 IPC::ResponseBuilder rb{ctx, 2};
615 rb.Push(ResultSuccess); 643 rb.Push(result);
616} 644}
617 645
618void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 646void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -626,9 +654,11 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
626 654
627 const auto parameters{rp.PopRaw<Parameters>()}; 655 const auto parameters{rp.PopRaw<Parameters>()};
628 656
629 const auto sixaxis_fusion_parameters = 657 Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
630 applet_resource->GetController<Controller_NPad>(HidController::NPad) 658 const auto& controller =
631 .GetSixAxisFusionParameters(parameters.sixaxis_handle); 659 GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
660 const auto result =
661 controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
632 662
633 LOG_DEBUG(Service_HID, 663 LOG_DEBUG(Service_HID,
634 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 664 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -636,8 +666,8 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
636 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 666 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
637 667
638 IPC::ResponseBuilder rb{ctx, 4}; 668 IPC::ResponseBuilder rb{ctx, 4};
639 rb.Push(ResultSuccess); 669 rb.Push(result);
640 rb.PushRaw(sixaxis_fusion_parameters); 670 rb.PushRaw(fusion_parameters);
641} 671}
642 672
643void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { 673void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -651,8 +681,15 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
651 681
652 const auto parameters{rp.PopRaw<Parameters>()}; 682 const auto parameters{rp.PopRaw<Parameters>()};
653 683
654 applet_resource->GetController<Controller_NPad>(HidController::NPad) 684 // Since these parameters are unknow just use what HW outputs
655 .ResetSixAxisFusionParameters(parameters.sixaxis_handle); 685 const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
686 .parameter1 = 0.03f,
687 .parameter2 = 0.4f,
688 };
689 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
690 const auto result1 =
691 controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
692 const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
656 693
657 LOG_DEBUG(Service_HID, 694 LOG_DEBUG(Service_HID,
658 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 695 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -660,6 +697,14 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
660 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 697 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
661 698
662 IPC::ResponseBuilder rb{ctx, 2}; 699 IPC::ResponseBuilder rb{ctx, 2};
700 if (result1.IsError()) {
701 rb.Push(result1);
702 return;
703 }
704 if (result2.IsError()) {
705 rb.Push(result2);
706 return;
707 }
663 rb.Push(ResultSuccess); 708 rb.Push(ResultSuccess);
664} 709}
665 710
@@ -669,8 +714,8 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
669 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()}; 714 const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
670 const auto applet_resource_user_id{rp.Pop<u64>()}; 715 const auto applet_resource_user_id{rp.Pop<u64>()};
671 716
672 applet_resource->GetController<Controller_NPad>(HidController::NPad) 717 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
673 .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); 718 const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
674 719
675 LOG_DEBUG(Service_HID, 720 LOG_DEBUG(Service_HID,
676 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " 721 "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -679,7 +724,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
679 drift_mode, applet_resource_user_id); 724 drift_mode, applet_resource_user_id);
680 725
681 IPC::ResponseBuilder rb{ctx, 2}; 726 IPC::ResponseBuilder rb{ctx, 2};
682 rb.Push(ResultSuccess); 727 rb.Push(result);
683} 728}
684 729
685void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 730void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -693,15 +738,18 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
693 738
694 const auto parameters{rp.PopRaw<Parameters>()}; 739 const auto parameters{rp.PopRaw<Parameters>()};
695 740
741 auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
742 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
743 const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
744
696 LOG_DEBUG(Service_HID, 745 LOG_DEBUG(Service_HID,
697 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 746 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
698 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 747 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
699 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 748 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
700 749
701 IPC::ResponseBuilder rb{ctx, 3}; 750 IPC::ResponseBuilder rb{ctx, 3};
702 rb.Push(ResultSuccess); 751 rb.Push(result);
703 rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad) 752 rb.PushEnum(drift_mode);
704 .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
705} 753}
706 754
707void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { 755void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -714,10 +762,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
714 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); 762 static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
715 763
716 const auto parameters{rp.PopRaw<Parameters>()}; 764 const auto parameters{rp.PopRaw<Parameters>()};
717 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
718 765
719 applet_resource->GetController<Controller_NPad>(HidController::NPad) 766 const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
720 .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); 767 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
768 const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
721 769
722 LOG_DEBUG(Service_HID, 770 LOG_DEBUG(Service_HID,
723 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 771 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -725,7 +773,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
725 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 773 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
726 774
727 IPC::ResponseBuilder rb{ctx, 2}; 775 IPC::ResponseBuilder rb{ctx, 2};
728 rb.Push(ResultSuccess); 776 rb.Push(result);
729} 777}
730 778
731void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { 779void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
@@ -739,15 +787,18 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
739 787
740 const auto parameters{rp.PopRaw<Parameters>()}; 788 const auto parameters{rp.PopRaw<Parameters>()};
741 789
790 bool is_at_rest{};
791 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
792 const auto result = controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
793
742 LOG_DEBUG(Service_HID, 794 LOG_DEBUG(Service_HID,
743 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 795 "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
744 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, 796 parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
745 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 797 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
746 798
747 IPC::ResponseBuilder rb{ctx, 3}; 799 IPC::ResponseBuilder rb{ctx, 3};
748 rb.Push(ResultSuccess); 800 rb.Push(result);
749 rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) 801 rb.Push(is_at_rest);
750 .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
751} 802}
752 803
753void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) { 804void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -761,6 +812,11 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
761 812
762 const auto parameters{rp.PopRaw<Parameters>()}; 813 const auto parameters{rp.PopRaw<Parameters>()};
763 814
815 bool is_firmware_available{};
816 auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
817 const auto result = controller.IsFirmwareUpdateAvailableForSixAxisSensor(
818 parameters.sixaxis_handle, is_firmware_available);
819
764 LOG_WARNING( 820 LOG_WARNING(
765 Service_HID, 821 Service_HID,
766 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", 822 "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -768,8 +824,8 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
768 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); 824 parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
769 825
770 IPC::ResponseBuilder rb{ctx, 3}; 826 IPC::ResponseBuilder rb{ctx, 3};
771 rb.Push(ResultSuccess); 827 rb.Push(result);
772 rb.Push(false); 828 rb.Push(is_firmware_available);
773} 829}
774 830
775void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) { 831void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
@@ -878,6 +934,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
878 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", 934 LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
879 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); 935 parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
880 936
937 // Games expect this event to be signaled after calling this function
938 applet_resource->GetController<Controller_NPad>(HidController::NPad)
939 .SignalStyleSetChangedEvent(parameters.npad_id);
940
881 IPC::ResponseBuilder rb{ctx, 2, 1}; 941 IPC::ResponseBuilder rb{ctx, 2, 1};
882 rb.Push(ResultSuccess); 942 rb.Push(ResultSuccess);
883 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 943 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -1115,7 +1175,7 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
1115 rb.Push(ResultSuccess); 1175 rb.Push(ResultSuccess);
1116 } else { 1176 } else {
1117 LOG_ERROR(Service_HID, "Npads are not connected!"); 1177 LOG_ERROR(Service_HID, "Npads are not connected!");
1118 rb.Push(ERR_NPAD_NOT_CONNECTED); 1178 rb.Push(NpadNotConnected);
1119 } 1179 }
1120} 1180}
1121 1181
@@ -2124,32 +2184,6 @@ public:
2124 } 2184 }
2125}; 2185};
2126 2186
2127class HidBus final : public ServiceFramework<HidBus> {
2128public:
2129 explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} {
2130 // clang-format off
2131 static const FunctionInfo functions[] = {
2132 {1, nullptr, "GetBusHandle"},
2133 {2, nullptr, "IsExternalDeviceConnected"},
2134 {3, nullptr, "Initialize"},
2135 {4, nullptr, "Finalize"},
2136 {5, nullptr, "EnableExternalDevice"},
2137 {6, nullptr, "GetExternalDeviceId"},
2138 {7, nullptr, "SendCommandAsync"},
2139 {8, nullptr, "GetSendCommandAsynceResult"},
2140 {9, nullptr, "SetEventForSendCommandAsycResult"},
2141 {10, nullptr, "GetSharedMemoryHandle"},
2142 {11, nullptr, "EnableJoyPollingReceiveMode"},
2143 {12, nullptr, "DisableJoyPollingReceiveMode"},
2144 {13, nullptr, "GetPollingData"},
2145 {14, nullptr, "SetStatusManagerType"},
2146 };
2147 // clang-format on
2148
2149 RegisterHandlers(functions);
2150 }
2151};
2152
2153void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 2187void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
2154 std::make_shared<Hid>(system)->InstallAsService(service_manager); 2188 std::make_shared<Hid>(system)->InstallAsService(service_manager);
2155 std::make_shared<HidBus>(system)->InstallAsService(service_manager); 2189 std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c281081a7..666cb6ff5 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -103,6 +103,7 @@ private:
103 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); 103 void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
104 void StartSixAxisSensor(Kernel::HLERequestContext& ctx); 104 void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
105 void StopSixAxisSensor(Kernel::HLERequestContext& ctx); 105 void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
106 void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx);
106 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); 107 void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
107 void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); 108 void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
108 void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx); 109 void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
new file mode 100644
index 000000000..af7662a15
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -0,0 +1,531 @@
1// Copyright 2021 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 "common/settings.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/core_timing_util.h"
10#include "core/hid/hid_types.h"
11#include "core/hle/ipc_helpers.h"
12#include "core/hle/kernel/k_event.h"
13#include "core/hle/kernel/k_readable_event.h"
14#include "core/hle/kernel/k_shared_memory.h"
15#include "core/hle/kernel/k_transfer_memory.h"
16#include "core/hle/service/hid/hidbus.h"
17#include "core/hle/service/hid/hidbus/ringcon.h"
18#include "core/hle/service/hid/hidbus/starlink.h"
19#include "core/hle/service/hid/hidbus/stubbed.h"
20#include "core/hle/service/service.h"
21#include "core/memory.h"
22
23namespace Service::HID {
24// (15ms, 66Hz)
25constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
26
27HidBus::HidBus(Core::System& system_)
28 : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
29
30 // clang-format off
31 static const FunctionInfo functions[] = {
32 {1, &HidBus::GetBusHandle, "GetBusHandle"},
33 {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
34 {3, &HidBus::Initialize, "Initialize"},
35 {4, &HidBus::Finalize, "Finalize"},
36 {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
37 {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
38 {7, &HidBus::SendCommandAsync, "SendCommandAsync"},
39 {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
40 {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
41 {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
42 {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
43 {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
44 {13, nullptr, "GetPollingData"},
45 {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
46 };
47 // clang-format on
48
49 RegisterHandlers(functions);
50
51 // Register update callbacks
52 hidbus_update_event = Core::Timing::CreateEvent(
53 "Hidbus::UpdateCallback",
54 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
55 const auto guard = LockService();
56 UpdateHidbus(user_data, ns_late);
57 });
58
59 system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
60}
61
62HidBus::~HidBus() {
63 system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
64}
65
66void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
67 auto& core_timing = system.CoreTiming();
68
69 if (is_hidbus_enabled) {
70 for (std::size_t i = 0; i < devices.size(); ++i) {
71 if (!devices[i].is_device_initializated) {
72 continue;
73 }
74 auto& device = devices[i].device;
75 device->OnUpdate();
76 auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
77 cur_entry.is_polling_mode = device->IsPollingMode();
78 cur_entry.polling_mode = device->GetPollingMode();
79 cur_entry.is_enabled = device->IsEnabled();
80
81 u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
82 std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
83 sizeof(HidbusStatusManagerEntry));
84 }
85 }
86
87 // If ns_late is higher than the update rate ignore the delay
88 if (ns_late > hidbus_update_ns) {
89 ns_late = {};
90 }
91
92 core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
93}
94
95std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
96 for (std::size_t i = 0; i < devices.size(); ++i) {
97 const auto& device_handle = devices[i].handle;
98 if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
99 handle.internal_index == device_handle.internal_index &&
100 handle.player_number == device_handle.player_number &&
101 handle.bus_type == device_handle.bus_type &&
102 handle.is_valid == device_handle.is_valid) {
103 return i;
104 }
105 }
106 return std::nullopt;
107}
108
109void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) {
110 IPC::RequestParser rp{ctx};
111 struct Parameters {
112 Core::HID::NpadIdType npad_id;
113 INSERT_PADDING_WORDS_NOINIT(1);
114 BusType bus_type;
115 u64 applet_resource_user_id;
116 };
117 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
118
119 const auto parameters{rp.PopRaw<Parameters>()};
120
121 LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
122 parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
123
124 bool is_handle_found = 0;
125 std::size_t handle_index = 0;
126
127 for (std::size_t i = 0; i < devices.size(); i++) {
128 const auto& handle = devices[i].handle;
129 if (!handle.is_valid) {
130 continue;
131 }
132 if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
133 handle.bus_type == parameters.bus_type) {
134 is_handle_found = true;
135 handle_index = i;
136 break;
137 }
138 }
139
140 // Handle not found. Create a new one
141 if (!is_handle_found) {
142 for (std::size_t i = 0; i < devices.size(); i++) {
143 if (devices[i].handle.is_valid) {
144 continue;
145 }
146 devices[i].handle = {
147 .abstracted_pad_id = static_cast<u8>(i),
148 .internal_index = static_cast<u8>(i),
149 .player_number = static_cast<u8>(parameters.npad_id),
150 .bus_type = parameters.bus_type,
151 .is_valid = true,
152 };
153 handle_index = i;
154 break;
155 }
156 }
157
158 struct OutData {
159 bool is_valid;
160 INSERT_PADDING_BYTES(7);
161 BusHandle handle;
162 };
163 static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
164
165 const OutData out_data{
166 .is_valid = true,
167 .handle = devices[handle_index].handle,
168 };
169
170 IPC::ResponseBuilder rb{ctx, 6};
171 rb.Push(ResultSuccess);
172 rb.PushRaw(out_data);
173}
174
175void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
176 IPC::RequestParser rp{ctx};
177 const auto bus_handle_{rp.PopRaw<BusHandle>()};
178
179 LOG_INFO(Service_HID,
180 "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
181 "player_number={}, is_valid={}",
182 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
183 bus_handle_.player_number, bus_handle_.is_valid);
184
185 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
186
187 if (device_index) {
188 const auto& device = devices[device_index.value()].device;
189 const bool is_attached = device->IsDeviceActivated();
190
191 IPC::ResponseBuilder rb{ctx, 3};
192 rb.Push(ResultSuccess);
193 rb.Push(is_attached);
194 return;
195 }
196
197 LOG_ERROR(Service_HID, "Invalid handle");
198 IPC::ResponseBuilder rb{ctx, 2};
199 rb.Push(ResultUnknown);
200 return;
201}
202
203void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
204 IPC::RequestParser rp{ctx};
205 const auto bus_handle_{rp.PopRaw<BusHandle>()};
206 const auto applet_resource_user_id{rp.Pop<u64>()};
207
208 LOG_INFO(Service_HID,
209 "called, abstracted_pad_id={} bus_type={} internal_index={} "
210 "player_number={} is_valid={}, applet_resource_user_id={}",
211 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
212 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
213
214 is_hidbus_enabled = true;
215
216 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
217
218 if (device_index) {
219 const auto entry_index = devices[device_index.value()].handle.internal_index;
220 auto& cur_entry = hidbus_status.entries[entry_index];
221
222 if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
223 MakeDevice<RingController>(bus_handle_);
224 devices[device_index.value()].is_device_initializated = true;
225 devices[device_index.value()].device->ActivateDevice();
226 cur_entry.is_in_focus = true;
227 cur_entry.is_connected = true;
228 cur_entry.is_connected_result = ResultSuccess;
229 cur_entry.is_enabled = false;
230 cur_entry.is_polling_mode = false;
231 } else {
232 MakeDevice<HidbusStubbed>(bus_handle_);
233 devices[device_index.value()].is_device_initializated = true;
234 cur_entry.is_in_focus = true;
235 cur_entry.is_connected = false;
236 cur_entry.is_connected_result = ResultSuccess;
237 cur_entry.is_enabled = false;
238 cur_entry.is_polling_mode = false;
239 }
240
241 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
242 sizeof(hidbus_status));
243
244 IPC::ResponseBuilder rb{ctx, 2};
245 rb.Push(ResultSuccess);
246 return;
247 }
248
249 LOG_ERROR(Service_HID, "Invalid handle");
250 IPC::ResponseBuilder rb{ctx, 2};
251 rb.Push(ResultUnknown);
252 return;
253}
254
255void HidBus::Finalize(Kernel::HLERequestContext& ctx) {
256 IPC::RequestParser rp{ctx};
257 const auto bus_handle_{rp.PopRaw<BusHandle>()};
258 const auto applet_resource_user_id{rp.Pop<u64>()};
259
260 LOG_INFO(Service_HID,
261 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
262 "player_number={}, is_valid={}, applet_resource_user_id={}",
263 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
264 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
265
266 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
267
268 if (device_index) {
269 const auto entry_index = devices[device_index.value()].handle.internal_index;
270 auto& cur_entry = hidbus_status.entries[entry_index];
271 auto& device = devices[device_index.value()].device;
272 devices[device_index.value()].is_device_initializated = false;
273 device->DeactivateDevice();
274
275 cur_entry.is_in_focus = true;
276 cur_entry.is_connected = false;
277 cur_entry.is_connected_result = ResultSuccess;
278 cur_entry.is_enabled = false;
279 cur_entry.is_polling_mode = false;
280 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
281 sizeof(hidbus_status));
282
283 IPC::ResponseBuilder rb{ctx, 2};
284 rb.Push(ResultSuccess);
285 return;
286 }
287
288 LOG_ERROR(Service_HID, "Invalid handle");
289 IPC::ResponseBuilder rb{ctx, 2};
290 rb.Push(ResultUnknown);
291 return;
292}
293
294void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
295 IPC::RequestParser rp{ctx};
296 struct Parameters {
297 bool enable;
298 INSERT_PADDING_BYTES_NOINIT(7);
299 BusHandle bus_handle;
300 u64 inval;
301 u64 applet_resource_user_id;
302 };
303 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
304
305 const auto parameters{rp.PopRaw<Parameters>()};
306
307 LOG_INFO(Service_HID,
308 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
309 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
310 parameters.enable, parameters.bus_handle.abstracted_pad_id,
311 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
312 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
313 parameters.applet_resource_user_id);
314
315 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
316
317 if (device_index) {
318 auto& device = devices[device_index.value()].device;
319 device->Enable(parameters.enable);
320
321 IPC::ResponseBuilder rb{ctx, 2};
322 rb.Push(ResultSuccess);
323 return;
324 }
325
326 LOG_ERROR(Service_HID, "Invalid handle");
327 IPC::ResponseBuilder rb{ctx, 2};
328 rb.Push(ResultUnknown);
329 return;
330}
331
332void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
333 IPC::RequestParser rp{ctx};
334 const auto bus_handle_{rp.PopRaw<BusHandle>()};
335
336 LOG_INFO(Service_HID,
337 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
338 "is_valid={}",
339 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
340 bus_handle_.player_number, bus_handle_.is_valid);
341
342 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
343
344 if (device_index) {
345 const auto& device = devices[device_index.value()].device;
346 u32 device_id = device->GetDeviceId();
347 IPC::ResponseBuilder rb{ctx, 3};
348 rb.Push(ResultSuccess);
349 rb.Push<u32>(device_id);
350 return;
351 }
352
353 LOG_ERROR(Service_HID, "Invalid handle");
354 IPC::ResponseBuilder rb{ctx, 2};
355 rb.Push(ResultUnknown);
356 return;
357}
358
359void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) {
360 IPC::RequestParser rp{ctx};
361 const auto data = ctx.ReadBuffer();
362 const auto bus_handle_{rp.PopRaw<BusHandle>()};
363
364 LOG_DEBUG(Service_HID,
365 "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
366 "player_number={}, is_valid={}",
367 data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
368 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
369
370 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
371
372 if (device_index) {
373 auto& device = devices[device_index.value()].device;
374 device->SetCommand(data);
375
376 IPC::ResponseBuilder rb{ctx, 2};
377 rb.Push(ResultSuccess);
378 return;
379 }
380
381 LOG_ERROR(Service_HID, "Invalid handle");
382 IPC::ResponseBuilder rb{ctx, 2};
383 rb.Push(ResultUnknown);
384 return;
385};
386
387void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) {
388 IPC::RequestParser rp{ctx};
389 const auto bus_handle_{rp.PopRaw<BusHandle>()};
390
391 LOG_DEBUG(Service_HID,
392 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
393 "is_valid={}",
394 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
395 bus_handle_.player_number, bus_handle_.is_valid);
396
397 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
398
399 if (device_index) {
400 const auto& device = devices[device_index.value()].device;
401 const std::vector<u8> data = device->GetReply();
402 const u64 data_size = ctx.WriteBuffer(data);
403
404 IPC::ResponseBuilder rb{ctx, 4};
405 rb.Push(ResultSuccess);
406 rb.Push<u64>(data_size);
407 return;
408 }
409
410 LOG_ERROR(Service_HID, "Invalid handle");
411 IPC::ResponseBuilder rb{ctx, 2};
412 rb.Push(ResultUnknown);
413 return;
414};
415
416void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) {
417 IPC::RequestParser rp{ctx};
418 const auto bus_handle_{rp.PopRaw<BusHandle>()};
419
420 LOG_INFO(Service_HID,
421 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
422 "is_valid={}",
423 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
424 bus_handle_.player_number, bus_handle_.is_valid);
425
426 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
427
428 if (device_index) {
429 const auto& device = devices[device_index.value()].device;
430 IPC::ResponseBuilder rb{ctx, 2, 1};
431 rb.Push(ResultSuccess);
432 rb.PushCopyObjects(device->GetSendCommandAsycEvent());
433 return;
434 }
435
436 LOG_ERROR(Service_HID, "Invalid handle");
437 IPC::ResponseBuilder rb{ctx, 2};
438 rb.Push(ResultUnknown);
439 return;
440};
441
442void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
443 LOG_DEBUG(Service_HID, "called");
444
445 IPC::ResponseBuilder rb{ctx, 2, 1};
446 rb.Push(ResultSuccess);
447 rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
448}
449
450void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
451 IPC::RequestParser rp{ctx};
452 const auto t_mem_size{rp.Pop<u32>()};
453 const auto t_mem_handle{ctx.GetCopyHandle(0)};
454 const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
455 const auto bus_handle_{rp.PopRaw<BusHandle>()};
456
457 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
458
459 auto t_mem =
460 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
461
462 if (t_mem.IsNull()) {
463 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
464 IPC::ResponseBuilder rb{ctx, 2};
465 rb.Push(ResultUnknown);
466 return;
467 }
468
469 ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
470
471 LOG_INFO(Service_HID,
472 "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
473 "internal_index={}, player_number={}, is_valid={}",
474 t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
475 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
476
477 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
478
479 if (device_index) {
480 auto& device = devices[device_index.value()].device;
481 device->SetPollingMode(polling_mode_);
482 device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress()));
483
484 IPC::ResponseBuilder rb{ctx, 2};
485 rb.Push(ResultSuccess);
486 return;
487 }
488
489 LOG_ERROR(Service_HID, "Invalid handle");
490 IPC::ResponseBuilder rb{ctx, 2};
491 rb.Push(ResultUnknown);
492 return;
493}
494
495void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
496 IPC::RequestParser rp{ctx};
497 const auto bus_handle_{rp.PopRaw<BusHandle>()};
498
499 LOG_INFO(Service_HID,
500 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
501 "is_valid={}",
502 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
503 bus_handle_.player_number, bus_handle_.is_valid);
504
505 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
506
507 if (device_index) {
508 auto& device = devices[device_index.value()].device;
509 device->DisablePollingMode();
510
511 IPC::ResponseBuilder rb{ctx, 2};
512 rb.Push(ResultSuccess);
513 return;
514 }
515
516 LOG_ERROR(Service_HID, "Invalid handle");
517 IPC::ResponseBuilder rb{ctx, 2};
518 rb.Push(ResultUnknown);
519 return;
520}
521
522void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) {
523 IPC::RequestParser rp{ctx};
524 const auto manager_type{rp.PopEnum<StatusManagerType>()};
525
526 LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
527
528 IPC::ResponseBuilder rb{ctx, 2};
529 rb.Push(ResultSuccess);
530};
531} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
new file mode 100644
index 000000000..b10d5156a
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.h
@@ -0,0 +1,131 @@
1// Copyright 2021 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
9#include "core/hle/service/hid/hidbus/hidbus_base.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h"
12
13namespace Core::Timing {
14struct EventType;
15} // namespace Core::Timing
16
17namespace Core {
18class System;
19} // namespace Core
20
21namespace Service::HID {
22
23class HidBus final : public ServiceFramework<HidBus> {
24public:
25 explicit HidBus(Core::System& system_);
26 ~HidBus() override;
27
28private:
29 static const std::size_t max_number_of_handles = 0x13;
30
31 enum class HidBusDeviceId : std::size_t {
32 RingController = 0x20,
33 FamicomRight = 0x21,
34 Starlink = 0x28,
35 };
36
37 // This is nn::hidbus::detail::StatusManagerType
38 enum class StatusManagerType : u32 {
39 None,
40 Type16,
41 Type32,
42 };
43
44 // This is nn::hidbus::BusType
45 enum class BusType : u8 {
46 LeftJoyRail,
47 RightJoyRail,
48 InternalBus, // Lark microphone
49
50 MaxBusType,
51 };
52
53 // This is nn::hidbus::BusHandle
54 struct BusHandle {
55 u32 abstracted_pad_id;
56 u8 internal_index;
57 u8 player_number;
58 BusType bus_type;
59 bool is_valid;
60 };
61 static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
62
63 // This is nn::hidbus::JoyPollingReceivedData
64 struct JoyPollingReceivedData {
65 std::array<u8, 0x30> data;
66 u64 out_size;
67 u64 sampling_number;
68 };
69 static_assert(sizeof(JoyPollingReceivedData) == 0x40,
70 "JoyPollingReceivedData is an invalid size");
71
72 struct HidbusStatusManagerEntry {
73 u8 is_connected{};
74 INSERT_PADDING_BYTES(0x3);
75 ResultCode is_connected_result{0};
76 u8 is_enabled{};
77 u8 is_in_focus{};
78 u8 is_polling_mode{};
79 u8 reserved{};
80 JoyPollingMode polling_mode{};
81 INSERT_PADDING_BYTES(0x70); // Unknown
82 };
83 static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
84 "HidbusStatusManagerEntry is an invalid size");
85
86 struct HidbusStatusManager {
87 std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
88 INSERT_PADDING_BYTES(0x680); // Unused
89 };
90 static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
91
92 struct HidbusDevice {
93 bool is_device_initializated{};
94 BusHandle handle{};
95 std::unique_ptr<HidbusBase> device{nullptr};
96 };
97
98 void GetBusHandle(Kernel::HLERequestContext& ctx);
99 void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx);
100 void Initialize(Kernel::HLERequestContext& ctx);
101 void Finalize(Kernel::HLERequestContext& ctx);
102 void EnableExternalDevice(Kernel::HLERequestContext& ctx);
103 void GetExternalDeviceId(Kernel::HLERequestContext& ctx);
104 void SendCommandAsync(Kernel::HLERequestContext& ctx);
105 void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx);
106 void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx);
107 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
108 void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
109 void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
110 void SetStatusManagerType(Kernel::HLERequestContext& ctx);
111
112 void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
113 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
114
115 template <typename T>
116 void MakeDevice(BusHandle handle) {
117 const auto device_index = GetDeviceIndexFromHandle(handle);
118 if (device_index) {
119 devices[device_index.value()].device =
120 std::make_unique<T>(system.HIDCore(), service_context);
121 }
122 }
123
124 bool is_hidbus_enabled{false};
125 HidbusStatusManager hidbus_status{};
126 std::array<HidbusDevice, max_number_of_handles> devices{};
127 std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
128 KernelHelpers::ServiceContext service_context;
129};
130
131} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
new file mode 100644
index 000000000..09bff10e5
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
@@ -0,0 +1,72 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/hid_core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_readable_event.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9#include "core/hle/service/kernel_helpers.h"
10
11namespace Service::HID {
12
13HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
14 : service_context(service_context_) {
15 send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
16}
17HidbusBase::~HidbusBase() = default;
18
19void HidbusBase::ActivateDevice() {
20 if (is_activated) {
21 return;
22 }
23 is_activated = true;
24 OnInit();
25}
26
27void HidbusBase::DeactivateDevice() {
28 if (is_activated) {
29 OnRelease();
30 }
31 is_activated = false;
32}
33
34bool HidbusBase::IsDeviceActivated() const {
35 return is_activated;
36}
37
38void HidbusBase::Enable(bool enable) {
39 device_enabled = enable;
40}
41
42bool HidbusBase::IsEnabled() const {
43 return device_enabled;
44}
45
46bool HidbusBase::IsPollingMode() const {
47 return polling_mode_enabled;
48}
49
50JoyPollingMode HidbusBase::GetPollingMode() const {
51 return polling_mode;
52}
53
54void HidbusBase::SetPollingMode(JoyPollingMode mode) {
55 polling_mode = mode;
56 polling_mode_enabled = true;
57}
58
59void HidbusBase::DisablePollingMode() {
60 polling_mode_enabled = false;
61}
62
63void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
64 is_transfer_memory_set = true;
65 transfer_memory = t_mem;
66}
67
68Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
69 return send_command_async_event->GetReadableEvent();
70}
71
72} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h
new file mode 100644
index 000000000..13d073a3d
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -0,0 +1,179 @@
1// Copyright 2021 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#include "common/common_types.h"
9#include "core/hle/result.h"
10
11namespace Kernel {
12class KEvent;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::KernelHelpers {
17class ServiceContext;
18}
19
20namespace Service::HID {
21
22// This is nn::hidbus::JoyPollingMode
23enum class JoyPollingMode : u32 {
24 SixAxisSensorDisable,
25 SixAxisSensorEnable,
26 ButtonOnly,
27};
28
29struct DataAccessorHeader {
30 ResultCode result{ResultUnknown};
31 INSERT_PADDING_WORDS(0x1);
32 std::array<u8, 0x18> unused{};
33 u64 latest_entry{};
34 u64 total_entries{};
35};
36static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
37
38struct JoyDisableSixAxisPollingData {
39 std::array<u8, 0x26> data;
40 u8 out_size;
41 INSERT_PADDING_BYTES(0x1);
42 u64 sampling_number;
43};
44static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
45 "JoyDisableSixAxisPollingData is an invalid size");
46
47struct JoyEnableSixAxisPollingData {
48 std::array<u8, 0x8> data;
49 u8 out_size;
50 INSERT_PADDING_BYTES(0x7);
51 u64 sampling_number;
52};
53static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
54 "JoyEnableSixAxisPollingData is an invalid size");
55
56struct JoyButtonOnlyPollingData {
57 std::array<u8, 0x2c> data;
58 u8 out_size;
59 INSERT_PADDING_BYTES(0x3);
60 u64 sampling_number;
61};
62static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
63 "JoyButtonOnlyPollingData is an invalid size");
64
65struct JoyDisableSixAxisPollingEntry {
66 u64 sampling_number;
67 JoyDisableSixAxisPollingData polling_data;
68};
69static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
70 "JoyDisableSixAxisPollingEntry is an invalid size");
71
72struct JoyEnableSixAxisPollingEntry {
73 u64 sampling_number;
74 JoyEnableSixAxisPollingData polling_data;
75};
76static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
77 "JoyEnableSixAxisPollingEntry is an invalid size");
78
79struct JoyButtonOnlyPollingEntry {
80 u64 sampling_number;
81 JoyButtonOnlyPollingData polling_data;
82};
83static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
84 "JoyButtonOnlyPollingEntry is an invalid size");
85
86struct JoyDisableSixAxisDataAccessor {
87 DataAccessorHeader header{};
88 std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
89};
90static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
91 "JoyDisableSixAxisDataAccessor is an invalid size");
92
93struct JoyEnableSixAxisDataAccessor {
94 DataAccessorHeader header{};
95 std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
96};
97static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
98 "JoyEnableSixAxisDataAccessor is an invalid size");
99
100struct ButtonOnlyPollingDataAccessor {
101 DataAccessorHeader header;
102 std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
103};
104static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
105 "ButtonOnlyPollingDataAccessor is an invalid size");
106
107class HidbusBase {
108public:
109 explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
110 virtual ~HidbusBase();
111
112 void ActivateDevice();
113
114 void DeactivateDevice();
115
116 bool IsDeviceActivated() const;
117
118 // Enables/disables the device
119 void Enable(bool enable);
120
121 // returns true if device is enabled
122 bool IsEnabled() const;
123
124 // returns true if polling mode is enabled
125 bool IsPollingMode() const;
126
127 // returns polling mode
128 JoyPollingMode GetPollingMode() const;
129
130 // Sets and enables JoyPollingMode
131 void SetPollingMode(JoyPollingMode mode);
132
133 // Disables JoyPollingMode
134 void DisablePollingMode();
135
136 // Called on EnableJoyPollingReceiveMode
137 void SetTransferMemoryPointer(u8* t_mem);
138
139 Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
140
141 virtual void OnInit() {}
142
143 virtual void OnRelease() {}
144
145 // Updates device transfer memory
146 virtual void OnUpdate() {}
147
148 // Returns the device ID of the joycon
149 virtual u8 GetDeviceId() const {
150 return {};
151 }
152
153 // Assigns a command from data
154 virtual bool SetCommand(const std::vector<u8>& data) {
155 return {};
156 }
157
158 // Returns a reply from a command
159 virtual std::vector<u8> GetReply() const {
160 return {};
161 }
162
163protected:
164 bool is_activated{};
165 bool device_enabled{};
166 bool polling_mode_enabled{};
167 JoyPollingMode polling_mode = {};
168 // TODO(German77): All data accessors need to be replaced with a ring lifo object
169 JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
170 JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
171 ButtonOnlyPollingDataAccessor button_only_data{};
172
173 u8* transfer_memory{nullptr};
174 bool is_transfer_memory_set{};
175
176 Kernel::KEvent* send_command_async_event;
177 KernelHelpers::ServiceContext& service_context;
178};
179} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
new file mode 100644
index 000000000..5ec3cc83c
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -0,0 +1,286 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_devices.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/service/hid/hidbus/ringcon.h"
10
11namespace Service::HID {
12
13RingController::RingController(Core::HID::HIDCore& hid_core_,
14 KernelHelpers::ServiceContext& service_context_)
15 : HidbusBase(service_context_) {
16 input = hid_core_.GetEmulatedDevices();
17}
18
19RingController::~RingController() = default;
20
21void RingController::OnInit() {
22 return;
23}
24
25void RingController::OnRelease() {
26 return;
27};
28
29void RingController::OnUpdate() {
30 if (!is_activated) {
31 return;
32 }
33
34 if (!device_enabled) {
35 return;
36 }
37
38 if (!polling_mode_enabled || !is_transfer_memory_set) {
39 return;
40 }
41
42 // TODO: Increment multitasking counters from motion and sensor data
43
44 switch (polling_mode) {
45 case JoyPollingMode::SixAxisSensorEnable: {
46 enable_sixaxis_data.header.total_entries = 10;
47 enable_sixaxis_data.header.result = ResultSuccess;
48 const auto& last_entry =
49 enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
50
51 enable_sixaxis_data.header.latest_entry =
52 (enable_sixaxis_data.header.latest_entry + 1) % 10;
53 auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
54
55 curr_entry.sampling_number = last_entry.sampling_number + 1;
56 curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
57
58 const RingConData ringcon_value = GetSensorValue();
59 curr_entry.polling_data.out_size = sizeof(ringcon_value);
60 std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
61
62 std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
63 break;
64 }
65 default:
66 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
67 break;
68 }
69}
70
71RingController::RingConData RingController::GetSensorValue() const {
72 RingConData ringcon_sensor_value{
73 .status = DataValid::Valid,
74 .data = 0,
75 };
76
77 const f32 force_value = input->GetRingSensorForce().force * range;
78 ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
79
80 return ringcon_sensor_value;
81}
82
83u8 RingController::GetDeviceId() const {
84 return device_id;
85}
86
87std::vector<u8> RingController::GetReply() const {
88 const RingConCommands current_command = command;
89
90 switch (current_command) {
91 case RingConCommands::GetFirmwareVersion:
92 return GetFirmwareVersionReply();
93 case RingConCommands::ReadId:
94 return GetReadIdReply();
95 case RingConCommands::c20105:
96 return GetC020105Reply();
97 case RingConCommands::ReadUnkCal:
98 return GetReadUnkCalReply();
99 case RingConCommands::ReadFactoryCal:
100 return GetReadFactoryCalReply();
101 case RingConCommands::ReadUserCal:
102 return GetReadUserCalReply();
103 case RingConCommands::ReadRepCount:
104 return GetReadRepCountReply();
105 case RingConCommands::ReadTotalPushCount:
106 return GetReadTotalPushCountReply();
107 case RingConCommands::ResetRepCount:
108 return GetResetRepCountReply();
109 case RingConCommands::SaveCalData:
110 return GetSaveDataReply();
111 default:
112 return GetErrorReply();
113 }
114}
115
116bool RingController::SetCommand(const std::vector<u8>& data) {
117 if (data.size() < 4) {
118 LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
119 command = RingConCommands::Error;
120 return false;
121 }
122
123 std::memcpy(&command, data.data(), sizeof(RingConCommands));
124
125 switch (command) {
126 case RingConCommands::GetFirmwareVersion:
127 case RingConCommands::ReadId:
128 case RingConCommands::c20105:
129 case RingConCommands::ReadUnkCal:
130 case RingConCommands::ReadFactoryCal:
131 case RingConCommands::ReadUserCal:
132 case RingConCommands::ReadRepCount:
133 case RingConCommands::ReadTotalPushCount:
134 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
135 send_command_async_event->GetWritableEvent().Signal();
136 return true;
137 case RingConCommands::ResetRepCount:
138 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
139 total_rep_count = 0;
140 send_command_async_event->GetWritableEvent().Signal();
141 return true;
142 case RingConCommands::SaveCalData: {
143 ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
144
145 SaveCalData save_info{};
146 std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
147 user_calibration = save_info.calibration;
148 send_command_async_event->GetWritableEvent().Signal();
149 return true;
150 }
151 default:
152 LOG_ERROR(Service_HID, "Command not implemented {}", command);
153 command = RingConCommands::Error;
154 // Signal a reply to avoid softlocking the game
155 send_command_async_event->GetWritableEvent().Signal();
156 return false;
157 }
158}
159
160std::vector<u8> RingController::GetFirmwareVersionReply() const {
161 const FirmwareVersionReply reply{
162 .status = DataValid::Valid,
163 .firmware = version,
164 };
165
166 return GetDataVector(reply);
167}
168
169std::vector<u8> RingController::GetReadIdReply() const {
170 // The values are hardcoded from a real joycon
171 const ReadIdReply reply{
172 .status = DataValid::Valid,
173 .id_l_x0 = 8,
174 .id_l_x0_2 = 41,
175 .id_l_x4 = 22294,
176 .id_h_x0 = 19777,
177 .id_h_x0_2 = 13621,
178 .id_h_x4 = 8245,
179 };
180
181 return GetDataVector(reply);
182}
183
184std::vector<u8> RingController::GetC020105Reply() const {
185 const Cmd020105Reply reply{
186 .status = DataValid::Valid,
187 .data = 1,
188 };
189
190 return GetDataVector(reply);
191}
192
193std::vector<u8> RingController::GetReadUnkCalReply() const {
194 const ReadUnkCalReply reply{
195 .status = DataValid::Valid,
196 .data = 0,
197 };
198
199 return GetDataVector(reply);
200}
201
202std::vector<u8> RingController::GetReadFactoryCalReply() const {
203 const ReadFactoryCalReply reply{
204 .status = DataValid::Valid,
205 .calibration = factory_calibration,
206 };
207
208 return GetDataVector(reply);
209}
210
211std::vector<u8> RingController::GetReadUserCalReply() const {
212 const ReadUserCalReply reply{
213 .status = DataValid::Valid,
214 .calibration = user_calibration,
215 };
216
217 return GetDataVector(reply);
218}
219
220std::vector<u8> RingController::GetReadRepCountReply() const {
221 const GetThreeByteReply reply{
222 .status = DataValid::Valid,
223 .data = {total_rep_count, 0, 0},
224 .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
225 };
226
227 return GetDataVector(reply);
228}
229
230std::vector<u8> RingController::GetReadTotalPushCountReply() const {
231 const GetThreeByteReply reply{
232 .status = DataValid::Valid,
233 .data = {total_push_count, 0, 0},
234 .crc = GetCrcValue({total_push_count, 0, 0, 0}),
235 };
236
237 return GetDataVector(reply);
238}
239
240std::vector<u8> RingController::GetResetRepCountReply() const {
241 return GetReadRepCountReply();
242}
243
244std::vector<u8> RingController::GetSaveDataReply() const {
245 const StatusReply reply{
246 .status = DataValid::Valid,
247 };
248
249 return GetDataVector(reply);
250}
251
252std::vector<u8> RingController::GetErrorReply() const {
253 const ErrorReply reply{
254 .status = DataValid::BadCRC,
255 };
256
257 return GetDataVector(reply);
258}
259
260u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
261 u8 crc = 0;
262 for (std::size_t index = 0; index < data.size(); index++) {
263 for (u8 i = 0x80; i > 0; i >>= 1) {
264 bool bit = (crc & 0x80) != 0;
265 if ((data[index] & i) != 0) {
266 bit = !bit;
267 }
268 crc <<= 1;
269 if (bit) {
270 crc ^= 0x8d;
271 }
272 }
273 }
274 return crc;
275}
276
277template <typename T>
278std::vector<u8> RingController::GetDataVector(const T& reply) const {
279 static_assert(std::is_trivially_copyable_v<T>);
280 std::vector<u8> data;
281 data.resize(sizeof(reply));
282 std::memcpy(data.data(), &reply, sizeof(reply));
283 return data;
284}
285
286} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
new file mode 100644
index 000000000..2dbc6150e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -0,0 +1,254 @@
1// Copyright 2021 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/service/hid/hidbus/hidbus_base.h"
11
12namespace Core::HID {
13class EmulatedDevices;
14} // namespace Core::HID
15
16namespace Service::HID {
17
18class RingController final : public HidbusBase {
19public:
20 explicit RingController(Core::HID::HIDCore& hid_core_,
21 KernelHelpers::ServiceContext& service_context_);
22 ~RingController() override;
23
24 void OnInit() override;
25
26 void OnRelease() override;
27
28 // Updates ringcon transfer memory
29 void OnUpdate() override;
30
31 // Returns the device ID of the joycon
32 u8 GetDeviceId() const override;
33
34 // Assigns a command from data
35 bool SetCommand(const std::vector<u8>& data) override;
36
37 // Returns a reply from a command
38 std::vector<u8> GetReply() const override;
39
40private:
41 // These values are obtained from a real ring controller
42 static constexpr s16 idle_value = 2280;
43 static constexpr s16 idle_deadzone = 120;
44 static constexpr s16 range = 2500;
45
46 // Most missing command names are leftovers from other firmware versions
47 enum class RingConCommands : u32 {
48 GetFirmwareVersion = 0x00020000,
49 ReadId = 0x00020100,
50 JoyPolling = 0x00020101,
51 Unknown1 = 0x00020104,
52 c20105 = 0x00020105,
53 Unknown2 = 0x00020204,
54 Unknown3 = 0x00020304,
55 Unknown4 = 0x00020404,
56 ReadUnkCal = 0x00020504,
57 ReadFactoryCal = 0x00020A04,
58 Unknown5 = 0x00021104,
59 Unknown6 = 0x00021204,
60 Unknown7 = 0x00021304,
61 ReadUserCal = 0x00021A04,
62 ReadRepCount = 0x00023104,
63 ReadTotalPushCount = 0x00023204,
64 ResetRepCount = 0x04013104,
65 Unknown8 = 0x04011104,
66 Unknown9 = 0x04011204,
67 Unknown10 = 0x04011304,
68 SaveCalData = 0x10011A04,
69 Error = 0xFFFFFFFF,
70 };
71
72 enum class DataValid : u32 {
73 Valid,
74 BadCRC,
75 Cal,
76 };
77
78 struct FirmwareVersion {
79 u8 sub;
80 u8 main;
81 };
82 static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
83
84 struct FactoryCalibration {
85 s32_le os_max;
86 s32_le hk_max;
87 s32_le zero_min;
88 s32_le zero_max;
89 };
90 static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
91
92 struct CalibrationValue {
93 s16 value;
94 u16 crc;
95 };
96 static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
97
98 struct UserCalibration {
99 CalibrationValue os_max;
100 CalibrationValue hk_max;
101 CalibrationValue zero;
102 };
103 static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
104
105 struct SaveCalData {
106 RingConCommands command;
107 UserCalibration calibration;
108 INSERT_PADDING_BYTES_NOINIT(4);
109 };
110 static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
111 static_assert(std::is_trivially_copyable_v<SaveCalData>,
112 "SaveCalData must be trivially copyable");
113
114 struct FirmwareVersionReply {
115 DataValid status;
116 FirmwareVersion firmware;
117 INSERT_PADDING_BYTES(0x2);
118 };
119 static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
120
121 struct Cmd020105Reply {
122 DataValid status;
123 u8 data;
124 INSERT_PADDING_BYTES(0x3);
125 };
126 static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
127
128 struct StatusReply {
129 DataValid status;
130 };
131 static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
132
133 struct GetThreeByteReply {
134 DataValid status;
135 std::array<u8, 3> data;
136 u8 crc;
137 };
138 static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
139
140 struct ReadUnkCalReply {
141 DataValid status;
142 u16 data;
143 INSERT_PADDING_BYTES(0x2);
144 };
145 static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
146
147 struct ReadFactoryCalReply {
148 DataValid status;
149 FactoryCalibration calibration;
150 };
151 static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
152
153 struct ReadUserCalReply {
154 DataValid status;
155 UserCalibration calibration;
156 INSERT_PADDING_BYTES(0x4);
157 };
158 static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
159
160 struct ReadIdReply {
161 DataValid status;
162 u16 id_l_x0;
163 u16 id_l_x0_2;
164 u16 id_l_x4;
165 u16 id_h_x0;
166 u16 id_h_x0_2;
167 u16 id_h_x4;
168 };
169 static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
170
171 struct ErrorReply {
172 DataValid status;
173 INSERT_PADDING_BYTES(0x3);
174 };
175 static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
176
177 struct RingConData {
178 DataValid status;
179 s16_le data;
180 INSERT_PADDING_BYTES(0x2);
181 };
182 static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
183
184 // Returns RingConData struct with pressure sensor values
185 RingConData GetSensorValue() const;
186
187 // Returns 8 byte reply with firmware version
188 std::vector<u8> GetFirmwareVersionReply() const;
189
190 // Returns 16 byte reply with ID values
191 std::vector<u8> GetReadIdReply() const;
192
193 // (STUBBED) Returns 8 byte reply
194 std::vector<u8> GetC020105Reply() const;
195
196 // (STUBBED) Returns 8 byte empty reply
197 std::vector<u8> GetReadUnkCalReply() const;
198
199 // Returns 20 byte reply with factory calibration values
200 std::vector<u8> GetReadFactoryCalReply() const;
201
202 // Returns 20 byte reply with user calibration values
203 std::vector<u8> GetReadUserCalReply() const;
204
205 // Returns 8 byte reply
206 std::vector<u8> GetReadRepCountReply() const;
207
208 // Returns 8 byte reply
209 std::vector<u8> GetReadTotalPushCountReply() const;
210
211 // Returns 8 byte reply
212 std::vector<u8> GetResetRepCountReply() const;
213
214 // Returns 4 byte save data reply
215 std::vector<u8> GetSaveDataReply() const;
216
217 // Returns 8 byte error reply
218 std::vector<u8> GetErrorReply() const;
219
220 // Returns 8 bit redundancy check from provided data
221 u8 GetCrcValue(const std::vector<u8>& data) const;
222
223 // Converts structs to an u8 vector equivalent
224 template <typename T>
225 std::vector<u8> GetDataVector(const T& reply) const;
226
227 RingConCommands command{RingConCommands::Error};
228
229 // These counters are used in multitasking mode while the switch is sleeping
230 // Total steps taken
231 u8 total_rep_count = 0;
232 // Total times the ring was pushed
233 u8 total_push_count = 0;
234
235 const u8 device_id = 0x20;
236 const FirmwareVersion version = {
237 .sub = 0x0,
238 .main = 0x2c,
239 };
240 const FactoryCalibration factory_calibration = {
241 .os_max = idle_value + range + idle_deadzone,
242 .hk_max = idle_value - range - idle_deadzone,
243 .zero_min = idle_value - idle_deadzone,
244 .zero_max = idle_value + idle_deadzone,
245 };
246 UserCalibration user_calibration = {
247 .os_max = {.value = range, .crc = 228},
248 .hk_max = {.value = -range, .crc = 239},
249 .zero = {.value = idle_value, .crc = 225},
250 };
251
252 Core::HID::EmulatedDevices* input;
253};
254} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp
new file mode 100644
index 000000000..3175c48da
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.cpp
@@ -0,0 +1,51 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/starlink.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0x28;
11
12Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
13 : HidbusBase(service_context_) {}
14Starlink::~Starlink() = default;
15
16void Starlink::OnInit() {
17 return;
18}
19
20void Starlink::OnRelease() {
21 return;
22};
23
24void Starlink::OnUpdate() {
25 if (!is_activated) {
26 return;
27 }
28 if (!device_enabled) {
29 return;
30 }
31 if (!polling_mode_enabled || !is_transfer_memory_set) {
32 return;
33 }
34
35 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
36}
37
38u8 Starlink::GetDeviceId() const {
39 return DEVICE_ID;
40}
41
42std::vector<u8> Starlink::GetReply() const {
43 return {};
44}
45
46bool Starlink::SetCommand(const std::vector<u8>& data) {
47 LOG_ERROR(Service_HID, "Command not implemented");
48 return false;
49}
50
51} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h
new file mode 100644
index 000000000..79770b68e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.h
@@ -0,0 +1,39 @@
1// Copyright 2021 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 "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class Starlink final : public HidbusBase {
17public:
18 explicit Starlink(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~Starlink() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp
new file mode 100644
index 000000000..5474657be
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.cpp
@@ -0,0 +1,52 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/stubbed.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0xFF;
11
12HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_,
13 KernelHelpers::ServiceContext& service_context_)
14 : HidbusBase(service_context_) {}
15HidbusStubbed::~HidbusStubbed() = default;
16
17void HidbusStubbed::OnInit() {
18 return;
19}
20
21void HidbusStubbed::OnRelease() {
22 return;
23};
24
25void HidbusStubbed::OnUpdate() {
26 if (!is_activated) {
27 return;
28 }
29 if (!device_enabled) {
30 return;
31 }
32 if (!polling_mode_enabled || !is_transfer_memory_set) {
33 return;
34 }
35
36 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
37}
38
39u8 HidbusStubbed::GetDeviceId() const {
40 return DEVICE_ID;
41}
42
43std::vector<u8> HidbusStubbed::GetReply() const {
44 return {};
45}
46
47bool HidbusStubbed::SetCommand(const std::vector<u8>& data) {
48 LOG_ERROR(Service_HID, "Command not implemented");
49 return false;
50}
51
52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h
new file mode 100644
index 000000000..40acdfe8f
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.h
@@ -0,0 +1,39 @@
1// Copyright 2021 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 "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class HidbusStubbed final : public HidbusBase {
17public:
18 explicit HidbusStubbed(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~HidbusStubbed() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
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 9fc7bb1b1..42f9cf811 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"
@@ -161,7 +160,8 @@ public:
161 160
162class RelocatableObject final : public ServiceFramework<RelocatableObject> { 161class RelocatableObject final : public ServiceFramework<RelocatableObject> {
163public: 162public:
164 explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} { 163 explicit RelocatableObject(Core::System& system_)
164 : ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} {
165 // clang-format off 165 // clang-format off
166 static const FunctionInfo functions[] = { 166 static const FunctionInfo functions[] = {
167 {0, &RelocatableObject::LoadModule, "LoadModule"}, 167 {0, &RelocatableObject::LoadModule, "LoadModule"},
@@ -288,7 +288,7 @@ public:
288 } 288 }
289 289
290 bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const { 290 bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const {
291 constexpr std::size_t padding_size{4 * Kernel::PageSize}; 291 const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
292 const auto start_info{page_table.QueryInfo(start - 1)}; 292 const auto start_info{page_table.QueryInfo(start - 1)};
293 293
294 if (start_info.state != Kernel::KMemoryState::Free) { 294 if (start_info.state != Kernel::KMemoryState::Free) {
@@ -308,31 +308,69 @@ public:
308 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); 308 return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
309 } 309 }
310 310
311 VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const { 311 ResultCode GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) {
312 VAddr addr{}; 312 size = Common::AlignUp(size, Kernel::PageSize);
313 const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >> 313 size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
314 Kernel::PageBits}; 314
315 do { 315 const auto is_region_available = [&](VAddr addr) {
316 addr = page_table.GetAliasCodeRegionStart() + 316 const auto end_addr = addr + size;
317 (Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits); 317 while (addr < end_addr) {
318 } while (!page_table.IsInsideAddressSpace(addr, size) || 318 if (system.Memory().IsValidVirtualAddress(addr)) {
319 page_table.IsInsideHeapRegion(addr, size) || 319 return false;
320 page_table.IsInsideAliasRegion(addr, size)); 320 }
321 return addr; 321
322 if (!page_table.IsInsideAddressSpace(out_addr, size)) {
323 return false;
324 }
325
326 if (page_table.IsInsideHeapRegion(out_addr, size)) {
327 return false;
328 }
329
330 if (page_table.IsInsideAliasRegion(out_addr, size)) {
331 return false;
332 }
333
334 addr += Kernel::PageSize;
335 }
336 return true;
337 };
338
339 bool succeeded = false;
340 const auto map_region_end =
341 page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize();
342 while (current_map_addr < map_region_end) {
343 if (is_region_available(current_map_addr)) {
344 succeeded = true;
345 break;
346 }
347 current_map_addr += 0x100000;
348 }
349
350 if (!succeeded) {
351 UNREACHABLE_MSG("Out of address space!");
352 return Kernel::ResultOutOfMemory;
353 }
354
355 out_addr = current_map_addr;
356 current_map_addr += size;
357
358 return ResultSuccess;
322 } 359 }
323 360
324 ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress, 361 ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) {
325 u64 size) const { 362 auto& page_table{process->PageTable()};
363 VAddr addr{};
364
326 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { 365 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
327 auto& page_table{process->PageTable()}; 366 R_TRY(GetAvailableMapRegion(page_table, size, addr));
328 const VAddr addr{GetRandomMapRegion(page_table, size)};
329 const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)};
330 367
368 const ResultCode result{page_table.MapCodeMemory(addr, base_addr, size)};
331 if (result == Kernel::ResultInvalidCurrentMemory) { 369 if (result == Kernel::ResultInvalidCurrentMemory) {
332 continue; 370 continue;
333 } 371 }
334 372
335 CASCADE_CODE(result); 373 R_TRY(result);
336 374
337 if (ValidateRegionForMap(page_table, addr, size)) { 375 if (ValidateRegionForMap(page_table, addr, size)) {
338 return addr; 376 return addr;
@@ -343,7 +381,7 @@ public:
343 } 381 }
344 382
345 ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, 383 ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
346 VAddr bss_addr, std::size_t bss_size, std::size_t size) const { 384 VAddr bss_addr, std::size_t bss_size, std::size_t size) {
347 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { 385 for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
348 auto& page_table{process->PageTable()}; 386 auto& page_table{process->PageTable()};
349 VAddr addr{}; 387 VAddr addr{};
@@ -352,8 +390,12 @@ public:
352 390
353 if (bss_size) { 391 if (bss_size) {
354 auto block_guard = detail::ScopeExit([&] { 392 auto block_guard = detail::ScopeExit([&] {
355 page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size); 393 page_table.UnmapCodeMemory(
356 page_table.UnmapCodeMemory(addr, nro_addr, nro_size); 394 addr + nro_size, bss_addr, bss_size,
395 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
396 page_table.UnmapCodeMemory(
397 addr, nro_addr, nro_size,
398 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
357 }); 399 });
358 400
359 const ResultCode result{ 401 const ResultCode result{
@@ -533,17 +575,21 @@ public:
533 auto& page_table{system.CurrentProcess()->PageTable()}; 575 auto& page_table{system.CurrentProcess()->PageTable()};
534 576
535 if (info.bss_size != 0) { 577 if (info.bss_size != 0) {
536 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + 578 CASCADE_CODE(page_table.UnmapCodeMemory(
537 info.ro_size + info.data_size, 579 info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
538 info.bss_address, info.bss_size)); 580 info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
539 } 581 }
540 582
541 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size, 583 CASCADE_CODE(page_table.UnmapCodeMemory(
542 info.src_addr + info.text_size + info.ro_size, 584 info.nro_address + info.text_size + info.ro_size,
543 info.data_size)); 585 info.src_addr + info.text_size + info.ro_size, info.data_size,
544 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size, 586 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
545 info.src_addr + info.text_size, info.ro_size)); 587 CASCADE_CODE(page_table.UnmapCodeMemory(
546 CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size)); 588 info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
589 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
590 CASCADE_CODE(page_table.UnmapCodeMemory(
591 info.nro_address, info.src_addr, info.text_size,
592 Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
547 return ResultSuccess; 593 return ResultSuccess;
548 } 594 }
549 595
@@ -597,6 +643,7 @@ public:
597 LOG_WARNING(Service_LDR, "(STUBBED) called"); 643 LOG_WARNING(Service_LDR, "(STUBBED) called");
598 644
599 initialized = true; 645 initialized = true;
646 current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart();
600 647
601 IPC::ResponseBuilder rb{ctx, 2}; 648 IPC::ResponseBuilder rb{ctx, 2};
602 rb.Push(ResultSuccess); 649 rb.Push(ResultSuccess);
@@ -607,6 +654,7 @@ private:
607 654
608 std::map<VAddr, NROInfo> nro; 655 std::map<VAddr, NROInfo> nro;
609 std::map<VAddr, std::vector<SHA256Hash>> nrr; 656 std::map<VAddr, std::vector<SHA256Hash>> nrr;
657 VAddr current_map_addr{};
610 658
611 bool IsValidNROHash(const SHA256Hash& hash) const { 659 bool IsValidNROHash(const SHA256Hash& hash) const {
612 return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) { 660 return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
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..148265218 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -9,7 +9,6 @@
9#include <string> 9#include <string>
10#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/spin_lock.h"
13#include "core/hle/kernel/hle_ipc.h" 12#include "core/hle/kernel/hle_ipc.h"
14 13
15//////////////////////////////////////////////////////////////////////////////////////////////////// 14////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,8 +32,9 @@ class FileSystemController;
33} 32}
34 33
35namespace NVFlinger { 34namespace NVFlinger {
35class HosBinderDriverServer;
36class NVFlinger; 36class NVFlinger;
37} 37} // namespace NVFlinger
38 38
39namespace SM { 39namespace SM {
40class ServiceManager; 40class ServiceManager;
@@ -89,7 +89,7 @@ protected:
89 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&); 89 using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
90 90
91 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 91 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
92 [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() { 92 [[nodiscard]] std::scoped_lock<std::mutex> LockService() {
93 return std::scoped_lock{lock_service}; 93 return std::scoped_lock{lock_service};
94 } 94 }
95 95
@@ -113,7 +113,8 @@ private:
113 Kernel::HLERequestContext& ctx); 113 Kernel::HLERequestContext& ctx);
114 114
115 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_, 115 explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
116 u32 max_sessions_, InvokerFn* handler_invoker_); 116 ServiceThreadType thread_type, u32 max_sessions_,
117 InvokerFn* handler_invoker_);
117 ~ServiceFrameworkBase() override; 118 ~ServiceFrameworkBase() override;
118 119
119 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); 120 void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
@@ -133,7 +134,7 @@ private:
133 boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc; 134 boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
134 135
135 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread. 136 /// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
136 Common::SpinLock lock_service; 137 std::mutex lock_service;
137}; 138};
138 139
139/** 140/**
@@ -175,14 +176,17 @@ protected:
175 /** 176 /**
176 * Initializes the handler with no functions installed. 177 * Initializes the handler with no functions installed.
177 * 178 *
178 * @param system_ The system context to construct this service under. 179 * @param system_ The system context to construct this service under.
179 * @param service_name_ Name of the service. 180 * @param service_name_ Name of the service.
180 * @param max_sessions_ Maximum number of sessions that can be 181 * @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. 182 * it creates a new thread for it, otherwise this uses the default thread.
183 * @param max_sessions_ Maximum number of sessions that can be connected to this service at the
184 * same time.
182 */ 185 */
183 explicit ServiceFramework(Core::System& system_, const char* service_name_, 186 explicit ServiceFramework(Core::System& system_, const char* service_name_,
187 ServiceThreadType thread_type = ServiceThreadType::Default,
184 u32 max_sessions_ = ServerSessionCountMax) 188 u32 max_sessions_ = ServerSessionCountMax)
185 : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {} 189 : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {}
186 190
187 /// Registers handlers in the service. 191 /// Registers handlers in the service.
188 template <std::size_t N> 192 template <std::size_t N>
@@ -236,6 +240,7 @@ public:
236 ~Services(); 240 ~Services();
237 241
238private: 242private:
243 std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
239 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger; 244 std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
240}; 245};
241 246
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