summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt151
-rw-r--r--src/core/arm/arm_interface.cpp332
-rw-r--r--src/core/arm/arm_interface.h69
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp25
-rw-r--r--src/core/arm/cpu_interrupt_handler.h40
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp311
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h41
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp333
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h41
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp38
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h7
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.cpp9
-rw-r--r--src/core/arm/dynarmic/arm_exclusive_monitor.h9
-rw-r--r--src/core/arm/exclusive_monitor.cpp5
-rw-r--r--src/core/arm/exclusive_monitor.h7
-rw-r--r--src/core/arm/symbols.cpp130
-rw-r--r--src/core/arm/symbols.h26
-rw-r--r--src/core/constants.cpp5
-rw-r--r--src/core/constants.h5
-rw-r--r--src/core/core.cpp176
-rw-r--r--src/core/core.h79
-rw-r--r--src/core/core_timing.cpp104
-rw-r--r--src/core/core_timing.h28
-rw-r--r--src/core/core_timing_util.h5
-rw-r--r--src/core/cpu_manager.cpp320
-rw-r--r--src/core/cpu_manager.h55
-rw-r--r--src/core/crypto/aes_util.cpp5
-rw-r--r--src/core/crypto/aes_util.h5
-rw-r--r--src/core/crypto/ctr_encryption_layer.cpp5
-rw-r--r--src/core/crypto/ctr_encryption_layer.h5
-rw-r--r--src/core/crypto/encryption_layer.cpp5
-rw-r--r--src/core/crypto/encryption_layer.h5
-rw-r--r--src/core/crypto/key_manager.cpp7
-rw-r--r--src/core/crypto/key_manager.h5
-rw-r--r--src/core/crypto/partition_data_manager.cpp5
-rw-r--r--src/core/crypto/partition_data_manager.h5
-rw-r--r--src/core/crypto/sha_util.cpp5
-rw-r--r--src/core/crypto/sha_util.h5
-rw-r--r--src/core/crypto/xts_encryption_layer.cpp5
-rw-r--r--src/core/crypto/xts_encryption_layer.h5
-rw-r--r--src/core/debugger/debugger.cpp316
-rw-r--r--src/core/debugger/debugger.h52
-rw-r--r--src/core/debugger/debugger_interface.h90
-rw-r--r--src/core/debugger/gdbstub.cpp718
-rw-r--r--src/core/debugger/gdbstub.h52
-rw-r--r--src/core/debugger/gdbstub_arch.cpp487
-rw-r--r--src/core/debugger/gdbstub_arch.h68
-rw-r--r--src/core/device_memory.cpp10
-rw-r--r--src/core/device_memory.h9
-rw-r--r--src/core/file_sys/bis_factory.cpp5
-rw-r--r--src/core/file_sys/bis_factory.h5
-rw-r--r--src/core/file_sys/card_image.cpp5
-rw-r--r--src/core/file_sys/card_image.h5
-rw-r--r--src/core/file_sys/common_funcs.h5
-rw-r--r--src/core/file_sys/content_archive.cpp7
-rw-r--r--src/core/file_sys/content_archive.h5
-rw-r--r--src/core/file_sys/control_metadata.cpp5
-rw-r--r--src/core/file_sys/control_metadata.h5
-rw-r--r--src/core/file_sys/directory.h6
-rw-r--r--src/core/file_sys/errors.h23
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp25
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h25
-rw-r--r--src/core/file_sys/ips_layer.cpp12
-rw-r--r--src/core/file_sys/ips_layer.h5
-rw-r--r--src/core/file_sys/kernel_executable.cpp5
-rw-r--r--src/core/file_sys/kernel_executable.h5
-rw-r--r--src/core/file_sys/mode.h5
-rw-r--r--src/core/file_sys/nca_metadata.cpp5
-rw-r--r--src/core/file_sys/nca_metadata.h5
-rw-r--r--src/core/file_sys/nca_patch.cpp7
-rw-r--r--src/core/file_sys/nca_patch.h5
-rw-r--r--src/core/file_sys/partition_filesystem.cpp5
-rw-r--r--src/core/file_sys/partition_filesystem.h5
-rw-r--r--src/core/file_sys/patch_manager.cpp101
-rw-r--r--src/core/file_sys/patch_manager.h5
-rw-r--r--src/core/file_sys/program_metadata.cpp5
-rw-r--r--src/core/file_sys/program_metadata.h7
-rw-r--r--src/core/file_sys/registered_cache.cpp13
-rw-r--r--src/core/file_sys/registered_cache.h5
-rw-r--r--src/core/file_sys/romfs.cpp5
-rw-r--r--src/core/file_sys/romfs.h5
-rw-r--r--src/core/file_sys/romfs_factory.cpp5
-rw-r--r--src/core/file_sys/romfs_factory.h5
-rw-r--r--src/core/file_sys/savedata_factory.cpp5
-rw-r--r--src/core/file_sys/savedata_factory.h5
-rw-r--r--src/core/file_sys/sdmc_factory.cpp5
-rw-r--r--src/core/file_sys/sdmc_factory.h5
-rw-r--r--src/core/file_sys/submission_package.cpp5
-rw-r--r--src/core/file_sys/submission_package.h5
-rw-r--r--src/core/file_sys/system_archive/data/font_chinese_simplified.cpp5
-rw-r--r--src/core/file_sys/system_archive/data/font_chinese_simplified.h5
-rw-r--r--src/core/file_sys/system_archive/data/font_chinese_traditional.cpp5
-rw-r--r--src/core/file_sys/system_archive/data/font_chinese_traditional.h5
-rw-r--r--src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp5
-rw-r--r--src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h5
-rw-r--r--src/core/file_sys/system_archive/data/font_korean.cpp5
-rw-r--r--src/core/file_sys/system_archive/data/font_korean.h5
-rw-r--r--src/core/file_sys/system_archive/data/font_nintendo_extended.cpp5
-rw-r--r--src/core/file_sys/system_archive/data/font_nintendo_extended.h5
-rw-r--r--src/core/file_sys/system_archive/data/font_standard.cpp5
-rw-r--r--src/core/file_sys/system_archive/data/font_standard.h5
-rw-r--r--src/core/file_sys/system_archive/mii_model.cpp5
-rw-r--r--src/core/file_sys/system_archive/mii_model.h5
-rw-r--r--src/core/file_sys/system_archive/ng_word.cpp5
-rw-r--r--src/core/file_sys/system_archive/ng_word.h5
-rw-r--r--src/core/file_sys/system_archive/shared_font.cpp7
-rw-r--r--src/core/file_sys/system_archive/shared_font.h5
-rw-r--r--src/core/file_sys/system_archive/system_archive.cpp5
-rw-r--r--src/core/file_sys/system_archive/system_archive.h5
-rw-r--r--src/core/file_sys/system_archive/system_version.cpp5
-rw-r--r--src/core/file_sys/system_archive/system_version.h5
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.cpp5
-rw-r--r--src/core/file_sys/system_archive/time_zone_binary.h5
-rw-r--r--src/core/file_sys/vfs.cpp5
-rw-r--r--src/core/file_sys/vfs.h5
-rw-r--r--src/core/file_sys/vfs_concat.cpp5
-rw-r--r--src/core/file_sys/vfs_concat.h5
-rw-r--r--src/core/file_sys/vfs_layered.cpp5
-rw-r--r--src/core/file_sys/vfs_layered.h5
-rw-r--r--src/core/file_sys/vfs_offset.cpp5
-rw-r--r--src/core/file_sys/vfs_offset.h5
-rw-r--r--src/core/file_sys/vfs_real.cpp7
-rw-r--r--src/core/file_sys/vfs_real.h5
-rw-r--r--src/core/file_sys/vfs_static.h5
-rw-r--r--src/core/file_sys/vfs_types.h5
-rw-r--r--src/core/file_sys/vfs_vector.cpp5
-rw-r--r--src/core/file_sys/vfs_vector.h5
-rw-r--r--src/core/file_sys/xts_archive.cpp5
-rw-r--r--src/core/file_sys/xts_archive.h5
-rw-r--r--src/core/frontend/applets/controller.cpp7
-rw-r--r--src/core/frontend/applets/controller.h5
-rw-r--r--src/core/frontend/applets/error.cpp11
-rw-r--r--src/core/frontend/applets/error.h17
-rw-r--r--src/core/frontend/applets/general_frontend.cpp5
-rw-r--r--src/core/frontend/applets/general_frontend.h5
-rw-r--r--src/core/frontend/applets/mii_edit.cpp17
-rw-r--r--src/core/frontend/applets/mii_edit.h22
-rw-r--r--src/core/frontend/applets/profile_select.cpp5
-rw-r--r--src/core/frontend/applets/profile_select.h5
-rw-r--r--src/core/frontend/applets/software_keyboard.cpp5
-rw-r--r--src/core/frontend/applets/software_keyboard.h7
-rw-r--r--src/core/frontend/applets/web_browser.cpp5
-rw-r--r--src/core/frontend/applets/web_browser.h5
-rw-r--r--src/core/frontend/emu_window.cpp5
-rw-r--r--src/core/frontend/emu_window.h16
-rw-r--r--src/core/frontend/framebuffer_layout.cpp5
-rw-r--r--src/core/frontend/framebuffer_layout.h5
-rw-r--r--src/core/hardware_interrupt_manager.cpp30
-rw-r--r--src/core/hardware_interrupt_manager.h33
-rw-r--r--src/core/hardware_properties.h8
-rw-r--r--src/core/hid/emulated_console.cpp37
-rw-r--r--src/core/hid/emulated_console.h6
-rw-r--r--src/core/hid/emulated_controller.cpp625
-rw-r--r--src/core/hid/emulated_controller.h95
-rw-r--r--src/core/hid/emulated_devices.cpp83
-rw-r--r--src/core/hid/emulated_devices.h40
-rw-r--r--src/core/hid/hid_core.cpp9
-rw-r--r--src/core/hid/hid_core.h5
-rw-r--r--src/core/hid/hid_types.h134
-rw-r--r--src/core/hid/input_converter.cpp44
-rw-r--r--src/core/hid/input_converter.h21
-rw-r--r--src/core/hid/input_interpreter.cpp5
-rw-r--r--src/core/hid/input_interpreter.h5
-rw-r--r--src/core/hid/irs_types.h301
-rw-r--r--src/core/hid/motion_input.cpp5
-rw-r--r--src/core/hid/motion_input.h5
-rw-r--r--src/core/hle/api_version.h5
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/ipc_helpers.h17
-rw-r--r--src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc5
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h12
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc5
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.cpp47
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/k_system_control.h6
-rw-r--r--src/core/hle/kernel/board/nintendo/nx/secure_monitor.h5
-rw-r--r--src/core/hle/kernel/code_set.cpp5
-rw-r--r--src/core/hle/kernel/code_set.h5
-rw-r--r--src/core/hle/kernel/global_scheduler_context.cpp12
-rw-r--r--src/core/hle/kernel/global_scheduler_context.h8
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp65
-rw-r--r--src/core/hle/kernel/hle_ipc.h45
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp106
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.h10
-rw-r--r--src/core/hle/kernel/initial_process.h22
-rw-r--r--src/core/hle/kernel/k_address_arbiter.cpp32
-rw-r--r--src/core/hle/kernel/k_address_arbiter.h28
-rw-r--r--src/core/hle/kernel/k_address_space_info.cpp9
-rw-r--r--src/core/hle/kernel/k_address_space_info.h5
-rw-r--r--src/core/hle/kernel/k_affinity_mask.h8
-rw-r--r--src/core/hle/kernel/k_auto_object.cpp5
-rw-r--r--src/core/hle/kernel/k_auto_object.h23
-rw-r--r--src/core/hle/kernel/k_auto_object_container.cpp5
-rw-r--r--src/core/hle/kernel/k_auto_object_container.h5
-rw-r--r--src/core/hle/kernel/k_class_token.cpp5
-rw-r--r--src/core/hle/kernel/k_class_token.h8
-rw-r--r--src/core/hle/kernel/k_client_port.cpp9
-rw-r--r--src/core/hle/kernel/k_client_port.h9
-rw-r--r--src/core/hle/kernel/k_client_session.cpp9
-rw-r--r--src/core/hle/kernel/k_client_session.h11
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp34
-rw-r--r--src/core/hle/kernel/k_code_memory.h25
-rw-r--r--src/core/hle/kernel/k_condition_variable.cpp26
-rw-r--r--src/core/hle/kernel/k_condition_variable.h11
-rw-r--r--src/core/hle/kernel/k_event.cpp19
-rw-r--r--src/core/hle/kernel/k_event.h7
-rw-r--r--src/core/hle/kernel/k_handle_table.cpp17
-rw-r--r--src/core/hle/kernel/k_handle_table.h45
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.cpp17
-rw-r--r--src/core/hle/kernel/k_interrupt_manager.h5
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.cpp8
-rw-r--r--src/core/hle/kernel/k_light_condition_variable.h5
-rw-r--r--src/core/hle/kernel/k_light_lock.cpp8
-rw-r--r--src/core/hle/kernel/k_light_lock.h5
-rw-r--r--src/core/hle/kernel/k_linked_list.h5
-rw-r--r--src/core/hle/kernel/k_memory_block.h5
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.cpp5
-rw-r--r--src/core/hle/kernel/k_memory_block_manager.h5
-rw-r--r--src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp5
-rw-r--r--src/core/hle/kernel/k_memory_layout.cpp5
-rw-r--r--src/core/hle/kernel/k_memory_layout.h15
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp475
-rw-r--r--src/core/hle/kernel/k_memory_manager.h174
-rw-r--r--src/core/hle/kernel/k_memory_region.h5
-rw-r--r--src/core/hle/kernel/k_memory_region_type.h15
-rw-r--r--src/core/hle/kernel/k_page_bitmap.h5
-rw-r--r--src/core/hle/kernel/k_page_buffer.cpp18
-rw-r--r--src/core/hle/kernel/k_page_buffer.h27
-rw-r--r--src/core/hle/kernel/k_page_group.h (renamed from src/core/hle/kernel/k_page_linked_list.h)19
-rw-r--r--src/core/hle/kernel/k_page_heap.cpp131
-rw-r--r--src/core/hle/kernel/k_page_heap.h226
-rw-r--r--src/core/hle/kernel/k_page_table.cpp1350
-rw-r--r--src/core/hle/kernel/k_page_table.h203
-rw-r--r--src/core/hle/kernel/k_port.cpp14
-rw-r--r--src/core/hle/kernel/k_port.h7
-rw-r--r--src/core/hle/kernel/k_priority_queue.h8
-rw-r--r--src/core/hle/kernel/k_process.cpp365
-rw-r--r--src/core/hle/kernel/k_process.h97
-rw-r--r--src/core/hle/kernel/k_readable_event.cpp11
-rw-r--r--src/core/hle/kernel/k_readable_event.h11
-rw-r--r--src/core/hle/kernel/k_resource_limit.cpp26
-rw-r--r--src/core/hle/kernel/k_resource_limit.h12
-rw-r--r--src/core/hle/kernel/k_scheduler.cpp744
-rw-r--r--src/core/hle/kernel/k_scheduler.h232
-rw-r--r--src/core/hle/kernel/k_scheduler_lock.h10
-rw-r--r--src/core/hle/kernel/k_scoped_lock.h8
-rw-r--r--src/core/hle/kernel/k_scoped_resource_reservation.h8
-rw-r--r--src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h8
-rw-r--r--src/core/hle/kernel/k_server_port.cpp11
-rw-r--r--src/core/hle/kernel/k_server_port.h13
-rw-r--r--src/core/hle/kernel/k_server_session.cpp37
-rw-r--r--src/core/hle/kernel/k_server_session.h17
-rw-r--r--src/core/hle/kernel/k_session.cpp5
-rw-r--r--src/core/hle/kernel/k_session.h5
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp21
-rw-r--r--src/core/hle/kernel/k_shared_memory.h23
-rw-r--r--src/core/hle/kernel/k_shared_memory_info.h5
-rw-r--r--src/core/hle/kernel/k_slab_heap.h235
-rw-r--r--src/core/hle/kernel/k_spin_lock.cpp44
-rw-r--r--src/core/hle/kernel/k_spin_lock.h9
-rw-r--r--src/core/hle/kernel/k_synchronization_object.cpp18
-rw-r--r--src/core/hle/kernel/k_synchronization_object.h13
-rw-r--r--src/core/hle/kernel/k_system_control.h5
-rw-r--r--src/core/hle/kernel/k_thread.cpp193
-rw-r--r--src/core/hle/kernel/k_thread.h141
-rw-r--r--src/core/hle/kernel/k_thread_local_page.cpp67
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h110
-rw-r--r--src/core/hle/kernel/k_thread_queue.cpp14
-rw-r--r--src/core/hle/kernel/k_thread_queue.h14
-rw-r--r--src/core/hle/kernel/k_trace.h5
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp9
-rw-r--r--src/core/hle/kernel/k_transfer_memory.h9
-rw-r--r--src/core/hle/kernel/k_worker_task.h5
-rw-r--r--src/core/hle/kernel/k_worker_task_manager.cpp7
-rw-r--r--src/core/hle/kernel/k_worker_task_manager.h5
-rw-r--r--src/core/hle/kernel/k_writable_event.cpp9
-rw-r--r--src/core/hle/kernel/k_writable_event.h9
-rw-r--r--src/core/hle/kernel/kernel.cpp442
-rw-r--r--src/core/hle/kernel/kernel.h79
-rw-r--r--src/core/hle/kernel/memory_types.h5
-rw-r--r--src/core/hle/kernel/physical_core.cpp37
-rw-r--r--src/core/hle/kernel/physical_core.h27
-rw-r--r--src/core/hle/kernel/physical_memory.h5
-rw-r--r--src/core/hle/kernel/process_capability.cpp48
-rw-r--r--src/core/hle/kernel/process_capability.h43
-rw-r--r--src/core/hle/kernel/service_thread.cpp12
-rw-r--r--src/core/hle/kernel/service_thread.h5
-rw-r--r--src/core/hle/kernel/slab_helpers.h7
-rw-r--r--src/core/hle/kernel/svc.cpp540
-rw-r--r--src/core/hle/kernel/svc.h5
-rw-r--r--src/core/hle/kernel/svc_common.h5
-rw-r--r--src/core/hle/kernel/svc_results.h63
-rw-r--r--src/core/hle/kernel/svc_types.h7
-rw-r--r--src/core/hle/kernel/svc_wrap.h147
-rw-r--r--src/core/hle/kernel/time_manager.cpp29
-rw-r--r--src/core/hle/kernel/time_manager.h5
-rw-r--r--src/core/hle/result.h81
-rw-r--r--src/core/hle/service/acc/acc.cpp42
-rw-r--r--src/core/hle/service/acc/acc.h7
-rw-r--r--src/core/hle/service/acc/acc_aa.cpp5
-rw-r--r--src/core/hle/service/acc/acc_aa.h5
-rw-r--r--src/core/hle/service/acc/acc_su.cpp5
-rw-r--r--src/core/hle/service/acc/acc_su.h5
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp5
-rw-r--r--src/core/hle/service/acc/acc_u0.h5
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp5
-rw-r--r--src/core/hle/service/acc/acc_u1.h5
-rw-r--r--src/core/hle/service/acc/async_context.cpp5
-rw-r--r--src/core/hle/service/acc/async_context.h7
-rw-r--r--src/core/hle/service/acc/errors.h9
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp28
-rw-r--r--src/core/hle/service/acc/profile_manager.h27
-rw-r--r--src/core/hle/service/am/am.cpp63
-rw-r--r--src/core/hle/service/am/am.h57
-rw-r--r--src/core/hle/service/am/applet_ae.cpp5
-rw-r--r--src/core/hle/service/am/applet_ae.h5
-rw-r--r--src/core/hle/service/am/applet_oe.cpp5
-rw-r--r--src/core/hle/service/am/applet_oe.h5
-rw-r--r--src/core/hle/service/am/applets/applet_controller.cpp13
-rw-r--r--src/core/hle/service/am/applets/applet_controller.h9
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp25
-rw-r--r--src/core/hle/service/am/applets/applet_error.h9
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.cpp19
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.h11
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.cpp138
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit.h44
-rw-r--r--src/core/hle/service/am/applets/applet_mii_edit_types.h82
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.cpp11
-rw-r--r--src/core/hle/service/am/applets/applet_profile_select.h9
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp489
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.h44
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard_types.h77
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp21
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.h9
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser_types.h5
-rw-r--r--src/core/hle/service/am/applets/applets.cpp20
-rw-r--r--src/core/hle/service/am/applets/applets.h18
-rw-r--r--src/core/hle/service/am/idle.cpp5
-rw-r--r--src/core/hle/service/am/idle.h5
-rw-r--r--src/core/hle/service/am/omm.cpp5
-rw-r--r--src/core/hle/service/am/omm.h5
-rw-r--r--src/core/hle/service/am/spsm.cpp5
-rw-r--r--src/core/hle/service/am/spsm.h5
-rw-r--r--src/core/hle/service/am/tcap.cpp5
-rw-r--r--src/core/hle/service/am/tcap.h5
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp5
-rw-r--r--src/core/hle/service/aoc/aoc_u.h5
-rw-r--r--src/core/hle/service/apm/apm.cpp5
-rw-r--r--src/core/hle/service/apm/apm.h5
-rw-r--r--src/core/hle/service/apm/apm_controller.cpp17
-rw-r--r--src/core/hle/service/apm/apm_controller.h20
-rw-r--r--src/core/hle/service/apm/apm_interface.cpp5
-rw-r--r--src/core/hle/service/apm/apm_interface.h5
-rw-r--r--src/core/hle/service/audio/audctl.cpp5
-rw-r--r--src/core/hle/service/audio/audctl.h5
-rw-r--r--src/core/hle/service/audio/auddbg.cpp5
-rw-r--r--src/core/hle/service/audio/auddbg.h5
-rw-r--r--src/core/hle/service/audio/audin_a.cpp5
-rw-r--r--src/core/hle/service/audio/audin_a.h5
-rw-r--r--src/core/hle/service/audio/audin_u.cpp389
-rw-r--r--src/core/hle/service/audio/audin_u.h52
-rw-r--r--src/core/hle/service/audio/audio.cpp5
-rw-r--r--src/core/hle/service/audio/audio.h5
-rw-r--r--src/core/hle/service/audio/audout_a.cpp5
-rw-r--r--src/core/hle/service/audio/audout_a.h5
-rw-r--r--src/core/hle/service/audio/audout_u.cpp332
-rw-r--r--src/core/hle/service/audio/audout_u.h26
-rw-r--r--src/core/hle/service/audio/audrec_a.cpp5
-rw-r--r--src/core/hle/service/audio/audrec_a.h5
-rw-r--r--src/core/hle/service/audio/audrec_u.cpp5
-rw-r--r--src/core/hle/service/audio/audrec_u.h5
-rw-r--r--src/core/hle/service/audio/audren_a.cpp5
-rw-r--r--src/core/hle/service/audio/audren_a.h5
-rw-r--r--src/core/hle/service/audio/audren_u.cpp713
-rw-r--r--src/core/hle/service/audio/audren_u.h27
-rw-r--r--src/core/hle/service/audio/codecctl.cpp5
-rw-r--r--src/core/hle/service/audio/codecctl.h5
-rw-r--r--src/core/hle/service/audio/errors.h20
-rw-r--r--src/core/hle/service/audio/hwopus.cpp35
-rw-r--r--src/core/hle/service/audio/hwopus.h16
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp7
-rw-r--r--src/core/hle/service/bcat/backend/backend.h9
-rw-r--r--src/core/hle/service/bcat/bcat.cpp5
-rw-r--r--src/core/hle/service/bcat/bcat.h5
-rw-r--r--src/core/hle/service/bcat/bcat_module.cpp19
-rw-r--r--src/core/hle/service/bcat/bcat_module.h5
-rw-r--r--src/core/hle/service/bpc/bpc.cpp5
-rw-r--r--src/core/hle/service/bpc/bpc.h5
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp10
-rw-r--r--src/core/hle/service/btdrv/btdrv.h5
-rw-r--r--src/core/hle/service/btm/btm.cpp13
-rw-r--r--src/core/hle/service/btm/btm.h5
-rw-r--r--src/core/hle/service/caps/caps.cpp5
-rw-r--r--src/core/hle/service/caps/caps.h5
-rw-r--r--src/core/hle/service/caps/caps_a.cpp5
-rw-r--r--src/core/hle/service/caps/caps_a.h5
-rw-r--r--src/core/hle/service/caps/caps_c.cpp5
-rw-r--r--src/core/hle/service/caps/caps_c.h5
-rw-r--r--src/core/hle/service/caps/caps_sc.cpp5
-rw-r--r--src/core/hle/service/caps/caps_sc.h5
-rw-r--r--src/core/hle/service/caps/caps_ss.cpp5
-rw-r--r--src/core/hle/service/caps/caps_ss.h5
-rw-r--r--src/core/hle/service/caps/caps_su.cpp5
-rw-r--r--src/core/hle/service/caps/caps_su.h5
-rw-r--r--src/core/hle/service/caps/caps_u.cpp5
-rw-r--r--src/core/hle/service/caps/caps_u.h5
-rw-r--r--src/core/hle/service/erpt/erpt.cpp5
-rw-r--r--src/core/hle/service/erpt/erpt.h5
-rw-r--r--src/core/hle/service/es/es.cpp9
-rw-r--r--src/core/hle/service/es/es.h5
-rw-r--r--src/core/hle/service/eupld/eupld.cpp5
-rw-r--r--src/core/hle/service/eupld/eupld.h5
-rw-r--r--src/core/hle/service/fatal/fatal.cpp16
-rw-r--r--src/core/hle/service/fatal/fatal.h5
-rw-r--r--src/core/hle/service/fatal/fatal_p.cpp13
-rw-r--r--src/core/hle/service/fatal/fatal_p.h5
-rw-r--r--src/core/hle/service/fatal/fatal_u.cpp5
-rw-r--r--src/core/hle/service/fatal/fatal_u.h5
-rw-r--r--src/core/hle/service/fgm/fgm.cpp5
-rw-r--r--src/core/hle/service/fgm/fgm.h5
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp36
-rw-r--r--src/core/hle/service/filesystem/filesystem.h31
-rw-r--r--src/core/hle/service/filesystem/fsp_ldr.cpp5
-rw-r--r--src/core/hle/service/filesystem/fsp_ldr.h5
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.cpp5
-rw-r--r--src/core/hle/service/filesystem/fsp_pr.h5
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp23
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h5
-rw-r--r--src/core/hle/service/friend/errors.h7
-rw-r--r--src/core/hle/service/friend/friend.cpp5
-rw-r--r--src/core/hle/service/friend/friend.h5
-rw-r--r--src/core/hle/service/friend/friend_interface.cpp5
-rw-r--r--src/core/hle/service/friend/friend_interface.h5
-rw-r--r--src/core/hle/service/glue/arp.cpp7
-rw-r--r--src/core/hle/service/glue/arp.h5
-rw-r--r--src/core/hle/service/glue/bgtc.cpp5
-rw-r--r--src/core/hle/service/glue/bgtc.h5
-rw-r--r--src/core/hle/service/glue/ectx.cpp5
-rw-r--r--src/core/hle/service/glue/ectx.h5
-rw-r--r--src/core/hle/service/glue/errors.h13
-rw-r--r--src/core/hle/service/glue/glue.cpp5
-rw-r--r--src/core/hle/service/glue/glue.h5
-rw-r--r--src/core/hle/service/glue/glue_manager.cpp11
-rw-r--r--src/core/hle/service/glue/glue_manager.h9
-rw-r--r--src/core/hle/service/glue/notif.cpp137
-rw-r--r--src/core/hle/service/glue/notif.h53
-rw-r--r--src/core/hle/service/grc/grc.cpp5
-rw-r--r--src/core/hle/service/grc/grc.h5
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.cpp26
-rw-r--r--src/core/hle/service/hid/controllers/console_sixaxis.h17
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp5
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h13
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp24
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h37
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp41
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h53
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp24
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h36
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h29
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp1075
-rw-r--r--src/core/hle/service/hid/controllers/npad.h246
-rw-r--r--src/core/hle/service/hid/controllers/palma.cpp229
-rw-r--r--src/core/hle/service/hid/controllers/palma.h163
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp16
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h18
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp34
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h36
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp26
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h33
-rw-r--r--src/core/hle/service/hid/errors.h25
-rw-r--r--src/core/hle/service/hid/hid.cpp983
-rw-r--r--src/core/hle/service/hid/hid.h50
-rw-r--r--src/core/hle/service/hid/hidbus.cpp524
-rw-r--r--src/core/hle/service/hid/hidbus.h130
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.cpp71
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h178
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp285
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h253
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp50
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h38
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp51
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h38
-rw-r--r--src/core/hle/service/hid/irs.cpp457
-rw-r--r--src/core/hle/service/hid/irs.h80
-rw-r--r--src/core/hle/service/hid/irs_ring_lifo.h47
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp265
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h110
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.cpp150
-rw-r--r--src/core/hle/service/hid/irsensor/image_transfer_processor.h73
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.cpp27
-rw-r--r--src/core/hle/service/hid/irsensor/ir_led_processor.h47
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.cpp34
-rw-r--r--src/core/hle/service/hid/irsensor/moment_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.cpp26
-rw-r--r--src/core/hle/service/hid/irsensor/pointing_processor.h61
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.cpp67
-rw-r--r--src/core/hle/service/hid/irsensor/processor_base.h33
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp29
-rw-r--r--src/core/hle/service/hid/irsensor/tera_plugin_processor.h53
-rw-r--r--src/core/hle/service/hid/ring_lifo.h5
-rw-r--r--src/core/hle/service/hid/xcd.cpp5
-rw-r--r--src/core/hle/service/hid/xcd.h5
-rw-r--r--src/core/hle/service/jit/jit.cpp404
-rw-r--r--src/core/hle/service/jit/jit.h19
-rw-r--r--src/core/hle/service/jit/jit_context.cpp426
-rw-r--r--src/core/hle/service/jit/jit_context.h65
-rw-r--r--src/core/hle/service/kernel_helpers.cpp13
-rw-r--r--src/core/hle/service/kernel_helpers.h5
-rw-r--r--src/core/hle/service/lbl/lbl.cpp5
-rw-r--r--src/core/hle/service/lbl/lbl.h5
-rw-r--r--src/core/hle/service/ldn/errors.h13
-rw-r--r--src/core/hle/service/ldn/lan_discovery.cpp633
-rw-r--r--src/core/hle/service/ldn/lan_discovery.h134
-rw-r--r--src/core/hle/service/ldn/ldn.cpp478
-rw-r--r--src/core/hle/service/ldn/ldn.h11
-rw-r--r--src/core/hle/service/ldn/ldn_results.h27
-rw-r--r--src/core/hle/service/ldn/ldn_types.h306
-rw-r--r--src/core/hle/service/ldr/ldr.cpp154
-rw-r--r--src/core/hle/service/ldr/ldr.h5
-rw-r--r--src/core/hle/service/lm/lm.cpp5
-rw-r--r--src/core/hle/service/lm/lm.h5
-rw-r--r--src/core/hle/service/mig/mig.cpp5
-rw-r--r--src/core/hle/service/mig/mig.h5
-rw-r--r--src/core/hle/service/mii/mii.cpp39
-rw-r--r--src/core/hle/service/mii/mii.h5
-rw-r--r--src/core/hle/service/mii/mii_manager.cpp250
-rw-r--r--src/core/hle/service/mii/mii_manager.h317
-rw-r--r--src/core/hle/service/mii/raw_data.cpp21
-rw-r--r--src/core/hle/service/mii/raw_data.h7
-rw-r--r--src/core/hle/service/mii/types.h436
-rw-r--r--src/core/hle/service/mm/mm_u.cpp15
-rw-r--r--src/core/hle/service/mm/mm_u.h5
-rw-r--r--src/core/hle/service/mnpp/mnpp_app.cpp44
-rw-r--r--src/core/hle/service/mnpp/mnpp_app.h19
-rw-r--r--src/core/hle/service/ncm/ncm.cpp5
-rw-r--r--src/core/hle/service/ncm/ncm.h5
-rw-r--r--src/core/hle/service/nfc/nfc.cpp13
-rw-r--r--src/core/hle/service/nfc/nfc.h5
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.cpp388
-rw-r--r--src/core/hle/service/nfp/amiibo_crypto.h100
-rw-r--r--src/core/hle/service/nfp/nfp.cpp348
-rw-r--r--src/core/hle/service/nfp/nfp.h49
-rw-r--r--src/core/hle/service/nfp/nfp_device.cpp681
-rw-r--r--src/core/hle/service/nfp/nfp_device.h101
-rw-r--r--src/core/hle/service/nfp/nfp_result.h24
-rw-r--r--src/core/hle/service/nfp/nfp_types.h320
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp669
-rw-r--r--src/core/hle/service/nfp/nfp_user.h49
-rw-r--r--src/core/hle/service/ngct/ngct.cpp5
-rw-r--r--src/core/hle/service/ngct/ngct.h5
-rw-r--r--src/core/hle/service/nifm/nifm.cpp360
-rw-r--r--src/core/hle/service/nifm/nifm.h32
-rw-r--r--src/core/hle/service/nim/nim.cpp5
-rw-r--r--src/core/hle/service/nim/nim.h5
-rw-r--r--src/core/hle/service/npns/npns.cpp5
-rw-r--r--src/core/hle/service/npns/npns.h5
-rw-r--r--src/core/hle/service/ns/errors.h7
-rw-r--r--src/core/hle/service/ns/iplatform_service_manager.cpp (renamed from src/core/hle/service/ns/pl_u.cpp)39
-rw-r--r--src/core/hle/service/ns/iplatform_service_manager.h (renamed from src/core/hle/service/ns/pl_u.h)11
-rw-r--r--src/core/hle/service/ns/language.cpp5
-rw-r--r--src/core/hle/service/ns/language.h5
-rw-r--r--src/core/hle/service/ns/ns.cpp10
-rw-r--r--src/core/hle/service/ns/ns.h5
-rw-r--r--src/core/hle/service/ns/pdm_qry.cpp6
-rw-r--r--src/core/hle/service/ns/pdm_qry.h5
-rw-r--r--src/core/hle/service/nvdrv/core/container.cpp50
-rw-r--r--src/core/hle/service/nvdrv/core/container.h52
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.cpp272
-rw-r--r--src/core/hle/service/nvdrv/core/nvmap.h175
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.cpp121
-rw-r--r--src/core/hle/service/nvdrv/core/syncpoint_manager.h134
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp34
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h27
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp493
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h192
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp364
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h115
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp30
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h21
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp135
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h66
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp28
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h11
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp86
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h28
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp25
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h11
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp235
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h61
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h28
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp131
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h127
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.cpp46
-rw-r--r--src/core/hle/service/nvdrv/nvdrv_interface.h7
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.cpp5
-rw-r--r--src/core/hle/service/nvdrv/nvmemp.h5
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp39
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.h85
-rw-r--r--src/core/hle/service/nvflinger/binder.h43
-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.cpp213
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_consumer.h43
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_core.cpp113
-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.cpp927
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue_producer.h90
-rw-r--r--src/core/hle/service/nvflinger/buffer_slot.h38
-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.cpp182
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h38
-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/olsc/olsc.cpp5
-rw-r--r--src/core/hle/service/olsc/olsc.h5
-rw-r--r--src/core/hle/service/pcie/pcie.cpp5
-rw-r--r--src/core/hle/service/pcie/pcie.h5
-rw-r--r--src/core/hle/service/pctl/pctl.cpp5
-rw-r--r--src/core/hle/service/pctl/pctl.h5
-rw-r--r--src/core/hle/service/pctl/pctl_module.cpp13
-rw-r--r--src/core/hle/service/pctl/pctl_module.h5
-rw-r--r--src/core/hle/service/pcv/pcv.cpp98
-rw-r--r--src/core/hle/service/pcv/pcv.h96
-rw-r--r--src/core/hle/service/pm/pm.cpp17
-rw-r--r--src/core/hle/service/pm/pm.h5
-rw-r--r--src/core/hle/service/prepo/prepo.cpp5
-rw-r--r--src/core/hle/service/prepo/prepo.h5
-rw-r--r--src/core/hle/service/psc/psc.cpp5
-rw-r--r--src/core/hle/service/psc/psc.h5
-rw-r--r--src/core/hle/service/ptm/psm.cpp127
-rw-r--r--src/core/hle/service/ptm/psm.h36
-rw-r--r--src/core/hle/service/ptm/ptm.cpp18
-rw-r--r--src/core/hle/service/ptm/ptm.h18
-rw-r--r--src/core/hle/service/ptm/ts.cpp41
-rw-r--r--src/core/hle/service/ptm/ts.h25
-rw-r--r--src/core/hle/service/service.cpp35
-rw-r--r--src/core/hle/service/service.h32
-rw-r--r--src/core/hle/service/set/set.cpp7
-rw-r--r--src/core/hle/service/set/set.h5
-rw-r--r--src/core/hle/service/set/set_cal.cpp5
-rw-r--r--src/core/hle/service/set/set_cal.h5
-rw-r--r--src/core/hle/service/set/set_fd.cpp5
-rw-r--r--src/core/hle/service/set/set_fd.h5
-rw-r--r--src/core/hle/service/set/set_sys.cpp7
-rw-r--r--src/core/hle/service/set/set_sys.h5
-rw-r--r--src/core/hle/service/set/settings.cpp5
-rw-r--r--src/core/hle/service/set/settings.h5
-rw-r--r--src/core/hle/service/sm/sm.cpp35
-rw-r--r--src/core/hle/service/sm/sm.h13
-rw-r--r--src/core/hle/service/sm/sm_controller.cpp7
-rw-r--r--src/core/hle/service/sm/sm_controller.h5
-rw-r--r--src/core/hle/service/sockets/bsd.cpp84
-rw-r--r--src/core/hle/service/sockets/bsd.h20
-rw-r--r--src/core/hle/service/sockets/ethc.cpp5
-rw-r--r--src/core/hle/service/sockets/ethc.h5
-rw-r--r--src/core/hle/service/sockets/nsd.cpp5
-rw-r--r--src/core/hle/service/sockets/nsd.h5
-rw-r--r--src/core/hle/service/sockets/sfdnsres.cpp210
-rw-r--r--src/core/hle/service/sockets/sfdnsres.h6
-rw-r--r--src/core/hle/service/sockets/sockets.cpp5
-rw-r--r--src/core/hle/service/sockets/sockets.h12
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp9
-rw-r--r--src/core/hle/service/sockets/sockets_translate.h7
-rw-r--r--src/core/hle/service/spl/csrng.cpp5
-rw-r--r--src/core/hle/service/spl/csrng.h5
-rw-r--r--src/core/hle/service/spl/spl.cpp5
-rw-r--r--src/core/hle/service/spl/spl.h5
-rw-r--r--src/core/hle/service/spl/spl_module.cpp5
-rw-r--r--src/core/hle/service/spl/spl_module.h5
-rw-r--r--src/core/hle/service/spl/spl_results.h37
-rw-r--r--src/core/hle/service/spl/spl_types.h5
-rw-r--r--src/core/hle/service/ssl/ssl.cpp5
-rw-r--r--src/core/hle/service/ssl/ssl.h5
-rw-r--r--src/core/hle/service/time/clock_types.h13
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h5
-rw-r--r--src/core/hle/service/time/ephemeral_network_system_clock_core.h5
-rw-r--r--src/core/hle/service/time/errors.h25
-rw-r--r--src/core/hle/service/time/local_system_clock_context_writer.h7
-rw-r--r--src/core/hle/service/time/network_system_clock_context_writer.h7
-rw-r--r--src/core/hle/service/time/standard_local_system_clock_core.h5
-rw-r--r--src/core/hle/service/time/standard_network_system_clock_core.h5
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp5
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.h5
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.cpp31
-rw-r--r--src/core/hle/service/time/standard_user_system_clock_core.h15
-rw-r--r--src/core/hle/service/time/steady_clock_core.h5
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.cpp11
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h9
-rw-r--r--src/core/hle/service/time/system_clock_core.cpp19
-rw-r--r--src/core/hle/service/time/system_clock_core.h19
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp5
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.h5
-rw-r--r--src/core/hle/service/time/time.cpp30
-rw-r--r--src/core/hle/service/time/time.h7
-rw-r--r--src/core/hle/service/time/time_interface.cpp5
-rw-r--r--src/core/hle/service/time/time_interface.h5
-rw-r--r--src/core/hle/service/time/time_manager.cpp15
-rw-r--r--src/core/hle/service/time/time_manager.h5
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp5
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h5
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp15
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h11
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp60
-rw-r--r--src/core/hle/service/time/time_zone_manager.h25
-rw-r--r--src/core/hle/service/time/time_zone_service.cpp20
-rw-r--r--src/core/hle/service/time/time_zone_service.h5
-rw-r--r--src/core/hle/service/time/time_zone_types.h5
-rw-r--r--src/core/hle/service/usb/usb.cpp5
-rw-r--r--src/core/hle/service/usb/usb.h5
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp74
-rw-r--r--src/core/hle/service/vi/display/vi_display.h53
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.cpp11
-rw-r--r--src/core/hle/service/vi/layer/vi_layer.h62
-rw-r--r--src/core/hle/service/vi/vi.cpp737
-rw-r--r--src/core/hle/service/vi/vi.h15
-rw-r--r--src/core/hle/service/vi/vi_m.cpp14
-rw-r--r--src/core/hle/service/vi/vi_m.h12
-rw-r--r--src/core/hle/service/vi/vi_results.h13
-rw-r--r--src/core/hle/service/vi/vi_s.cpp14
-rw-r--r--src/core/hle/service/vi/vi_s.h12
-rw-r--r--src/core/hle/service/vi/vi_u.cpp14
-rw-r--r--src/core/hle/service/vi/vi_u.h12
-rw-r--r--src/core/hle/service/wlan/wlan.cpp5
-rw-r--r--src/core/hle/service/wlan/wlan.h5
-rw-r--r--src/core/internal_network/network.cpp (renamed from src/core/network/network.cpp)89
-rw-r--r--src/core/internal_network/network.h (renamed from src/core/network/network.h)48
-rw-r--r--src/core/internal_network/network_interface.cpp (renamed from src/core/network/network_interface.cpp)19
-rw-r--r--src/core/internal_network/network_interface.h (renamed from src/core/network/network_interface.h)6
-rw-r--r--src/core/internal_network/socket_proxy.cpp296
-rw-r--r--src/core/internal_network/socket_proxy.h97
-rw-r--r--src/core/internal_network/sockets.h174
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp5
-rw-r--r--src/core/loader/deconstructed_rom_directory.h5
-rw-r--r--src/core/loader/elf.cpp414
-rw-r--r--src/core/loader/elf.h36
-rw-r--r--src/core/loader/kip.cpp7
-rw-r--r--src/core/loader/kip.h5
-rw-r--r--src/core/loader/loader.cpp23
-rw-r--r--src/core/loader/loader.h6
-rw-r--r--src/core/loader/nax.cpp5
-rw-r--r--src/core/loader/nax.h5
-rw-r--r--src/core/loader/nca.cpp5
-rw-r--r--src/core/loader/nca.h5
-rw-r--r--src/core/loader/nro.cpp7
-rw-r--r--src/core/loader/nro.h5
-rw-r--r--src/core/loader/nso.cpp16
-rw-r--r--src/core/loader/nso.h5
-rw-r--r--src/core/loader/nsp.cpp5
-rw-r--r--src/core/loader/nsp.h5
-rw-r--r--src/core/loader/xci.cpp5
-rw-r--r--src/core/loader/xci.h5
-rw-r--r--src/core/memory.cpp176
-rw-r--r--src/core/memory.h47
-rw-r--r--src/core/memory/cheat_engine.cpp13
-rw-r--r--src/core/memory/cheat_engine.h5
-rw-r--r--src/core/memory/dmnt_cheat_types.h25
-rw-r--r--src/core/memory/dmnt_cheat_vm.cpp25
-rw-r--r--src/core/memory/dmnt_cheat_vm.h25
-rw-r--r--src/core/network/sockets.h94
-rw-r--r--src/core/perf_stats.cpp15
-rw-r--r--src/core/perf_stats.h5
-rw-r--r--src/core/reporter.cpp14
-rw-r--r--src/core/reporter.h11
-rw-r--r--src/core/telemetry_session.cpp5
-rw-r--r--src/core/telemetry_session.h5
-rw-r--r--src/core/tools/freezer.cpp28
-rw-r--r--src/core/tools/freezer.h5
786 files changed, 30385 insertions, 12106 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 6e8d11919..95302c419 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,18 +1,15 @@
+# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
+# SPDX-License-Identifier: GPL-2.0-or-later
+
add_library(core STATIC
arm/arm_interface.h
arm/arm_interface.cpp
- arm/cpu_interrupt_handler.cpp
- arm/cpu_interrupt_handler.h
- arm/dynarmic/arm_dynarmic_32.cpp
- arm/dynarmic/arm_dynarmic_32.h
- arm/dynarmic/arm_dynarmic_64.cpp
- arm/dynarmic/arm_dynarmic_64.h
- arm/dynarmic/arm_dynarmic_cp15.cpp
- arm/dynarmic/arm_dynarmic_cp15.h
arm/dynarmic/arm_exclusive_monitor.cpp
arm/dynarmic/arm_exclusive_monitor.h
arm/exclusive_monitor.cpp
arm/exclusive_monitor.h
+ arm/symbols.cpp
+ arm/symbols.h
constants.cpp
constants.h
core.cpp
@@ -34,6 +31,13 @@ add_library(core STATIC
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
crypto/xts_encryption_layer.h
+ debugger/debugger_interface.h
+ debugger/debugger.cpp
+ debugger/debugger.h
+ debugger/gdbstub_arch.cpp
+ debugger/gdbstub_arch.h
+ debugger/gdbstub.cpp
+ debugger/gdbstub.h
device_memory.cpp
device_memory.h
file_sys/bis_factory.cpp
@@ -122,6 +126,8 @@ add_library(core STATIC
frontend/applets/error.h
frontend/applets/general_frontend.cpp
frontend/applets/general_frontend.h
+ frontend/applets/mii_edit.cpp
+ frontend/applets/mii_edit.h
frontend/applets/profile_select.cpp
frontend/applets/profile_select.h
frontend/applets/software_keyboard.cpp
@@ -132,8 +138,6 @@ add_library(core STATIC
frontend/emu_window.h
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
- hardware_interrupt_manager.cpp
- hardware_interrupt_manager.h
hid/emulated_console.cpp
hid/emulated_console.h
hid/emulated_controller.cpp
@@ -147,11 +151,13 @@ add_library(core STATIC
hid/input_converter.h
hid/input_interpreter.cpp
hid/input_interpreter.h
+ hid/irs_types.h
hid/motion_input.cpp
hid/motion_input.h
hle/api_version.h
hle/ipc.h
hle/ipc_helpers.h
+ hle/kernel/board/nintendo/nx/k_memory_layout.h
hle/kernel/board/nintendo/nx/k_system_control.cpp
hle/kernel/board/nintendo/nx/k_system_control.h
hle/kernel/board/nintendo/nx/secure_monitor.h
@@ -164,6 +170,7 @@ add_library(core STATIC
hle/kernel/hle_ipc.h
hle/kernel/init/init_slab_setup.cpp
hle/kernel/init/init_slab_setup.h
+ hle/kernel/initial_process.h
hle/kernel/k_address_arbiter.cpp
hle/kernel/k_address_arbiter.h
hle/kernel/k_address_space_info.cpp
@@ -205,9 +212,11 @@ add_library(core STATIC
hle/kernel/k_memory_region.h
hle/kernel/k_memory_region_type.h
hle/kernel/k_page_bitmap.h
+ hle/kernel/k_page_buffer.cpp
+ hle/kernel/k_page_buffer.h
hle/kernel/k_page_heap.cpp
hle/kernel/k_page_heap.h
- hle/kernel/k_page_linked_list.h
+ hle/kernel/k_page_group.h
hle/kernel/k_page_table.cpp
hle/kernel/k_page_table.h
hle/kernel/k_port.cpp
@@ -242,6 +251,8 @@ add_library(core STATIC
hle/kernel/k_system_control.h
hle/kernel/k_thread.cpp
hle/kernel/k_thread.h
+ hle/kernel/k_thread_local_page.cpp
+ hle/kernel/k_thread_local_page.h
hle/kernel/k_thread_queue.cpp
hle/kernel/k_thread_queue.h
hle/kernel/k_trace.h
@@ -298,6 +309,9 @@ add_library(core STATIC
hle/service/am/applets/applet_error.h
hle/service/am/applets/applet_general_backend.cpp
hle/service/am/applets/applet_general_backend.h
+ hle/service/am/applets/applet_mii_edit.cpp
+ hle/service/am/applets/applet_mii_edit.h
+ hle/service/am/applets/applet_mii_edit_types.h
hle/service/am/applets/applet_profile_select.cpp
hle/service/am/applets/applet_profile_select.h
hle/service/am/applets/applet_software_keyboard.cpp
@@ -421,8 +435,11 @@ add_library(core STATIC
hle/service/grc/grc.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
+ hle/service/hid/hidbus.cpp
+ hle/service/hid/hidbus.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
+ hle/service/hid/irs_ring_lifo.h
hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
@@ -441,17 +458,48 @@ add_library(core STATIC
hle/service/hid/controllers/mouse.h
hle/service/hid/controllers/npad.cpp
hle/service/hid/controllers/npad.h
+ hle/service/hid/controllers/palma.cpp
+ hle/service/hid/controllers/palma.h
hle/service/hid/controllers/stubbed.cpp
hle/service/hid/controllers/stubbed.h
hle/service/hid/controllers/touchscreen.cpp
hle/service/hid/controllers/touchscreen.h
hle/service/hid/controllers/xpad.cpp
hle/service/hid/controllers/xpad.h
+ hle/service/hid/hidbus/hidbus_base.cpp
+ hle/service/hid/hidbus/hidbus_base.h
+ hle/service/hid/hidbus/ringcon.cpp
+ hle/service/hid/hidbus/ringcon.h
+ hle/service/hid/hidbus/starlink.cpp
+ hle/service/hid/hidbus/starlink.h
+ hle/service/hid/hidbus/stubbed.cpp
+ hle/service/hid/hidbus/stubbed.h
+ hle/service/hid/irsensor/clustering_processor.cpp
+ hle/service/hid/irsensor/clustering_processor.h
+ hle/service/hid/irsensor/image_transfer_processor.cpp
+ hle/service/hid/irsensor/image_transfer_processor.h
+ hle/service/hid/irsensor/ir_led_processor.cpp
+ hle/service/hid/irsensor/ir_led_processor.h
+ hle/service/hid/irsensor/moment_processor.cpp
+ hle/service/hid/irsensor/moment_processor.h
+ hle/service/hid/irsensor/pointing_processor.cpp
+ hle/service/hid/irsensor/pointing_processor.h
+ hle/service/hid/irsensor/processor_base.cpp
+ hle/service/hid/irsensor/processor_base.h
+ hle/service/hid/irsensor/tera_plugin_processor.cpp
+ hle/service/hid/irsensor/tera_plugin_processor.h
+ hle/service/jit/jit_context.cpp
+ hle/service/jit/jit_context.h
+ hle/service/jit/jit.cpp
+ hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
- hle/service/ldn/errors.h
+ hle/service/ldn/lan_discovery.cpp
+ hle/service/ldn/lan_discovery.h
+ hle/service/ldn/ldn_results.h
hle/service/ldn/ldn.cpp
hle/service/ldn/ldn.h
+ hle/service/ldn/ldn_types.h
hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
@@ -467,12 +515,20 @@ add_library(core STATIC
hle/service/mii/types.h
hle/service/mm/mm_u.cpp
hle/service/mm/mm_u.h
+ hle/service/mnpp/mnpp_app.cpp
+ hle/service/mnpp/mnpp_app.h
hle/service/ncm/ncm.cpp
hle/service/ncm/ncm.h
hle/service/nfc/nfc.cpp
hle/service/nfc/nfc.h
+ hle/service/nfp/amiibo_crypto.cpp
+ hle/service/nfp/amiibo_crypto.h
hle/service/nfp/nfp.cpp
hle/service/nfp/nfp.h
+ hle/service/nfp/nfp_device.cpp
+ hle/service/nfp/nfp_device.h
+ hle/service/nfp/nfp_result.h
+ hle/service/nfp/nfp_types.h
hle/service/nfp/nfp_user.cpp
hle/service/nfp/nfp_user.h
hle/service/ngct/ngct.cpp
@@ -484,14 +540,20 @@ add_library(core STATIC
hle/service/npns/npns.cpp
hle/service/npns/npns.h
hle/service/ns/errors.h
+ hle/service/ns/iplatform_service_manager.cpp
+ hle/service/ns/iplatform_service_manager.h
hle/service/ns/language.cpp
hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pdm_qry.cpp
hle/service/ns/pdm_qry.h
- hle/service/ns/pl_u.cpp
- hle/service/ns/pl_u.h
+ hle/service/nvdrv/core/container.cpp
+ hle/service/nvdrv/core/container.h
+ hle/service/nvdrv/core/nvmap.cpp
+ hle/service/nvdrv/core/nvmap.h
+ hle/service/nvdrv/core/syncpoint_manager.cpp
+ hle/service/nvdrv/core/syncpoint_manager.h
hle/service/nvdrv/devices/nvdevice.h
hle/service/nvdrv/devices/nvdisp_disp0.cpp
hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -520,12 +582,35 @@ add_library(core STATIC
hle/service/nvdrv/nvdrv_interface.h
hle/service/nvdrv/nvmemp.cpp
hle/service/nvdrv/nvmemp.h
- hle/service/nvdrv/syncpoint_manager.cpp
- hle/service/nvdrv/syncpoint_manager.h
- hle/service/nvflinger/buffer_queue.cpp
- hle/service/nvflinger/buffer_queue.h
+ hle/service/nvflinger/binder.h
+ hle/service/nvflinger/buffer_item.h
+ hle/service/nvflinger/buffer_item_consumer.cpp
+ hle/service/nvflinger/buffer_item_consumer.h
+ hle/service/nvflinger/buffer_queue_consumer.cpp
+ hle/service/nvflinger/buffer_queue_consumer.h
+ hle/service/nvflinger/buffer_queue_core.cpp
+ hle/service/nvflinger/buffer_queue_core.h
+ hle/service/nvflinger/buffer_queue_defs.h
+ hle/service/nvflinger/buffer_queue_producer.cpp
+ hle/service/nvflinger/buffer_queue_producer.h
+ hle/service/nvflinger/buffer_slot.h
+ hle/service/nvflinger/buffer_transform_flags.h
+ hle/service/nvflinger/consumer_base.cpp
+ hle/service/nvflinger/consumer_base.h
+ hle/service/nvflinger/consumer_listener.h
+ hle/service/nvflinger/graphic_buffer_producer.cpp
+ hle/service/nvflinger/graphic_buffer_producer.h
+ hle/service/nvflinger/hos_binder_driver_server.cpp
+ hle/service/nvflinger/hos_binder_driver_server.h
hle/service/nvflinger/nvflinger.cpp
hle/service/nvflinger/nvflinger.h
+ hle/service/nvflinger/parcel.h
+ hle/service/nvflinger/pixel_format.h
+ hle/service/nvflinger/producer_listener.h
+ hle/service/nvflinger/status.h
+ hle/service/nvflinger/ui/fence.h
+ hle/service/nvflinger/ui/graphic_buffer.h
+ hle/service/nvflinger/window.h
hle/service/olsc/olsc.cpp
hle/service/olsc/olsc.h
hle/service/pcie/pcie.cpp
@@ -544,6 +629,10 @@ add_library(core STATIC
hle/service/psc/psc.h
hle/service/ptm/psm.cpp
hle/service/ptm/psm.h
+ hle/service/ptm/ptm.cpp
+ hle/service/ptm/ptm.h
+ hle/service/ptm/ts.cpp
+ hle/service/ptm/ts.h
hle/service/kernel_helpers.cpp
hle/service/kernel_helpers.h
hle/service/service.cpp
@@ -634,10 +723,15 @@ add_library(core STATIC
hle/service/vi/vi_u.h
hle/service/wlan/wlan.cpp
hle/service/wlan/wlan.h
+ internal_network/network.cpp
+ internal_network/network.h
+ internal_network/network_interface.cpp
+ internal_network/network_interface.h
+ internal_network/sockets.h
+ internal_network/socket_proxy.cpp
+ internal_network/socket_proxy.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
- loader/elf.cpp
- loader/elf.h
loader/kip.cpp
loader/kip.h
loader/loader.cpp
@@ -661,11 +755,6 @@ add_library(core STATIC
memory/dmnt_cheat_vm.h
memory.cpp
memory.h
- network/network.cpp
- network/network.h
- network/network_interface.cpp
- network/network_interface.h
- network/sockets.h
perf_stats.cpp
perf_stats.h
reporter.cpp
@@ -682,16 +771,11 @@ if (MSVC)
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
- /we4456 # Declaration of 'identifier' hides previous local declaration
- /we4457 # Declaration of 'identifier' hides function parameter
- /we4458 # Declaration of 'identifier' hides class member
- /we4459 # Declaration of 'identifier' hides global declaration
)
else()
target_compile_options(core PRIVATE
-Werror=conversion
-Werror=ignored-qualifiers
- -Werror=shadow
$<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
@@ -705,8 +789,11 @@ endif()
create_target_directory_groups(core)
-target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
-target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
+target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
+target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus)
+if (MINGW)
+ target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
+endif()
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 0951e1976..953d96439 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -1,6 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#ifndef _MSC_VER
+#include <cxxabi.h>
+#endif
#include <map>
#include <optional>
@@ -8,170 +11,43 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
+#include "core/arm/symbols.h"
#include "core/core.h"
+#include "core/debugger/debugger.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/svc.h"
#include "core/loader/loader.h"
#include "core/memory.h"
-namespace Core {
-namespace {
-
-constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
-constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
-constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
-constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
-
-enum class ELFSymbolType : u8 {
- None = 0,
- Object = 1,
- Function = 2,
- Section = 3,
- File = 4,
- Common = 5,
- TLS = 6,
-};
-
-enum class ELFSymbolBinding : u8 {
- Local = 0,
- Global = 1,
- Weak = 2,
-};
-
-enum class ELFSymbolVisibility : u8 {
- Default = 0,
- Internal = 1,
- Hidden = 2,
- Protected = 3,
-};
-
-struct ELFSymbol {
- u32 name_index;
- union {
- u8 info;
-
- BitField<0, 4, ELFSymbolType> type;
- BitField<4, 4, ELFSymbolBinding> binding;
- };
- ELFSymbolVisibility visibility;
- u16 sh_index;
- u64 value;
- u64 size;
-};
-static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
-
-using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
-
-Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {
- const auto mod_offset = text_offset + memory.Read32(text_offset + 4);
-
- if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
- memory.Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
- return {};
- }
-
- const auto dynamic_offset = memory.Read32(mod_offset + 0x4) + mod_offset;
-
- VAddr string_table_offset{};
- VAddr symbol_table_offset{};
- u64 symbol_entry_size{};
-
- VAddr dynamic_index = dynamic_offset;
- while (true) {
- const u64 tag = memory.Read64(dynamic_index);
- const u64 value = memory.Read64(dynamic_index + 0x8);
- dynamic_index += 0x10;
-
- if (tag == ELF_DYNAMIC_TAG_NULL) {
- break;
- }
-
- if (tag == ELF_DYNAMIC_TAG_STRTAB) {
- string_table_offset = value;
- } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
- symbol_table_offset = value;
- } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
- symbol_entry_size = value;
- }
- }
-
- if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
- return {};
- }
-
- const auto string_table_address = text_offset + string_table_offset;
- const auto symbol_table_address = text_offset + symbol_table_offset;
-
- Symbols out;
-
- VAddr symbol_index = symbol_table_address;
- while (symbol_index < string_table_address) {
- ELFSymbol symbol{};
- memory.ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
-
- VAddr string_offset = string_table_address + symbol.name_index;
- std::string name;
- for (u8 c = memory.Read8(string_offset); c != 0; c = memory.Read8(++string_offset)) {
- name += static_cast<char>(c);
- }
-
- symbol_index += symbol_entry_size;
- out.push_back({symbol, name});
- }
-
- return out;
-}
-
-std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
- const auto iter =
- std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
- const auto& symbol = pair.first;
- const auto end_address = symbol.value + symbol.size;
- return func_address >= symbol.value && func_address < end_address;
- });
-
- if (iter == symbols.end()) {
- return std::nullopt;
- }
+#include "core/arm/dynarmic/arm_dynarmic_32.h"
+#include "core/arm/dynarmic/arm_dynarmic_64.h"
- return iter->second;
-}
-
-} // Anonymous namespace
+namespace Core {
constexpr u64 SEGMENT_BASE = 0x7100000000ull;
std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
- System& system, const ThreadContext64& ctx) {
- std::vector<BacktraceEntry> out;
- auto& memory = system.Memory();
-
- auto fp = ctx.cpu_registers[29];
- auto lr = ctx.cpu_registers[30];
- while (true) {
- out.push_back({
- .module = "",
- .address = 0,
- .original_address = lr,
- .offset = 0,
- .name = {},
- });
-
- if (fp == 0) {
- break;
- }
+ Core::System& system, const ARM_Interface::ThreadContext32& ctx) {
+ return ARM_Dynarmic_32::GetBacktraceFromContext(system, ctx);
+}
- lr = memory.Read64(fp + 8) - 4;
- fp = memory.Read64(fp);
- }
+std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContext(
+ Core::System& system, const ARM_Interface::ThreadContext64& ctx) {
+ return ARM_Dynarmic_64::GetBacktraceFromContext(system, ctx);
+}
+void ARM_Interface::SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out) {
std::map<VAddr, std::string> modules;
auto& loader{system.GetAppLoader()};
if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
- return {};
+ return;
}
- std::map<std::string, Symbols> symbols;
+ std::map<std::string, Symbols::Symbols> symbols;
for (const auto& module : modules) {
- symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
+ symbols.insert_or_assign(module.second,
+ Symbols::GetSymbols(module.first, system.Memory(),
+ system.CurrentProcess()->Is64BitProcess()));
}
for (auto& entry : out) {
@@ -188,91 +64,137 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
entry.offset = entry.original_address - base;
entry.address = SEGMENT_BASE + entry.offset;
- if (entry.module.empty())
+ if (entry.module.empty()) {
entry.module = "unknown";
+ }
const auto symbol_set = symbols.find(entry.module);
if (symbol_set != symbols.end()) {
- const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
+ const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
if (symbol.has_value()) {
+#ifdef _MSC_VER
// TODO(DarkLordZach): Add demangling of symbol names.
entry.name = *symbol;
+#else
+ int status{-1};
+ char* demangled{abi::__cxa_demangle(symbol->c_str(), nullptr, nullptr, &status)};
+ if (status == 0 && demangled != nullptr) {
+ entry.name = demangled;
+ std::free(demangled);
+ } else {
+ entry.name = *symbol;
+ }
+#endif
}
}
}
+}
+
+void ARM_Interface::LogBacktrace() const {
+ const VAddr sp = GetSP();
+ const VAddr pc = GetPC();
+ LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
+ LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
+ "Offset", "Symbol");
+ LOG_ERROR(Core_ARM, "");
- return out;
+ const auto backtrace = GetBacktrace();
+ for (const auto& entry : backtrace) {
+ LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
+ entry.original_address, entry.offset, entry.name);
+ }
}
-std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
- std::vector<BacktraceEntry> out;
- auto& memory = system.Memory();
+void ARM_Interface::Run() {
+ using Kernel::StepState;
+ using Kernel::SuspendType;
- auto fp = GetReg(29);
- auto lr = GetReg(30);
while (true) {
- out.push_back({"", 0, lr, 0, ""});
- if (!fp) {
+ Kernel::KThread* current_thread{Kernel::GetCurrentThreadPointer(system.Kernel())};
+ Dynarmic::HaltReason hr{};
+
+ // Notify the debugger and go to sleep if a step was performed
+ // and this thread has been scheduled again.
+ if (current_thread->GetStepState() == StepState::StepPerformed) {
+ system.GetDebugger().NotifyThreadStopped(current_thread);
+ current_thread->RequestSuspend(SuspendType::Debug);
break;
}
- lr = memory.Read64(fp + 8) - 4;
- fp = memory.Read64(fp);
- }
- std::map<VAddr, std::string> modules;
- auto& loader{system.GetAppLoader()};
- if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
- return {};
- }
-
- std::map<std::string, Symbols> symbols;
- for (const auto& module : modules) {
- symbols.insert_or_assign(module.second, GetSymbols(module.first, memory));
- }
+ // Otherwise, run the thread.
+ system.EnterDynarmicProfile();
+ if (current_thread->GetStepState() == StepState::StepPending) {
+ hr = StepJit();
- for (auto& entry : out) {
- VAddr base = 0;
- for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
- const auto& module{*iter};
- if (entry.original_address >= module.first) {
- entry.module = module.second;
- base = module.first;
- break;
+ if (Has(hr, step_thread)) {
+ current_thread->SetStepState(StepState::StepPerformed);
}
+ } else {
+ hr = RunJit();
+ }
+ system.ExitDynarmicProfile();
+
+ // Notify the debugger and go to sleep if a breakpoint was hit,
+ // or if the thread is unable to continue for any reason.
+ if (Has(hr, breakpoint) || Has(hr, no_execute)) {
+ RewindBreakpointInstruction();
+ if (system.DebuggerEnabled()) {
+ system.GetDebugger().NotifyThreadStopped(current_thread);
+ }
+ current_thread->RequestSuspend(Kernel::SuspendType::Debug);
+ break;
}
- entry.offset = entry.original_address - base;
- entry.address = SEGMENT_BASE + entry.offset;
-
- if (entry.module.empty())
- entry.module = "unknown";
-
- const auto symbol_set = symbols.find(entry.module);
- if (symbol_set != symbols.end()) {
- const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
- if (symbol.has_value()) {
- // TODO(DarkLordZach): Add demangling of symbol names.
- entry.name = *symbol;
+ // Notify the debugger and go to sleep if a watchpoint was hit.
+ if (Has(hr, watchpoint)) {
+ if (system.DebuggerEnabled()) {
+ system.GetDebugger().NotifyThreadWatchpoint(current_thread, *HaltedWatchpoint());
}
+ current_thread->RequestSuspend(SuspendType::Debug);
+ break;
+ }
+
+ // Handle syscalls and scheduling (this may change the current thread/core)
+ if (Has(hr, svc_call)) {
+ Kernel::Svc::Call(system, GetSvcNumber());
+ break;
+ }
+ if (Has(hr, break_loop) || !uses_wall_clock) {
+ break;
}
}
+}
- return out;
+void ARM_Interface::LoadWatchpointArray(const WatchpointArray& wp) {
+ watchpoints = &wp;
}
-void ARM_Interface::LogBacktrace() const {
- const VAddr sp = GetReg(13);
- const VAddr pc = GetPC();
- LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
- LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
- "Offset", "Symbol");
- LOG_ERROR(Core_ARM, "");
+const Kernel::DebugWatchpoint* ARM_Interface::MatchingWatchpoint(
+ VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const {
+ if (!watchpoints) {
+ return nullptr;
+ }
- const auto backtrace = GetBacktrace();
- for (const auto& entry : backtrace) {
- LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
- entry.original_address, entry.offset, entry.name);
+ const VAddr start_address{addr};
+ const VAddr end_address{addr + size};
+
+ for (size_t i = 0; i < Core::Hardware::NUM_WATCHPOINTS; i++) {
+ const auto& watch{(*watchpoints)[i]};
+
+ if (end_address <= watch.start_address) {
+ continue;
+ }
+ if (start_address >= watch.end_address) {
+ continue;
+ }
+ if ((access_type & watch.type) == Kernel::DebugWatchpointType::None) {
+ continue;
+ }
+
+ return &watch;
}
+
+ return nullptr;
}
} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c60322442..7d62d030e 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -1,11 +1,14 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
+#include <span>
#include <vector>
+
+#include <dynarmic/interface/halt_reason.h>
+
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hardware_properties.h"
@@ -16,13 +19,15 @@ struct PageTable;
namespace Kernel {
enum class VMAPermission : u8;
-}
+enum class DebugWatchpointType : u8;
+struct DebugWatchpoint;
+} // namespace Kernel
namespace Core {
class System;
class CPUInterruptHandler;
-using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
+using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
/// Generic ARMv8 CPU interface
class ARM_Interface {
@@ -30,10 +35,8 @@ public:
YUZU_NON_COPYABLE(ARM_Interface);
YUZU_NON_MOVEABLE(ARM_Interface);
- explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_)
- : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
- uses_wall_clock_} {}
+ explicit ARM_Interface(System& system_, bool uses_wall_clock_)
+ : system{system_}, uses_wall_clock{uses_wall_clock_} {}
virtual ~ARM_Interface() = default;
struct ThreadContext32 {
@@ -64,10 +67,7 @@ public:
static_assert(sizeof(ThreadContext64) == 0x320);
/// Runs the CPU until an event happens
- virtual void Run() = 0;
-
- /// Step CPU by one instruction
- virtual void Step() = 0;
+ void Run();
/// Clear all instruction cache
virtual void ClearInstructionCache() = 0;
@@ -101,6 +101,12 @@ public:
virtual u64 GetPC() const = 0;
/**
+ * Get the current Stack Pointer
+ * @return Returns current SP
+ */
+ virtual u64 GetSP() const = 0;
+
+ /**
* Get an ARM register
* @param index Register index
* @return Returns the value in the register
@@ -164,12 +170,16 @@ public:
virtual void SaveContext(ThreadContext64& ctx) = 0;
virtual void LoadContext(const ThreadContext32& ctx) = 0;
virtual void LoadContext(const ThreadContext64& ctx) = 0;
+ void LoadWatchpointArray(const WatchpointArray& wp);
/// Clears the exclusive monitor's state.
virtual void ClearExclusiveState() = 0;
- /// Prepare core for thread reschedule (if needed to correctly handle state)
- virtual void PrepareReschedule() = 0;
+ /// Signal an interrupt and ask the core to halt as soon as possible.
+ virtual void SignalInterrupt() = 0;
+
+ /// Clear a previous interrupt.
+ virtual void ClearInterrupt() = 0;
struct BacktraceEntry {
std::string module;
@@ -180,23 +190,36 @@ public:
};
static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext32& ctx);
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
const ThreadContext64& ctx);
- std::vector<BacktraceEntry> GetBacktrace() const;
+ virtual std::vector<BacktraceEntry> GetBacktrace() const = 0;
- /// fp (= r29) points to the last frame record.
- /// Note that this is the frame record for the *previous* frame, not the current one.
- /// Note we need to subtract 4 from our last read to get the proper address
- /// Frame records are two words long:
- /// fp+0 : pointer to previous frame record
- /// fp+8 : value of lr for frame
void LogBacktrace() const;
+ static constexpr Dynarmic::HaltReason step_thread = Dynarmic::HaltReason::Step;
+ static constexpr Dynarmic::HaltReason break_loop = Dynarmic::HaltReason::UserDefined2;
+ static constexpr Dynarmic::HaltReason svc_call = Dynarmic::HaltReason::UserDefined3;
+ static constexpr Dynarmic::HaltReason breakpoint = Dynarmic::HaltReason::UserDefined4;
+ static constexpr Dynarmic::HaltReason watchpoint = Dynarmic::HaltReason::MemoryAbort;
+ static constexpr Dynarmic::HaltReason no_execute = Dynarmic::HaltReason::UserDefined6;
+
protected:
/// System context that this ARM interface is running under.
System& system;
- CPUInterrupts& interrupt_handlers;
+ const WatchpointArray* watchpoints;
bool uses_wall_clock;
+
+ static void SymbolicateBacktrace(Core::System& system, std::vector<BacktraceEntry>& out);
+ const Kernel::DebugWatchpoint* MatchingWatchpoint(
+ VAddr addr, u64 size, Kernel::DebugWatchpointType access_type) const;
+
+ virtual Dynarmic::HaltReason RunJit() = 0;
+ virtual Dynarmic::HaltReason StepJit() = 0;
+ virtual u32 GetSvcNumber() const = 0;
+ virtual const Kernel::DebugWatchpoint* HaltedWatchpoint() const = 0;
+ virtual void RewindBreakpointInstruction() = 0;
};
} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
deleted file mode 100644
index 9c8898700..000000000
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/thread.h"
-#include "core/arm/cpu_interrupt_handler.h"
-
-namespace Core {
-
-CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
-
-CPUInterruptHandler::~CPUInterruptHandler() = default;
-
-void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
- if (is_interrupted_) {
- interrupt_event->Set();
- }
- is_interrupted = is_interrupted_;
-}
-
-void CPUInterruptHandler::AwaitInterrupt() {
- interrupt_event->Wait();
-}
-
-} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
deleted file mode 100644
index c20c280f1..000000000
--- a/src/core/arm/cpu_interrupt_handler.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <atomic>
-#include <memory>
-
-namespace Common {
-class Event;
-}
-
-namespace Core {
-
-class CPUInterruptHandler {
-public:
- CPUInterruptHandler();
- ~CPUInterruptHandler();
-
- CPUInterruptHandler(const CPUInterruptHandler&) = delete;
- CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
-
- CPUInterruptHandler(CPUInterruptHandler&&) = delete;
- CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
-
- bool IsInterrupted() const {
- return is_interrupted;
- }
-
- void SetInterrupt(bool is_interrupted);
-
- void AwaitInterrupt();
-
-private:
- std::unique_ptr<Common::Event> interrupt_event;
- std::atomic_bool is_interrupted{false};
-};
-
-} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index b0d89c539..d1e70f19d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <memory>
@@ -12,12 +11,13 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/debugger/debugger.h"
+#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
#include "core/memory.h"
@@ -28,69 +28,106 @@ using namespace Common::Literals;
class DynarmicCallbacks32 : public Dynarmic::A32::UserCallbacks {
public:
explicit DynarmicCallbacks32(ARM_Dynarmic_32& parent_)
- : parent{parent_}, memory(parent.system.Memory()) {}
+ : parent{parent_},
+ memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
u8 MemoryRead8(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u32 vaddr) override {
+ CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return memory.Read64(vaddr);
}
+ std::optional<u32> MemoryReadCode(u32 vaddr) override {
+ if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
+ return std::nullopt;
+ }
+ return memory.Read32(vaddr);
+ }
void MemoryWrite8(u32 vaddr, u8 value) override {
- memory.Write8(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
+ memory.Write8(vaddr, value);
+ }
}
void MemoryWrite16(u32 vaddr, u16 value) override {
- memory.Write16(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
+ memory.Write16(vaddr, value);
+ }
}
void MemoryWrite32(u32 vaddr, u32 value) override {
- memory.Write32(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
+ memory.Write32(vaddr, value);
+ }
}
void MemoryWrite64(u32 vaddr, u64 value) override {
- memory.Write64(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value);
+ }
}
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
- return memory.WriteExclusive8(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
- return memory.WriteExclusive16(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
- return memory.WriteExclusive32(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
- return memory.WriteExclusive64(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive64(vaddr, value, expected);
}
void InterpreterFallback(u32 pc, std::size_t num_instructions) override {
- UNIMPLEMENTED_MSG("This should never happen, pc = {:08X}, code = {:08X}", pc,
- MemoryReadCode(pc));
+ parent.LogBacktrace();
+ LOG_ERROR(Core_ARM,
+ "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
+ num_instructions, memory.Read32(pc));
}
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
- LOG_CRITICAL(Core_ARM,
- "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
- exception, pc, MemoryReadCode(pc), parent.IsInThumbMode());
- UNIMPLEMENTED();
+ switch (exception) {
+ case Dynarmic::A32::Exception::NoExecuteFault:
+ LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#08x}", pc);
+ ReturnException(pc, ARM_Interface::no_execute);
+ return;
+ default:
+ if (debugger_enabled) {
+ ReturnException(pc, ARM_Interface::breakpoint);
+ return;
+ }
+
+ parent.LogBacktrace();
+ LOG_CRITICAL(Core_ARM,
+ "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})",
+ exception, pc, memory.Read32(pc), parent.IsInThumbMode());
+ }
}
void CallSVC(u32 swi) override {
- parent.svc_called = true;
parent.svc_swi = swi;
- parent.jit->HaltExecution();
+ parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
}
void AddTicks(u64 ticks) override {
if (parent.uses_wall_clock) {
return;
}
+
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
// if not all cores are doing a similar amount of work. Instead of doing this, we should
@@ -107,18 +144,45 @@ public:
u64 GetTicksRemaining() override {
if (parent.uses_wall_clock) {
- if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
+ if (!IsInterrupted()) {
return minimum_run_cycles;
}
return 0U;
}
+
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
+ bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
+ if (!debugger_enabled) {
+ return true;
+ }
+
+ const auto match{parent.MatchingWatchpoint(addr, size, type)};
+ if (match) {
+ parent.halted_watchpoint = match;
+ parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
+ return false;
+ }
+
+ return true;
+ }
+
+ void ReturnException(u32 pc, Dynarmic::HaltReason hr) {
+ parent.SaveContext(parent.breakpoint_context);
+ parent.breakpoint_context.cpu_registers[15] = pc;
+ parent.jit.load()->HaltExecution(hr);
+ }
+
+ bool IsInterrupted() {
+ return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
+ }
+
ARM_Dynarmic_32& parent;
Core::Memory::Memory& memory;
std::size_t num_interpreted_instructions{};
- static constexpr u64 minimum_run_cycles = 1000U;
+ bool debugger_enabled{};
+ static constexpr u64 minimum_run_cycles = 10000U;
};
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const {
@@ -126,17 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
config.callbacks = cb.get();
config.coprocessors[15] = cp15;
config.define_unpredictable_behaviour = true;
- static constexpr std::size_t PAGE_BITS = 12;
- static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
+ static constexpr std::size_t YUZU_PAGEBITS = 12;
+ static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS);
if (page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
page_table->pointers.data());
+ config.absolute_offset_page_table = true;
+ config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
+ config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
+ config.only_detect_misalignment_via_page_table_on_page_boundary = true;
+
config.fastmem_pointer = page_table->fastmem_arena;
+
+ config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
+ config.recompile_on_exclusive_fastmem_failure = true;
}
- config.absolute_offset_page_table = true;
- config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
- config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
- config.only_detect_misalignment_via_page_table_on_page_boundary = true;
// Multi-process state
config.processor_id = core_index;
@@ -144,10 +212,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
+ config.enable_cycle_counting = true;
// Code cache size
config.code_cache_size = 512_MiB;
- config.far_code_offset = 400_MiB;
+
+ // Allow memory fault handling to work
+ if (system.DebuggerEnabled()) {
+ config.check_halt_on_memory_access = true;
+ }
+
+ // null_jit
+ if (!page_table) {
+ // Don't waste too much memory on null_jit
+ config.code_cache_size = 8_MiB;
+ }
// Safe optimizations
if (Settings::values.cpu_debug_mode) {
@@ -177,80 +256,101 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
+ config.fastmem_exclusive_access = false;
}
- }
-
- // Unsafe optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
- config.unsafe_optimizations = true;
- if (Settings::values.cpuopt_unsafe_unfuse_fma) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ if (!Settings::values.cpuopt_fastmem_exclusives) {
+ config.fastmem_exclusive_access = false;
}
- if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ if (!Settings::values.cpuopt_recompile_exclusives) {
+ config.recompile_on_exclusive_fastmem_failure = false;
}
- if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
+ } else {
+ // Unsafe optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
+ config.unsafe_optimizations = true;
+ if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ }
+ if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_standard_fpcr) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
+ }
+ if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
+ }
}
- if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
+
+ // Curated optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
+ config.unsafe_optimizations = true;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
- }
- // Curated optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
- config.unsafe_optimizations = true;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ // Paranoia mode for debugging optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
+ config.unsafe_optimizations = false;
+ config.optimizations = Dynarmic::no_optimizations;
+ }
}
return std::make_unique<Dynarmic::A32::Jit>(config);
}
-void ARM_Dynarmic_32::Run() {
- while (true) {
- jit->Run();
- if (!svc_called) {
- break;
- }
- svc_called = false;
- Kernel::Svc::Call(system, svc_swi);
- if (shutdown) {
- break;
- }
- }
+Dynarmic::HaltReason ARM_Dynarmic_32::RunJit() {
+ return jit.load()->Run();
+}
+
+Dynarmic::HaltReason ARM_Dynarmic_32::StepJit() {
+ return jit.load()->Step();
+}
+
+u32 ARM_Dynarmic_32::GetSvcNumber() const {
+ return svc_swi;
+}
+
+const Kernel::DebugWatchpoint* ARM_Dynarmic_32::HaltedWatchpoint() const {
+ return halted_watchpoint;
}
-void ARM_Dynarmic_32::Step() {
- jit->Step();
+void ARM_Dynarmic_32::RewindBreakpointInstruction() {
+ LoadContext(breakpoint_context);
}
-ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
- std::size_t core_index_)
- : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
- cb(std::make_unique<DynarmicCallbacks32>(*this)),
+ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
+ ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
+ : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
- jit(MakeJit(nullptr)) {}
+ null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
void ARM_Dynarmic_32::SetPC(u64 pc) {
- jit->Regs()[15] = static_cast<u32>(pc);
+ jit.load()->Regs()[15] = static_cast<u32>(pc);
}
u64 ARM_Dynarmic_32::GetPC() const {
- return jit->Regs()[15];
+ return jit.load()->Regs()[15];
+}
+
+u64 ARM_Dynarmic_32::GetSP() const {
+ return jit.load()->Regs()[13];
}
u64 ARM_Dynarmic_32::GetReg(int index) const {
- return jit->Regs()[index];
+ return jit.load()->Regs()[index];
}
void ARM_Dynarmic_32::SetReg(int index, u64 value) {
- jit->Regs()[index] = static_cast<u32>(value);
+ jit.load()->Regs()[index] = static_cast<u32>(value);
}
u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
@@ -260,11 +360,11 @@ u128 ARM_Dynarmic_32::GetVectorReg(int index) const {
void ARM_Dynarmic_32::SetVectorReg(int index, u128 value) {}
u32 ARM_Dynarmic_32::GetPSTATE() const {
- return jit->Cpsr();
+ return jit.load()->Cpsr();
}
void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
- jit->SetCpsr(cpsr);
+ jit.load()->SetCpsr(cpsr);
}
u64 ARM_Dynarmic_32::GetTlsAddress() const {
@@ -285,7 +385,7 @@ void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
Dynarmic::A32::Context context;
- jit->SaveContext(context);
+ jit.load()->SaveContext(context);
ctx.cpu_registers = context.Regs();
ctx.extension_registers = context.ExtRegs();
ctx.cpsr = context.Cpsr();
@@ -298,24 +398,27 @@ void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
context.ExtRegs() = ctx.extension_registers;
context.SetCpsr(ctx.cpsr);
context.SetFpscr(ctx.fpscr);
- jit->LoadContext(context);
+ jit.load()->LoadContext(context);
}
-void ARM_Dynarmic_32::PrepareReschedule() {
- jit->HaltExecution();
- shutdown = true;
+void ARM_Dynarmic_32::SignalInterrupt() {
+ jit.load()->HaltExecution(break_loop);
+}
+
+void ARM_Dynarmic_32::ClearInterrupt() {
+ jit.load()->ClearHalt(break_loop);
}
void ARM_Dynarmic_32::ClearInstructionCache() {
- jit->ClearCache();
+ jit.load()->ClearCache();
}
void ARM_Dynarmic_32::InvalidateCacheRange(VAddr addr, std::size_t size) {
- jit->InvalidateCacheRange(static_cast<u32>(addr), size);
+ jit.load()->InvalidateCacheRange(static_cast<u32>(addr), size);
}
void ARM_Dynarmic_32::ClearExclusiveState() {
- jit->ClearExclusiveState();
+ jit.load()->ClearExclusiveState();
}
void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
@@ -326,13 +429,49 @@ void ARM_Dynarmic_32::PageTableChanged(Common::PageTable& page_table,
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
auto iter = jit_cache.find(key);
if (iter != jit_cache.end()) {
- jit = iter->second;
+ jit.store(iter->second.get());
LoadContext(ctx);
return;
}
- jit = MakeJit(&page_table);
+ std::shared_ptr new_jit = MakeJit(&page_table);
+ jit.store(new_jit.get());
LoadContext(ctx);
- jit_cache.emplace(key, jit);
+ jit_cache.emplace(key, std::move(new_jit));
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace(Core::System& system,
+ u64 fp, u64 lr, u64 pc) {
+ std::vector<BacktraceEntry> out;
+ auto& memory = system.Memory();
+
+ out.push_back({"", 0, pc, 0, ""});
+
+ // fp (= r11) points to the last frame record.
+ // Frame records are two words long:
+ // fp+0 : pointer to previous frame record
+ // fp+4 : value of lr for frame
+ while (true) {
+ out.push_back({"", 0, lr, 0, ""});
+ if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
+ break;
+ }
+ lr = memory.Read32(fp + 4);
+ fp = memory.Read32(fp);
+ }
+
+ SymbolicateBacktrace(system, out);
+
+ return out;
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktraceFromContext(
+ System& system, const ThreadContext32& ctx) {
+ const auto& reg = ctx.cpu_registers;
+ return GetBacktrace(system, reg[11], reg[14], reg[15]);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_32::GetBacktrace() const {
+ return GetBacktrace(system, GetReg(11), GetReg(14), GetReg(15));
}
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 5d47b600d..d24ba2289 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -1,9 +1,9 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <atomic>
#include <memory>
#include <unordered_map>
@@ -28,20 +28,19 @@ class System;
class ARM_Dynarmic_32 final : public ARM_Interface {
public:
- ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
- ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
+ ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
+ std::size_t core_index_);
~ARM_Dynarmic_32() override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
+ u64 GetSP() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
- void Run() override;
- void Step() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
@@ -56,7 +55,8 @@ public:
void LoadContext(const ThreadContext32& ctx) override;
void LoadContext(const ThreadContext64& ctx) override {}
- void PrepareReschedule() override;
+ void SignalInterrupt() override;
+ void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -64,9 +64,23 @@ public:
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext32& ctx);
+
+ std::vector<BacktraceEntry> GetBacktrace() const override;
+
+protected:
+ Dynarmic::HaltReason RunJit() override;
+ Dynarmic::HaltReason StepJit() override;
+ u32 GetSvcNumber() const override;
+ const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
+ void RewindBreakpointInstruction() override;
+
private:
std::shared_ptr<Dynarmic::A32::Jit> MakeJit(Common::PageTable* page_table) const;
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
+
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
@@ -79,13 +93,18 @@ private:
std::shared_ptr<DynarmicCP15> cp15;
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
- std::shared_ptr<Dynarmic::A32::Jit> jit;
+
+ std::shared_ptr<Dynarmic::A32::Jit> null_jit;
+
+ // A raw pointer here is fine; we never delete Jit instances.
+ std::atomic<Dynarmic::A32::Jit*> jit;
// SVC callback
u32 svc_swi{};
- bool svc_called{};
- bool shutdown{};
+ // Watchpoint info
+ const Kernel::DebugWatchpoint* halted_watchpoint;
+ ThreadContext32 breakpoint_context;
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 56836bd05..1d46f6d40 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <memory>
@@ -11,11 +10,11 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/debugger/debugger.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc.h"
@@ -29,61 +28,89 @@ using namespace Common::Literals;
class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
public:
explicit DynarmicCallbacks64(ARM_Dynarmic_64& parent_)
- : parent{parent_}, memory(parent.system.Memory()) {}
+ : parent{parent_},
+ memory(parent.system.Memory()), debugger_enabled{parent.system.DebuggerEnabled()} {}
u8 MemoryRead8(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Read);
return memory.Read8(vaddr);
}
u16 MemoryRead16(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Read);
return memory.Read16(vaddr);
}
u32 MemoryRead32(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Read);
return memory.Read32(vaddr);
}
u64 MemoryRead64(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Read);
return memory.Read64(vaddr);
}
Vector MemoryRead128(u64 vaddr) override {
+ CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Read);
return {memory.Read64(vaddr), memory.Read64(vaddr + 8)};
}
+ std::optional<u32> MemoryReadCode(u64 vaddr) override {
+ if (!memory.IsValidVirtualAddressRange(vaddr, sizeof(u32))) {
+ return std::nullopt;
+ }
+ return memory.Read32(vaddr);
+ }
void MemoryWrite8(u64 vaddr, u8 value) override {
- memory.Write8(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write)) {
+ memory.Write8(vaddr, value);
+ }
}
void MemoryWrite16(u64 vaddr, u16 value) override {
- memory.Write16(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write)) {
+ memory.Write16(vaddr, value);
+ }
}
void MemoryWrite32(u64 vaddr, u32 value) override {
- memory.Write32(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write)) {
+ memory.Write32(vaddr, value);
+ }
}
void MemoryWrite64(u64 vaddr, u64 value) override {
- memory.Write64(vaddr, value);
+ if (CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value);
+ }
}
void MemoryWrite128(u64 vaddr, Vector value) override {
- memory.Write64(vaddr, value[0]);
- memory.Write64(vaddr + 8, value[1]);
+ if (CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write)) {
+ memory.Write64(vaddr, value[0]);
+ memory.Write64(vaddr + 8, value[1]);
+ }
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, std::uint8_t expected) override {
- return memory.WriteExclusive8(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 1, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive8(vaddr, value, expected);
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, std::uint16_t expected) override {
- return memory.WriteExclusive16(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 2, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive16(vaddr, value, expected);
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, std::uint32_t expected) override {
- return memory.WriteExclusive32(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 4, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive32(vaddr, value, expected);
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, std::uint64_t expected) override {
- return memory.WriteExclusive64(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 8, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive64(vaddr, value, expected);
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, Vector expected) override {
- return memory.WriteExclusive128(vaddr, value, expected);
+ return CheckMemoryAccess(vaddr, 16, Kernel::DebugWatchpointType::Write) &&
+ memory.WriteExclusive128(vaddr, value, expected);
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
+ parent.LogBacktrace();
LOG_ERROR(Core_ARM,
"Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
- num_instructions, MemoryReadCode(pc));
+ num_instructions, memory.Read32(pc));
}
void InstructionCacheOperationRaised(Dynarmic::A64::InstructionCacheOperation op,
@@ -93,17 +120,19 @@ public:
static constexpr u64 ICACHE_LINE_SIZE = 64;
const u64 cache_line_start = value & ~(ICACHE_LINE_SIZE - 1);
- parent.InvalidateCacheRange(cache_line_start, ICACHE_LINE_SIZE);
+ parent.system.InvalidateCpuInstructionCacheRange(cache_line_start, ICACHE_LINE_SIZE);
break;
}
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoU:
- parent.ClearInstructionCache();
+ parent.system.InvalidateCpuInstructionCaches();
break;
case Dynarmic::A64::InstructionCacheOperation::InvalidateAllToPoUInnerSharable:
default:
LOG_DEBUG(Core_ARM, "Unprocesseed instruction cache operation: {}", op);
break;
}
+
+ parent.jit.load()->HaltExecution(Dynarmic::HaltReason::CacheInvalidation);
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -114,17 +143,25 @@ public:
case Dynarmic::A64::Exception::SendEventLocal:
case Dynarmic::A64::Exception::Yield:
return;
- case Dynarmic::A64::Exception::Breakpoint:
+ case Dynarmic::A64::Exception::NoExecuteFault:
+ LOG_CRITICAL(Core_ARM, "Cannot execute instruction at unmapped address {:#016x}", pc);
+ ReturnException(pc, ARM_Interface::no_execute);
+ return;
default:
- ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
- static_cast<std::size_t>(exception), pc, MemoryReadCode(pc));
+ if (debugger_enabled) {
+ ReturnException(pc, ARM_Interface::breakpoint);
+ return;
+ }
+
+ parent.LogBacktrace();
+ LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})",
+ static_cast<std::size_t>(exception), pc, memory.Read32(pc));
}
}
void CallSVC(u32 swi) override {
- parent.svc_called = true;
parent.svc_swi = swi;
- parent.jit->HaltExecution();
+ parent.jit.load()->HaltExecution(ARM_Interface::svc_call);
}
void AddTicks(u64 ticks) override {
@@ -146,11 +183,12 @@ public:
u64 GetTicksRemaining() override {
if (parent.uses_wall_clock) {
- if (!parent.interrupt_handlers[parent.core_index].IsInterrupted()) {
+ if (!IsInterrupted()) {
return minimum_run_cycles;
}
return 0U;
}
+
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -158,11 +196,37 @@ public:
return parent.system.CoreTiming().GetClockTicks();
}
+ bool CheckMemoryAccess(VAddr addr, u64 size, Kernel::DebugWatchpointType type) {
+ if (!debugger_enabled) {
+ return true;
+ }
+
+ const auto match{parent.MatchingWatchpoint(addr, size, type)};
+ if (match) {
+ parent.halted_watchpoint = match;
+ parent.jit.load()->HaltExecution(ARM_Interface::watchpoint);
+ return false;
+ }
+
+ return true;
+ }
+
+ void ReturnException(u64 pc, Dynarmic::HaltReason hr) {
+ parent.SaveContext(parent.breakpoint_context);
+ parent.breakpoint_context.pc = pc;
+ parent.jit.load()->HaltExecution(hr);
+ }
+
+ bool IsInterrupted() {
+ return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
+ }
+
ARM_Dynarmic_64& parent;
Core::Memory::Memory& memory;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
- static constexpr u64 minimum_run_cycles = 1000U;
+ bool debugger_enabled{};
+ static constexpr u64 minimum_run_cycles = 10000U;
};
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table,
@@ -185,6 +249,9 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
config.fastmem_pointer = page_table->fastmem_arena;
config.fastmem_address_space_bits = address_space_bits;
config.silently_mirror_fastmem = false;
+
+ config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
+ config.recompile_on_exclusive_fastmem_failure = true;
}
// Multi-process state
@@ -203,10 +270,21 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
+ config.enable_cycle_counting = true;
// Code cache size
config.code_cache_size = 512_MiB;
- config.far_code_offset = 400_MiB;
+
+ // Allow memory fault handling to work
+ if (system.DebuggerEnabled()) {
+ config.check_halt_on_memory_access = true;
+ }
+
+ // null_jit
+ if (!page_table) {
+ // Don't waste too much memory on null_jit
+ config.code_cache_size = 8_MiB;
+ }
// Safe optimizations
if (Settings::values.cpu_debug_mode) {
@@ -236,95 +314,117 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
+ config.fastmem_exclusive_access = false;
}
- }
-
- // Unsafe optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
- config.unsafe_optimizations = true;
- if (Settings::values.cpuopt_unsafe_unfuse_fma) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ if (!Settings::values.cpuopt_fastmem_exclusives) {
+ config.fastmem_exclusive_access = false;
}
- if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ if (!Settings::values.cpuopt_recompile_exclusives) {
+ config.recompile_on_exclusive_fastmem_failure = false;
}
- if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ } else {
+ // Unsafe optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Unsafe) {
+ config.unsafe_optimizations = true;
+ if (Settings::values.cpuopt_unsafe_unfuse_fma) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ }
+ if (Settings::values.cpuopt_unsafe_reduce_fp_error) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP;
+ }
+ if (Settings::values.cpuopt_unsafe_inaccurate_nan) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
+ }
+ if (Settings::values.cpuopt_unsafe_fastmem_check) {
+ config.fastmem_address_space_bits = 64;
+ }
+ if (Settings::values.cpuopt_unsafe_ignore_global_monitor) {
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
+ }
}
- if (Settings::values.cpuopt_unsafe_fastmem_check) {
+
+ // Curated optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
+ config.unsafe_optimizations = true;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.fastmem_address_space_bits = 64;
+ config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
}
- }
- // Curated optimizations
- if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Auto) {
- config.unsafe_optimizations = true;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
- config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
- config.fastmem_address_space_bits = 64;
+ // Paranoia mode for debugging optimizations
+ if (Settings::values.cpu_accuracy.GetValue() == Settings::CPUAccuracy::Paranoid) {
+ config.unsafe_optimizations = false;
+ config.optimizations = Dynarmic::no_optimizations;
+ }
}
return std::make_shared<Dynarmic::A64::Jit>(config);
}
-void ARM_Dynarmic_64::Run() {
- while (true) {
- jit->Run();
- if (!svc_called) {
- break;
- }
- svc_called = false;
- Kernel::Svc::Call(system, svc_swi);
- if (shutdown) {
- break;
- }
- }
+Dynarmic::HaltReason ARM_Dynarmic_64::RunJit() {
+ return jit.load()->Run();
+}
+
+Dynarmic::HaltReason ARM_Dynarmic_64::StepJit() {
+ return jit.load()->Step();
+}
+
+u32 ARM_Dynarmic_64::GetSvcNumber() const {
+ return svc_swi;
+}
+
+const Kernel::DebugWatchpoint* ARM_Dynarmic_64::HaltedWatchpoint() const {
+ return halted_watchpoint;
}
-void ARM_Dynarmic_64::Step() {
- jit->Step();
+void ARM_Dynarmic_64::RewindBreakpointInstruction() {
+ LoadContext(breakpoint_context);
}
-ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
- std::size_t core_index_)
- : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
+ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
+ ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
+ : ARM_Interface{system_, uses_wall_clock_},
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
- jit(MakeJit(nullptr, 48)) {}
+ null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
void ARM_Dynarmic_64::SetPC(u64 pc) {
- jit->SetPC(pc);
+ jit.load()->SetPC(pc);
}
u64 ARM_Dynarmic_64::GetPC() const {
- return jit->GetPC();
+ return jit.load()->GetPC();
+}
+
+u64 ARM_Dynarmic_64::GetSP() const {
+ return jit.load()->GetSP();
}
u64 ARM_Dynarmic_64::GetReg(int index) const {
- return jit->GetRegister(index);
+ return jit.load()->GetRegister(index);
}
void ARM_Dynarmic_64::SetReg(int index, u64 value) {
- jit->SetRegister(index, value);
+ jit.load()->SetRegister(index, value);
}
u128 ARM_Dynarmic_64::GetVectorReg(int index) const {
- return jit->GetVector(index);
+ return jit.load()->GetVector(index);
}
void ARM_Dynarmic_64::SetVectorReg(int index, u128 value) {
- jit->SetVector(index, value);
+ jit.load()->SetVector(index, value);
}
u32 ARM_Dynarmic_64::GetPSTATE() const {
- return jit->GetPstate();
+ return jit.load()->GetPstate();
}
void ARM_Dynarmic_64::SetPSTATE(u32 pstate) {
- jit->SetPstate(pstate);
+ jit.load()->SetPstate(pstate);
}
u64 ARM_Dynarmic_64::GetTlsAddress() const {
@@ -344,42 +444,47 @@ void ARM_Dynarmic_64::SetTPIDR_EL0(u64 value) {
}
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
- ctx.cpu_registers = jit->GetRegisters();
- ctx.sp = jit->GetSP();
- ctx.pc = jit->GetPC();
- ctx.pstate = jit->GetPstate();
- ctx.vector_registers = jit->GetVectors();
- ctx.fpcr = jit->GetFpcr();
- ctx.fpsr = jit->GetFpsr();
+ Dynarmic::A64::Jit* j = jit.load();
+ ctx.cpu_registers = j->GetRegisters();
+ ctx.sp = j->GetSP();
+ ctx.pc = j->GetPC();
+ ctx.pstate = j->GetPstate();
+ ctx.vector_registers = j->GetVectors();
+ ctx.fpcr = j->GetFpcr();
+ ctx.fpsr = j->GetFpsr();
ctx.tpidr = cb->tpidr_el0;
}
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
- jit->SetRegisters(ctx.cpu_registers);
- jit->SetSP(ctx.sp);
- jit->SetPC(ctx.pc);
- jit->SetPstate(ctx.pstate);
- jit->SetVectors(ctx.vector_registers);
- jit->SetFpcr(ctx.fpcr);
- jit->SetFpsr(ctx.fpsr);
+ Dynarmic::A64::Jit* j = jit.load();
+ j->SetRegisters(ctx.cpu_registers);
+ j->SetSP(ctx.sp);
+ j->SetPC(ctx.pc);
+ j->SetPstate(ctx.pstate);
+ j->SetVectors(ctx.vector_registers);
+ j->SetFpcr(ctx.fpcr);
+ j->SetFpsr(ctx.fpsr);
SetTPIDR_EL0(ctx.tpidr);
}
-void ARM_Dynarmic_64::PrepareReschedule() {
- jit->HaltExecution();
- shutdown = true;
+void ARM_Dynarmic_64::SignalInterrupt() {
+ jit.load()->HaltExecution(break_loop);
+}
+
+void ARM_Dynarmic_64::ClearInterrupt() {
+ jit.load()->ClearHalt(break_loop);
}
void ARM_Dynarmic_64::ClearInstructionCache() {
- jit->ClearCache();
+ jit.load()->ClearCache();
}
void ARM_Dynarmic_64::InvalidateCacheRange(VAddr addr, std::size_t size) {
- jit->InvalidateCacheRange(addr, size);
+ jit.load()->InvalidateCacheRange(addr, size);
}
void ARM_Dynarmic_64::ClearExclusiveState() {
- jit->ClearExclusiveState();
+ jit.load()->ClearExclusiveState();
}
void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
@@ -390,13 +495,49 @@ void ARM_Dynarmic_64::PageTableChanged(Common::PageTable& page_table,
auto key = std::make_pair(&page_table, new_address_space_size_in_bits);
auto iter = jit_cache.find(key);
if (iter != jit_cache.end()) {
- jit = iter->second;
+ jit.store(iter->second.get());
LoadContext(ctx);
return;
}
- jit = MakeJit(&page_table, new_address_space_size_in_bits);
+ std::shared_ptr new_jit = MakeJit(&page_table, new_address_space_size_in_bits);
+ jit.store(new_jit.get());
LoadContext(ctx);
- jit_cache.emplace(key, jit);
+ jit_cache.emplace(key, std::move(new_jit));
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace(Core::System& system,
+ u64 fp, u64 lr, u64 pc) {
+ std::vector<BacktraceEntry> out;
+ auto& memory = system.Memory();
+
+ out.push_back({"", 0, pc, 0, ""});
+
+ // fp (= x29) points to the previous frame record.
+ // Frame records are two words long:
+ // fp+0 : pointer to previous frame record
+ // fp+8 : value of lr for frame
+ while (true) {
+ out.push_back({"", 0, lr, 0, ""});
+ if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
+ break;
+ }
+ lr = memory.Read64(fp + 8);
+ fp = memory.Read64(fp);
+ }
+
+ SymbolicateBacktrace(system, out);
+
+ return out;
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktraceFromContext(
+ System& system, const ThreadContext64& ctx) {
+ const auto& reg = ctx.cpu_registers;
+ return GetBacktrace(system, reg[29], reg[30], ctx.pc);
+}
+
+std::vector<ARM_Interface::BacktraceEntry> ARM_Dynarmic_64::GetBacktrace() const {
+ return GetBacktrace(system, GetReg(29), GetReg(30), GetPC());
}
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 0c4e46c64..ed1a5eb96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -1,9 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <atomic>
#include <memory>
#include <unordered_map>
@@ -20,26 +20,24 @@ class Memory;
namespace Core {
class DynarmicCallbacks64;
-class CPUInterruptHandler;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic_64 final : public ARM_Interface {
public:
- ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
- ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
+ ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
+ std::size_t core_index_);
~ARM_Dynarmic_64() override;
void SetPC(u64 pc) override;
u64 GetPC() const override;
+ u64 GetSP() const override;
u64 GetReg(int index) const override;
void SetReg(int index, u64 value) override;
u128 GetVectorReg(int index) const override;
void SetVectorReg(int index, u128 value) override;
u32 GetPSTATE() const override;
void SetPSTATE(u32 pstate) override;
- void Run() override;
- void Step() override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
@@ -50,7 +48,8 @@ public:
void LoadContext(const ThreadContext32& ctx) override {}
void LoadContext(const ThreadContext64& ctx) override;
- void PrepareReschedule() override;
+ void SignalInterrupt() override;
+ void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
@@ -58,10 +57,24 @@ public:
void PageTableChanged(Common::PageTable& new_page_table,
std::size_t new_address_space_size_in_bits) override;
+ static std::vector<BacktraceEntry> GetBacktraceFromContext(System& system,
+ const ThreadContext64& ctx);
+
+ std::vector<BacktraceEntry> GetBacktrace() const override;
+
+protected:
+ Dynarmic::HaltReason RunJit() override;
+ Dynarmic::HaltReason StepJit() override;
+ u32 GetSvcNumber() const override;
+ const Kernel::DebugWatchpoint* HaltedWatchpoint() const override;
+ void RewindBreakpointInstruction() override;
+
private:
std::shared_ptr<Dynarmic::A64::Jit> MakeJit(Common::PageTable* page_table,
std::size_t address_space_bits) const;
+ static std::vector<BacktraceEntry> GetBacktrace(Core::System& system, u64 fp, u64 lr, u64 pc);
+
using JitCacheKey = std::pair<Common::PageTable*, std::size_t>;
using JitCacheType =
std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A64::Jit>, Common::PairHash>;
@@ -73,13 +86,17 @@ private:
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
- std::shared_ptr<Dynarmic::A64::Jit> jit;
+ std::shared_ptr<Dynarmic::A64::Jit> null_jit;
+
+ // A raw pointer here is fine; we never delete Jit instances.
+ std::atomic<Dynarmic::A64::Jit*> jit;
// SVC callback
u32 svc_swi{};
- bool svc_called{};
- bool shutdown{};
+ // Breakpoint info
+ const Kernel::DebugWatchpoint* halted_watchpoint;
+ ThreadContext64 breakpoint_context;
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index a043e6735..200efe4db 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/logging/log.h"
@@ -9,6 +8,10 @@
#include "core/core.h"
#include "core/core_timing.h"
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
using Callback = Dynarmic::A32::Coprocessor::Callback;
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
@@ -20,7 +23,7 @@ struct fmt::formatter<Dynarmic::A32::CoprocReg> {
}
template <typename FormatContext>
auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
- return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
+ return fmt::format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
}
};
@@ -48,12 +51,31 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
switch (opc2) {
case 4:
// CP15_DATA_SYNC_BARRIER
- // This is a dummy write, we ignore the value written here.
- return &dummy_value;
+ return Callback{
+ [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#ifdef _MSC_VER
+ _mm_mfence();
+ _mm_lfence();
+#else
+ asm volatile("mfence\n\tlfence\n\t" : : : "memory");
+#endif
+ return 0;
+ },
+ std::nullopt,
+ };
case 5:
// CP15_DATA_MEMORY_BARRIER
- // This is a dummy write, we ignore the value written here.
- return &dummy_value;
+ return Callback{
+ [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#ifdef _MSC_VER
+ _mm_mfence();
+#else
+ asm volatile("mfence\n\t" : : : "memory");
+#endif
+ return 0;
+ },
+ std::nullopt,
+ };
}
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index f271b2070..d90b3e568 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -36,6 +35,8 @@ public:
ARM_Dynarmic_32& parent;
u32 uprw = 0;
u32 uro = 0;
+
+ friend class ARM_Dynarmic_32;
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
index 397d054a8..fa0c48b25 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/memory.h"
@@ -37,8 +36,8 @@ u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr ad
});
}
-void DynarmicExclusiveMonitor::ClearExclusive() {
- monitor.Clear();
+void DynarmicExclusiveMonitor::ClearExclusive(std::size_t core_index) {
+ monitor.ClearProcessor(core_index);
}
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
diff --git a/src/core/arm/dynarmic/arm_exclusive_monitor.h b/src/core/arm/dynarmic/arm_exclusive_monitor.h
index 265c4ecef..57e6dd0d0 100644
--- a/src/core/arm/dynarmic/arm_exclusive_monitor.h
+++ b/src/core/arm/dynarmic/arm_exclusive_monitor.h
@@ -1,11 +1,8 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <unordered_map>
-
#include <dynarmic/interface/exclusive_monitor.h>
#include "common/common_types.h"
@@ -29,7 +26,7 @@ public:
u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override;
u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override;
u128 ExclusiveRead128(std::size_t core_index, VAddr addr) override;
- void ClearExclusive() override;
+ void ClearExclusive(std::size_t core_index) override;
bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp
index d8cba369d..2db0b035d 100644
--- a/src/core/arm/exclusive_monitor.cpp
+++ b/src/core/arm/exclusive_monitor.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#ifdef ARCHITECTURE_x86_64
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h
index 62f6e6023..15d3c96c1 100644
--- a/src/core/arm/exclusive_monitor.h
+++ b/src/core/arm/exclusive_monitor.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -23,7 +22,7 @@ public:
virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0;
virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0;
virtual u128 ExclusiveRead128(std::size_t core_index, VAddr addr) = 0;
- virtual void ClearExclusive() = 0;
+ virtual void ClearExclusive(std::size_t core_index) = 0;
virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0;
virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0;
diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp
new file mode 100644
index 000000000..0259c7ea2
--- /dev/null
+++ b/src/core/arm/symbols.cpp
@@ -0,0 +1,130 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/elf.h"
+#include "core/arm/symbols.h"
+#include "core/core.h"
+#include "core/memory.h"
+
+using namespace Common::ELF;
+
+namespace Core {
+namespace Symbols {
+
+template <typename Word, typename ELFSymbol, typename ByteReader>
+static Symbols GetSymbols(ByteReader ReadBytes) {
+ const auto Read8{[&](u64 index) {
+ u8 ret;
+ ReadBytes(&ret, index, sizeof(u8));
+ return ret;
+ }};
+
+ const auto Read32{[&](u64 index) {
+ u32 ret;
+ ReadBytes(&ret, index, sizeof(u32));
+ return ret;
+ }};
+
+ const auto ReadWord{[&](u64 index) {
+ Word ret;
+ ReadBytes(&ret, index, sizeof(Word));
+ return ret;
+ }};
+
+ const u32 mod_offset = Read32(4);
+
+ if (Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
+ return {};
+ }
+
+ VAddr string_table_offset{};
+ VAddr symbol_table_offset{};
+ u64 symbol_entry_size{};
+
+ const auto dynamic_offset = Read32(mod_offset + 0x4) + mod_offset;
+
+ VAddr dynamic_index = dynamic_offset;
+ while (true) {
+ const Word tag = ReadWord(dynamic_index);
+ const Word value = ReadWord(dynamic_index + sizeof(Word));
+ dynamic_index += 2 * sizeof(Word);
+
+ if (tag == ElfDtNull) {
+ break;
+ }
+
+ if (tag == ElfDtStrtab) {
+ string_table_offset = value;
+ } else if (tag == ElfDtSymtab) {
+ symbol_table_offset = value;
+ } else if (tag == ElfDtSyment) {
+ symbol_entry_size = value;
+ }
+ }
+
+ if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
+ return {};
+ }
+
+ Symbols out;
+
+ VAddr symbol_index = symbol_table_offset;
+ while (symbol_index < string_table_offset) {
+ ELFSymbol symbol{};
+ ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol));
+
+ VAddr string_offset = string_table_offset + symbol.st_name;
+ std::string name;
+ for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) {
+ name += static_cast<char>(c);
+ }
+
+ symbol_index += symbol_entry_size;
+ out[name] = std::make_pair(symbol.st_value, symbol.st_size);
+ }
+
+ return out;
+}
+
+Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) {
+ const auto ReadBytes{
+ [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }};
+
+ if (is_64) {
+ return GetSymbols<u64, Elf64_Sym>(ReadBytes);
+ } else {
+ return GetSymbols<u32, Elf32_Sym>(ReadBytes);
+ }
+}
+
+Symbols GetSymbols(std::span<const u8> data, bool is_64) {
+ const auto ReadBytes{[&](void* ptr, size_t offset, size_t size) {
+ std::memcpy(ptr, data.data() + offset, size);
+ }};
+
+ if (is_64) {
+ return GetSymbols<u64, Elf64_Sym>(ReadBytes);
+ } else {
+ return GetSymbols<u32, Elf32_Sym>(ReadBytes);
+ }
+}
+
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr) {
+ const auto iter = std::find_if(symbols.cbegin(), symbols.cend(), [addr](const auto& pair) {
+ const auto& [name, sym_info] = pair;
+ const auto& [start_address, size] = sym_info;
+ const auto end_address = start_address + size;
+ return addr >= start_address && addr < end_address;
+ });
+
+ if (iter == symbols.cend()) {
+ return std::nullopt;
+ }
+
+ return iter->first;
+}
+
+} // namespace Symbols
+} // namespace Core
diff --git a/src/core/arm/symbols.h b/src/core/arm/symbols.h
new file mode 100644
index 000000000..42631b09a
--- /dev/null
+++ b/src/core/arm/symbols.h
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+#include <optional>
+#include <span>
+#include <string>
+#include <utility>
+
+#include "common/common_types.h"
+
+namespace Core::Memory {
+class Memory;
+} // namespace Core::Memory
+
+namespace Core::Symbols {
+
+using Symbols = std::map<std::string, std::pair<VAddr, std::size_t>, std::less<>>;
+
+Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64 = true);
+Symbols GetSymbols(std::span<const u8> data, bool is_64 = true);
+std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr addr);
+
+} // namespace Core::Symbols
diff --git a/src/core/constants.cpp b/src/core/constants.cpp
index dccb3e03c..4430173ef 100644
--- a/src/core/constants.cpp
+++ b/src/core/constants.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/constants.h"
diff --git a/src/core/constants.h b/src/core/constants.h
index 81c5cb279..f916ce0b6 100644
--- a/src/core/constants.h
+++ b/src/core/constants.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3f9a7f44b..1deeee154 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <atomic>
@@ -8,6 +7,7 @@
#include <memory>
#include <utility>
+#include "audio_core/audio_core.h"
#include "common/fs/fs.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -17,6 +17,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
+#include "core/debugger/debugger.h"
#include "core/device_memory.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/mode.h"
@@ -26,9 +27,10 @@
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
-#include "core/hardware_interrupt_manager.h"
#include "core/hid/hid_core.h"
+#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
@@ -36,18 +38,19 @@
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
-#include "core/hle/service/hid/hid.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/time/time_manager.h"
+#include "core/internal_network/network.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
-#include "core/network/network.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
+#include "network/network.h"
+#include "video_core/host1x/host1x.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -127,7 +130,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
- : kernel{system}, fs_controller{system}, memory{system}, hid_core{},
+ : kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
SystemResultStatus Run() {
@@ -136,7 +139,6 @@ struct System::Impl {
kernel.Suspend(false);
core_timing.SyncPause(false);
- cpu_manager.Pause(false);
is_paused = false;
return status;
@@ -148,28 +150,34 @@ struct System::Impl {
core_timing.SyncPause(true);
kernel.Suspend(true);
- cpu_manager.Pause(true);
is_paused = true;
return status;
}
- std::unique_lock<std::mutex> StallCPU() {
+ bool IsPaused() const {
+ std::unique_lock lk(suspend_guard);
+ return is_paused;
+ }
+
+ std::unique_lock<std::mutex> StallProcesses() {
std::unique_lock<std::mutex> lk(suspend_guard);
kernel.Suspend(true);
core_timing.SyncPause(true);
- cpu_manager.Pause(true);
return lk;
}
- void UnstallCPU() {
+ void UnstallProcesses() {
if (!is_paused) {
core_timing.SyncPause(false);
kernel.Suspend(false);
- cpu_manager.Pause(false);
}
}
+ void InitializeDebugger(System& system, u16 port) {
+ debugger = std::make_unique<Debugger>(system, port);
+ }
+
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
LOG_DEBUG(Core, "initialized OK");
@@ -207,14 +215,16 @@ struct System::Impl {
telemetry_session = std::make_unique<Core::TelemetrySession>();
+ host1x_core = std::make_unique<Tegra::Host1x::Host1x>(system);
gpu_core = VideoCore::CreateGPU(emu_window, system);
if (!gpu_core) {
return SystemResultStatus::ErrorVideoCore;
}
+ audio_core = std::make_unique<AudioCore::AudioCore>(system);
+
service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
services = std::make_unique<Service::Services>(service_manager, system);
- interrupt_manager = std::make_unique<Hardware::InterruptManager>(system);
// Initialize time manager, which must happen after kernel is created
time_manager.Initialize();
@@ -252,9 +262,16 @@ struct System::Impl {
}
telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
+
+ // Create a resource limit for the process.
+ const auto physical_memory_size =
+ kernel.MemoryManager().GetSize(Kernel::KMemoryManager::Pool::Application);
+ auto* resource_limit = Kernel::CreateResourceLimitForProcess(system, physical_memory_size);
+
+ // Create the process.
auto main_process = Kernel::KProcess::Create(system.Kernel());
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
- Kernel::KProcess::ProcessType::Userland)
+ Kernel::KProcess::ProcessType::Userland, resource_limit)
.IsSuccess());
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
if (load_result != Loader::ResultStatus::Success) {
@@ -281,7 +298,7 @@ struct System::Impl {
if (Settings::values.gamecard_current_game) {
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
} else if (!Settings::values.gamecard_path.GetValue().empty()) {
- const auto gamecard_path = Settings::values.gamecard_path.GetValue();
+ const auto& gamecard_path = Settings::values.gamecard_path.GetValue();
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, gamecard_path));
}
}
@@ -294,11 +311,33 @@ struct System::Impl {
GetAndResetPerfStats();
perf_stats->BeginSystemFrame();
+ std::string name = "Unknown Game";
+ if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
+ LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result);
+ }
+
+ std::string title_version;
+ const FileSys::PatchManager pm(program_id, system.GetFileSystemController(),
+ system.GetContentProvider());
+ const auto metadata = pm.GetControlMetadata();
+ if (metadata.first != nullptr) {
+ title_version = metadata.first->GetVersionString();
+ }
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ Network::GameInfo game_info;
+ game_info.name = name;
+ game_info.id = program_id;
+ game_info.version = title_version;
+ room_member->SendGameInfo(game_info);
+ }
+
status = SystemResultStatus::Success;
return status;
}
void Shutdown() {
+ SetShuttingDown(true);
+
// Log last frame performance stats if game was loded
if (perf_stats) {
const auto perf_results = GetAndResetPerfStats();
@@ -317,25 +356,45 @@ struct System::Impl {
is_powered_on = false;
exit_lock = false;
- gpu_core->NotifyShutdown();
+ if (gpu_core != nullptr) {
+ gpu_core->NotifyShutdown();
+ }
+ kernel.ShutdownCores();
+ cpu_manager.Shutdown();
+ debugger.reset();
+ kernel.CloseServices();
services.reset();
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
- cpu_manager.Shutdown();
time_manager.Shutdown();
core_timing.Shutdown();
app_loader.reset();
+ audio_core.reset();
gpu_core.reset();
+ host1x_core.reset();
perf_stats.reset();
kernel.Shutdown();
memory.Reset();
applet_manager.ClearAll();
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ Network::GameInfo game_info{};
+ room_member->SendGameInfo(game_info);
+ }
+
LOG_DEBUG(Core, "Shutdown OK");
}
+ bool IsShuttingDown() const {
+ return is_shutting_down;
+ }
+
+ void SetShuttingDown(bool shutting_down) {
+ is_shutting_down = shutting_down;
+ }
+
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
@@ -378,8 +437,9 @@ struct System::Impl {
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
}
- std::mutex suspend_guard;
+ mutable std::mutex suspend_guard;
bool is_paused{};
+ std::atomic<bool> is_shutting_down{};
Timing::CoreTiming core_timing;
Kernel::KernelCore kernel;
@@ -391,10 +451,13 @@ struct System::Impl {
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<Tegra::GPU> gpu_core;
- std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
+ std::unique_ptr<Tegra::Host1x::Host1x> host1x_core;
std::unique_ptr<Core::DeviceMemory> device_memory;
+ std::unique_ptr<AudioCore::AudioCore> audio_core;
Core::Memory::Memory memory;
Core::HID::HIDCore hid_core;
+ Network::RoomNetwork room_network;
+
CpuManager cpu_manager;
std::atomic_bool is_powered_on{};
bool exit_lock = false;
@@ -426,6 +489,9 @@ struct System::Impl {
/// Network instance
Network::NetworkInstance network_instance;
+ /// Debugger
+ std::unique_ptr<Core::Debugger> debugger;
+
SystemResultStatus status = SystemResultStatus::Success;
std::string status_details = "";
@@ -462,8 +528,8 @@ SystemResultStatus System::Pause() {
return impl->Pause();
}
-SystemResultStatus System::SingleStep() {
- return SystemResultStatus::Success;
+bool System::IsPaused() const {
+ return impl->IsPaused();
}
void System::InvalidateCpuInstructionCaches() {
@@ -478,12 +544,30 @@ void System::Shutdown() {
impl->Shutdown();
}
-std::unique_lock<std::mutex> System::StallCPU() {
- return impl->StallCPU();
+bool System::IsShuttingDown() const {
+ return impl->IsShuttingDown();
}
-void System::UnstallCPU() {
- impl->UnstallCPU();
+void System::SetShuttingDown(bool shutting_down) {
+ impl->SetShuttingDown(shutting_down);
+}
+
+void System::DetachDebugger() {
+ if (impl->debugger) {
+ impl->debugger->NotifyShutdown();
+ }
+}
+
+std::unique_lock<std::mutex> System::StallProcesses() {
+ return impl->StallProcesses();
+}
+
+void System::UnstallProcesses() {
+ impl->UnstallProcesses();
+}
+
+void System::InitializeDebugger() {
+ impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
}
SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
@@ -495,10 +579,6 @@ bool System::IsPoweredOn() const {
return impl->is_powered_on.load(std::memory_order::relaxed);
}
-void System::PrepareReschedule() {
- // Deprecated, does nothing, kept for backward compatibility.
-}
-
void System::PrepareReschedule(const u32 core_index) {
impl->kernel.PrepareReschedule(core_index);
}
@@ -589,12 +669,12 @@ const Tegra::GPU& System::GPU() const {
return *impl->gpu_core;
}
-Core::Hardware::InterruptManager& System::InterruptManager() {
- return *impl->interrupt_manager;
+Tegra::Host1x::Host1x& System::Host1x() {
+ return *impl->host1x_core;
}
-const Core::Hardware::InterruptManager& System::InterruptManager() const {
- return *impl->interrupt_manager;
+const Tegra::Host1x::Host1x& System::Host1x() const {
+ return *impl->host1x_core;
}
VideoCore::RendererBase& System::Renderer() {
@@ -621,6 +701,14 @@ const HID::HIDCore& System::HIDCore() const {
return impl->hid_core;
}
+AudioCore::AudioCore& System::AudioCore() {
+ return *impl->audio_core;
+}
+
+const AudioCore::AudioCore& System::AudioCore() const {
+ return *impl->audio_core;
+}
+
Timing::CoreTiming& System::CoreTiming() {
return impl->core_timing;
}
@@ -803,6 +891,26 @@ bool System::IsMulticore() const {
return impl->is_multicore;
}
+bool System::DebuggerEnabled() const {
+ return Settings::values.use_gdbstub.GetValue();
+}
+
+Core::Debugger& System::GetDebugger() {
+ return *impl->debugger;
+}
+
+const Core::Debugger& System::GetDebugger() const {
+ return *impl->debugger;
+}
+
+Network::RoomNetwork& System::GetRoomNetwork() {
+ return impl->room_network;
+}
+
+const Network::RoomNetwork& System::GetRoomNetwork() const {
+ return impl->room_network;
+}
+
void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
impl->execute_program_callback = std::move(callback);
}
diff --git a/src/core/core.h b/src/core/core.h
index 52ff90359..7843cc8ad 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -75,28 +74,36 @@ class TimeManager;
namespace Tegra {
class DebugContext;
class GPU;
+namespace Host1x {
+class Host1x;
+} // namespace Host1x
} // namespace Tegra
namespace VideoCore {
class RendererBase;
} // namespace VideoCore
+namespace AudioCore {
+class AudioCore;
+} // namespace AudioCore
+
namespace Core::Timing {
class CoreTiming;
}
-namespace Core::Hardware {
-class InterruptManager;
-}
-
namespace Core::HID {
class HIDCore;
}
+namespace Network {
+class RoomNetwork;
+}
+
namespace Core {
class ARM_Interface;
class CpuManager;
+class Debugger;
class DeviceMemory;
class ExclusiveMonitor;
class SpeedLimiter;
@@ -147,11 +154,8 @@ public:
*/
[[nodiscard]] SystemResultStatus Pause();
- /**
- * Step the CPU one instruction
- * @return Result status, indicating whether or not the operation succeeded.
- */
- [[nodiscard]] SystemResultStatus SingleStep();
+ /// Check if the core is currently paused.
+ [[nodiscard]] bool IsPaused() const;
/**
* Invalidate the CPU instruction caches
@@ -165,8 +169,22 @@ public:
/// Shutdown the emulated system.
void Shutdown();
- std::unique_lock<std::mutex> StallCPU();
- void UnstallCPU();
+ /// Check if the core is shutting down.
+ [[nodiscard]] bool IsShuttingDown() const;
+
+ /// Set the shutting down state.
+ void SetShuttingDown(bool shutting_down);
+
+ /// Forcibly detach the debugger if it is running.
+ void DetachDebugger();
+
+ std::unique_lock<std::mutex> StallProcesses();
+ void UnstallProcesses();
+
+ /**
+ * Initialize the debugger.
+ */
+ void InitializeDebugger();
/**
* Load an executable application.
@@ -194,9 +212,6 @@ public:
[[nodiscard]] const Core::TelemetrySession& TelemetrySession() const;
/// Prepare the core emulation for a reschedule
- void PrepareReschedule();
-
- /// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index);
/// Gets and resets core performance statistics
@@ -244,12 +259,24 @@ public:
/// Gets an immutable reference to the GPU interface.
[[nodiscard]] const Tegra::GPU& GPU() const;
+ /// Gets a mutable reference to the Host1x interface
+ [[nodiscard]] Tegra::Host1x::Host1x& Host1x();
+
+ /// Gets an immutable reference to the Host1x interface.
+ [[nodiscard]] const Tegra::Host1x::Host1x& Host1x() const;
+
/// Gets a mutable reference to the renderer.
[[nodiscard]] VideoCore::RendererBase& Renderer();
/// Gets an immutable reference to the renderer.
[[nodiscard]] const VideoCore::RendererBase& Renderer() const;
+ /// Gets a mutable reference to the audio interface
+ [[nodiscard]] AudioCore::AudioCore& AudioCore();
+
+ /// Gets an immutable reference to the audio interface.
+ [[nodiscard]] const AudioCore::AudioCore& AudioCore() const;
+
/// Gets the global scheduler
[[nodiscard]] Kernel::GlobalSchedulerContext& GlobalSchedulerContext();
@@ -274,12 +301,6 @@ public:
/// Provides a constant reference to the core timing instance.
[[nodiscard]] const Timing::CoreTiming& CoreTiming() const;
- /// Provides a reference to the interrupt manager instance.
- [[nodiscard]] Core::Hardware::InterruptManager& InterruptManager();
-
- /// Provides a constant reference to the interrupt manager instance.
- [[nodiscard]] const Core::Hardware::InterruptManager& InterruptManager() const;
-
/// Provides a reference to the kernel instance.
[[nodiscard]] Kernel::KernelCore& Kernel();
@@ -357,6 +378,15 @@ public:
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
+ [[nodiscard]] Core::Debugger& GetDebugger();
+ [[nodiscard]] const Core::Debugger& GetDebugger() const;
+
+ /// Gets a mutable reference to the Room Network.
+ [[nodiscard]] Network::RoomNetwork& GetRoomNetwork();
+
+ /// Gets an immutable reference to the Room Network.
+ [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const;
+
void SetExitLock(bool locked);
[[nodiscard]] bool GetExitLock() const;
@@ -378,6 +408,9 @@ public:
/// Tells if system is running on multicore.
[[nodiscard]] bool IsMulticore() const;
+ /// Tells if the system debugger is enabled.
+ [[nodiscard]] bool DebuggerEnabled() const;
+
/// Type used for the frontend to designate a callback for System to re-launch the application
/// using a specified program index.
using ExecuteProgramCallback = std::function<void(std::size_t)>;
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index c2f0f609f..6c0fcb7b5 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <mutex>
@@ -21,10 +20,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
}
struct CoreTiming::Event {
- u64 time;
+ s64 time;
u64 fifo_order;
std::uintptr_t user_data;
std::weak_ptr<EventType> type;
+ s64 reschedule_time;
// Sort by time, unless the times are the same, in which case sort by
// the order added to the queue
@@ -43,10 +43,10 @@ CoreTiming::CoreTiming()
CoreTiming::~CoreTiming() = default;
void CoreTiming::ThreadEntry(CoreTiming& instance) {
- constexpr char name[] = "yuzu:HostTiming";
+ constexpr char name[] = "HostTiming";
MicroProfileOnThreadCreate(name);
Common::SetCurrentThreadName(name);
- Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
+ Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
instance.on_thread_init();
instance.ThreadLoop();
MicroProfileOnThreadExit();
@@ -57,7 +57,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
event_fifo_id = 0;
shutting_down = false;
ticks = 0;
- const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {};
+ const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
+ -> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
if (is_multicore) {
timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
@@ -80,12 +81,17 @@ void CoreTiming::Shutdown() {
void CoreTiming::Pause(bool is_paused) {
paused = is_paused;
pause_event.Set();
+
+ if (!is_paused) {
+ pause_end_time = GetGlobalTimeNs().count();
+ }
}
void CoreTiming::SyncPause(bool is_paused) {
if (is_paused == paused && paused_set == paused) {
return;
}
+
Pause(is_paused);
if (timer_thread) {
if (!is_paused) {
@@ -95,6 +101,10 @@ void CoreTiming::SyncPause(bool is_paused) {
while (paused_set != is_paused)
;
}
+
+ if (!is_paused) {
+ pause_end_time = GetGlobalTimeNs().count();
+ }
}
bool CoreTiming::IsRunning() const {
@@ -107,15 +117,33 @@ bool CoreTiming::HasPendingEvents() const {
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
const std::shared_ptr<EventType>& event_type,
- std::uintptr_t user_data) {
+ std::uintptr_t user_data, bool absolute_time) {
{
std::scoped_lock scope{basic_lock};
- const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
+ const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
+
+ event_queue.emplace_back(
+ Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
+ std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+ }
- event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type});
+ event.Set();
+}
+
+void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
+ std::chrono::nanoseconds resched_time,
+ const std::shared_ptr<EventType>& event_type,
+ std::uintptr_t user_data, bool absolute_time) {
+ {
+ std::scoped_lock scope{basic_lock};
+ const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
+
+ event_queue.emplace_back(
+ Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
+
event.Set();
}
@@ -194,20 +222,39 @@ std::optional<s64> CoreTiming::Advance() {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
- basic_lock.unlock();
if (const auto event_type{evt.type.lock()}) {
- event_type->callback(
- evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)});
+ basic_lock.unlock();
+
+ const auto new_schedule_time{event_type->callback(
+ evt.user_data, evt.time,
+ std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
+
+ basic_lock.lock();
+
+ if (evt.reschedule_time != 0) {
+ const auto next_schedule_time{new_schedule_time.has_value()
+ ? new_schedule_time.value().count()
+ : evt.reschedule_time};
+
+ // If this event was scheduled into a pause, its time now is going to be way behind.
+ // Re-set this event to continue from the end of the pause.
+ auto next_time{evt.time + next_schedule_time};
+ if (evt.time < pause_end_time) {
+ next_time = pause_end_time + next_schedule_time;
+ }
+
+ event_queue.emplace_back(
+ Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
+ std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+ }
}
- basic_lock.lock();
global_timer = GetGlobalTimeNs().count();
}
if (!event_queue.empty()) {
- const s64 next_time = event_queue.front().time - global_timer;
- return next_time;
+ return event_queue.front().time;
} else {
return std::nullopt;
}
@@ -220,16 +267,35 @@ void CoreTiming::ThreadLoop() {
paused_set = false;
const auto next_time = Advance();
if (next_time) {
- if (*next_time > 0) {
- std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
- event.WaitFor(next_time_ns);
+ // There are more events left in the queue, wait until the next event.
+ const auto wait_time = *next_time - GetGlobalTimeNs().count();
+ if (wait_time > 0) {
+ // Assume a timer resolution of 1ms.
+ static constexpr s64 TimerResolutionNS = 1000000;
+
+ // Sleep in discrete intervals of the timer resolution, and spin the rest.
+ const auto sleep_time = wait_time - (wait_time % TimerResolutionNS);
+ if (sleep_time > 0) {
+ event.WaitFor(std::chrono::nanoseconds(sleep_time));
+ }
+
+ while (!paused && !event.IsSet() && GetGlobalTimeNs().count() < *next_time) {
+ // Yield to reduce thread starvation.
+ std::this_thread::yield();
+ }
+
+ if (event.IsSet()) {
+ event.Reset();
+ }
}
} else {
+ // Queue is empty, wait until another event is scheduled and signals us to continue.
wait_set = true;
event.Wait();
}
wait_set = false;
}
+
paused_set = true;
clock->Pause(true);
pause_event.Wait();
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 888828fd0..3259397b2 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,21 +7,21 @@
#include <chrono>
#include <functional>
#include <memory>
+#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <vector>
#include "common/common_types.h"
-#include "common/spin_lock.h"
#include "common/thread.h"
#include "common/wall_clock.h"
namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event.
-using TimedCallback =
- std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>;
+using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
+ std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
/// Contains the characteristics of a particular event.
struct EventType {
@@ -94,7 +93,15 @@ public:
/// Schedules an event in core timing
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
- const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0);
+ const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0,
+ bool absolute_time = false);
+
+ /// Schedules an event which will automatically re-schedule itself with the given time, until
+ /// unscheduled
+ void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
+ std::chrono::nanoseconds resched_time,
+ const std::shared_ptr<EventType>& event_type,
+ std::uintptr_t user_data = 0, bool absolute_time = false);
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
@@ -137,7 +144,7 @@ private:
std::unique_ptr<Common::WallClock> clock;
- u64 global_timer = 0;
+ s64 global_timer = 0;
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
@@ -149,8 +156,8 @@ private:
std::shared_ptr<EventType> ev_lost;
Common::Event event{};
Common::Event pause_event{};
- Common::SpinLock basic_lock{};
- Common::SpinLock advance_lock{};
+ std::mutex basic_lock;
+ std::mutex advance_lock;
std::unique_ptr<std::thread> timer_thread;
std::atomic<bool> paused{};
std::atomic<bool> paused_set{};
@@ -160,6 +167,7 @@ private:
std::function<void()> on_thread_init{};
bool is_multicore{};
+ s64 pause_end_time{};
/// Cycle timing
u64 ticks{};
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h
index 14c36a485..fe5aaefc7 100644
--- a/src/core/core_timing_util.h
+++ b/src/core/core_timing_util.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index cbcc54891..0dd4c2196 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/fiber.h"
#include "common/microprofile.h"
@@ -9,6 +8,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/cpu_manager.h"
+#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -22,75 +22,51 @@ CpuManager::~CpuManager() = default;
void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
std::size_t core) {
- cpu_manager.RunThread(stop_token, core);
+ cpu_manager.RunThread(core);
}
void CpuManager::Initialize() {
- running_mode = true;
- if (is_multicore) {
- for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
- }
- } else {
- core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
+ num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
+ gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
+
+ for (std::size_t core = 0; core < num_cores; core++) {
+ core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
}
}
void CpuManager::Shutdown() {
- running_mode = false;
- Pause(false);
-}
-
-std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
- return GuestThreadFunction;
-}
-
-std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
- return IdleThreadFunction;
-}
-
-std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
- return SuspendThreadFunction;
-}
-
-void CpuManager::GuestThreadFunction(void* cpu_manager_) {
- CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
- if (cpu_manager->is_multicore) {
- cpu_manager->MultiCoreRunGuestThread();
- } else {
- cpu_manager->SingleCoreRunGuestThread();
+ for (std::size_t core = 0; core < num_cores; core++) {
+ if (core_data[core].host_thread.joinable()) {
+ core_data[core].host_thread.join();
+ }
}
}
-void CpuManager::GuestRewindFunction(void* cpu_manager_) {
- CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
- if (cpu_manager->is_multicore) {
- cpu_manager->MultiCoreRunGuestLoop();
+void CpuManager::GuestThreadFunction() {
+ if (is_multicore) {
+ MultiCoreRunGuestThread();
} else {
- cpu_manager->SingleCoreRunGuestLoop();
+ SingleCoreRunGuestThread();
}
}
-void CpuManager::IdleThreadFunction(void* cpu_manager_) {
- CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
- if (cpu_manager->is_multicore) {
- cpu_manager->MultiCoreRunIdleThread();
+void CpuManager::IdleThreadFunction() {
+ if (is_multicore) {
+ MultiCoreRunIdleThread();
} else {
- cpu_manager->SingleCoreRunIdleThread();
+ SingleCoreRunIdleThread();
}
}
-void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
- CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
- if (cpu_manager->is_multicore) {
- cpu_manager->MultiCoreRunSuspendThread();
- } else {
- cpu_manager->SingleCoreRunSuspendThread();
- }
+void CpuManager::ShutdownThreadFunction() {
+ ShutdownThread();
}
-void* CpuManager::GetStartFuncParamater() {
- return static_cast<void*>(this);
+void CpuManager::HandleInterrupt() {
+ auto& kernel = system.Kernel();
+ auto core_index = kernel.CurrentPhysicalCoreIndex();
+
+ Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index));
}
///////////////////////////////////////////////////////////////////////////////
@@ -98,90 +74,37 @@ void* CpuManager::GetStartFuncParamater() {
///////////////////////////////////////////////////////////////////////////////
void CpuManager::MultiCoreRunGuestThread() {
+ // Similar to UserModeThreadStarter in HOS
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
- auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
- auto& host_context = thread->GetHostContext();
- host_context->SetRewindPoint(GuestRewindFunction, this);
- MultiCoreRunGuestLoop();
-}
-
-void CpuManager::MultiCoreRunGuestLoop() {
- auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
- system.EnterDynarmicProfile();
while (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
- system.ExitDynarmicProfile();
- {
- Kernel::KScopedDisableDispatch dd(kernel);
- physical_core->ArmInterface().ClearExclusiveState();
- }
+
+ HandleInterrupt();
}
}
void CpuManager::MultiCoreRunIdleThread() {
- auto& kernel = system.Kernel();
- while (true) {
- Kernel::KScopedDisableDispatch dd(kernel);
- kernel.CurrentPhysicalCore().Idle();
- }
-}
+ // Not accurate to HOS. Remove this entire method when singlecore is removed.
+ // See notes in KScheduler::ScheduleImpl for more information about why this
+ // is inaccurate.
-void CpuManager::MultiCoreRunSuspendThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
- while (true) {
- auto core = kernel.CurrentPhysicalCoreIndex();
- auto& scheduler = *kernel.CurrentScheduler();
- Kernel::KThread* current_thread = scheduler.GetCurrentThread();
- Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
- ASSERT(scheduler.ContextSwitchPending());
- ASSERT(core == kernel.CurrentPhysicalCoreIndex());
- scheduler.RescheduleCurrentCore();
- }
-}
-void CpuManager::MultiCorePause(bool paused) {
- if (!paused) {
- bool all_not_barrier = false;
- while (!all_not_barrier) {
- all_not_barrier = true;
- for (const auto& data : core_data) {
- all_not_barrier &= !data.is_running.load() && data.initialized.load();
- }
- }
- for (auto& data : core_data) {
- data.enter_barrier->Set();
- }
- if (paused_state.load()) {
- bool all_barrier = false;
- while (!all_barrier) {
- all_barrier = true;
- for (const auto& data : core_data) {
- all_barrier &= data.is_paused.load() && data.initialized.load();
- }
- }
- for (auto& data : core_data) {
- data.exit_barrier->Set();
- }
- }
- } else {
- /// Wait until all cores are paused.
- bool all_barrier = false;
- while (!all_barrier) {
- all_barrier = true;
- for (const auto& data : core_data) {
- all_barrier &= data.is_paused.load() && data.initialized.load();
- }
+ while (true) {
+ auto& physical_core = kernel.CurrentPhysicalCore();
+ if (!physical_core.IsInterrupted()) {
+ physical_core.Idle();
}
- /// Don't release the barrier
+
+ HandleInterrupt();
}
- paused_state = paused;
}
///////////////////////////////////////////////////////////////////////////////
@@ -191,175 +114,110 @@ void CpuManager::MultiCorePause(bool paused) {
void CpuManager::SingleCoreRunGuestThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
- auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
- auto& host_context = thread->GetHostContext();
- host_context->SetRewindPoint(GuestRewindFunction, this);
- SingleCoreRunGuestLoop();
-}
-void CpuManager::SingleCoreRunGuestLoop() {
- auto& kernel = system.Kernel();
while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore();
- system.EnterDynarmicProfile();
if (!physical_core->IsInterrupted()) {
physical_core->Run();
physical_core = &kernel.CurrentPhysicalCore();
}
- system.ExitDynarmicProfile();
+
kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance();
kernel.SetIsPhantomModeForSingleCore(false);
- physical_core->ArmInterface().ClearExclusiveState();
+
PreemptSingleCore();
- auto& scheduler = kernel.Scheduler(current_core);
- scheduler.RescheduleCurrentCore();
+ HandleInterrupt();
}
}
void CpuManager::SingleCoreRunIdleThread() {
auto& kernel = system.Kernel();
+ kernel.CurrentScheduler()->OnThreadStart();
+
while (true) {
- auto& physical_core = kernel.CurrentPhysicalCore();
PreemptSingleCore(false);
system.CoreTiming().AddTicks(1000U);
idle_count++;
- auto& scheduler = physical_core.Scheduler();
- scheduler.RescheduleCurrentCore();
+ HandleInterrupt();
}
}
-void CpuManager::SingleCoreRunSuspendThread() {
+void CpuManager::PreemptSingleCore(bool from_running_environment) {
auto& kernel = system.Kernel();
- kernel.CurrentScheduler()->OnThreadStart();
- while (true) {
- auto core = kernel.GetCurrentHostThreadID();
- auto& scheduler = *kernel.CurrentScheduler();
- Kernel::KThread* current_thread = scheduler.GetCurrentThread();
- Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context);
- ASSERT(scheduler.ContextSwitchPending());
- ASSERT(core == kernel.GetCurrentHostThreadID());
- scheduler.RescheduleCurrentCore();
- }
-}
-void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
- {
- auto& kernel = system.Kernel();
- auto& scheduler = kernel.Scheduler(current_core);
- Kernel::KThread* current_thread = scheduler.GetCurrentThread();
- if (idle_count >= 4 || from_running_enviroment) {
- if (!from_running_enviroment) {
- system.CoreTiming().Idle();
- idle_count = 0;
- }
- kernel.SetIsPhantomModeForSingleCore(true);
- system.CoreTiming().Advance();
- kernel.SetIsPhantomModeForSingleCore(false);
+ if (idle_count >= 4 || from_running_environment) {
+ if (!from_running_environment) {
+ system.CoreTiming().Idle();
+ idle_count = 0;
}
- current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
- system.CoreTiming().ResetTicks();
- scheduler.Unload(scheduler.GetCurrentThread());
-
- auto& next_scheduler = kernel.Scheduler(current_core);
- Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext());
+ kernel.SetIsPhantomModeForSingleCore(true);
+ system.CoreTiming().Advance();
+ kernel.SetIsPhantomModeForSingleCore(false);
}
+ current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
+ system.CoreTiming().ResetTicks();
+ kernel.Scheduler(current_core).PreemptSingleCore();
- // May have changed scheduler
- {
- auto& scheduler = system.Kernel().Scheduler(current_core);
- scheduler.Reload(scheduler.GetCurrentThread());
- if (!scheduler.IsIdle()) {
- idle_count = 0;
- }
+ // We've now been scheduled again, and we may have exchanged schedulers.
+ // Reload the scheduler in case it's different.
+ if (!kernel.Scheduler(current_core).IsIdle()) {
+ idle_count = 0;
}
}
-void CpuManager::SingleCorePause(bool paused) {
- if (!paused) {
- bool all_not_barrier = false;
- while (!all_not_barrier) {
- all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
- }
- core_data[0].enter_barrier->Set();
- if (paused_state.load()) {
- bool all_barrier = false;
- while (!all_barrier) {
- all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
- }
- core_data[0].exit_barrier->Set();
- }
- } else {
- /// Wait until all cores are paused.
- bool all_barrier = false;
- while (!all_barrier) {
- all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
- }
- /// Don't release the barrier
- }
- paused_state = paused;
+void CpuManager::GuestActivate() {
+ // Similar to the HorizonKernelMain callback in HOS
+ auto& kernel = system.Kernel();
+ auto* scheduler = kernel.CurrentScheduler();
+
+ scheduler->Activate();
+ UNREACHABLE();
}
-void CpuManager::Pause(bool paused) {
- if (is_multicore) {
- MultiCorePause(paused);
- } else {
- SingleCorePause(paused);
- }
+void CpuManager::ShutdownThread() {
+ auto& kernel = system.Kernel();
+ auto* thread = kernel.GetCurrentEmuThread();
+ auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
+
+ Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context);
+ UNREACHABLE();
}
-void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
+void CpuManager::RunThread(std::size_t core) {
/// Initialization
system.RegisterCoreThread(core);
std::string name;
if (is_multicore) {
- name = "yuzu:CPUCore_" + std::to_string(core);
+ name = "CPUCore_" + std::to_string(core);
} else {
- name = "yuzu:CPUThread";
+ name = "CPUThread";
}
MicroProfileOnThreadCreate(name.c_str());
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
auto& data = core_data[core];
- data.enter_barrier = std::make_unique<Common::Event>();
- data.exit_barrier = std::make_unique<Common::Event>();
data.host_context = Common::Fiber::ThreadToFiber();
- data.is_running = false;
- data.initialized = true;
- const bool sc_sync = !is_async_gpu && !is_multicore;
- bool sc_sync_first_use = sc_sync;
// Cleanup
SCOPE_EXIT({
data.host_context->Exit();
- data.enter_barrier.reset();
- data.exit_barrier.reset();
- data.initialized = false;
MicroProfileOnThreadExit();
});
- /// Running
- while (running_mode) {
- data.is_running = false;
- data.enter_barrier->Wait();
- if (sc_sync_first_use) {
- system.GPU().ObtainContext();
- sc_sync_first_use = false;
- }
+ // Running
+ gpu_barrier->Sync();
- // Emulation was stopped
- if (stop_token.stop_requested()) {
- return;
- }
-
- auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
- data.is_running = true;
- Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
- data.is_running = false;
- data.is_paused = true;
- data.exit_barrier->Wait();
- data.is_paused = false;
+ if (!is_async_gpu && !is_multicore) {
+ system.GPU().ObtainContext();
}
+
+ auto& kernel = system.Kernel();
+ auto& scheduler = *kernel.CurrentScheduler();
+ auto* thread = scheduler.GetSchedulerCurrentThread();
+ Kernel::SetCurrentThread(kernel, thread);
+
+ Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext());
}
} // namespace Core
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 9d92d4af0..95ea3ef39 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -44,15 +43,25 @@ public:
is_async_gpu = is_async;
}
+ void OnGpuReady() {
+ gpu_barrier->Sync();
+ }
+
void Initialize();
void Shutdown();
- void Pause(bool paused);
-
- static std::function<void(void*)> GetGuestThreadStartFunc();
- static std::function<void(void*)> GetIdleThreadStartFunc();
- static std::function<void(void*)> GetSuspendThreadStartFunc();
- void* GetStartFuncParamater();
+ std::function<void()> GetGuestActivateFunc() {
+ return [this] { GuestActivate(); };
+ }
+ std::function<void()> GetGuestThreadFunc() {
+ return [this] { GuestThreadFunction(); };
+ }
+ std::function<void()> GetIdleThreadStartFunc() {
+ return [this] { IdleThreadFunction(); };
+ }
+ std::function<void()> GetShutdownThreadStartFunc() {
+ return [this] { ShutdownThreadFunction(); };
+ }
void PreemptSingleCore(bool from_running_enviroment = true);
@@ -61,46 +70,36 @@ public:
}
private:
- static void GuestThreadFunction(void* cpu_manager);
- static void GuestRewindFunction(void* cpu_manager);
- static void IdleThreadFunction(void* cpu_manager);
- static void SuspendThreadFunction(void* cpu_manager);
+ void GuestThreadFunction();
+ void IdleThreadFunction();
+ void ShutdownThreadFunction();
void MultiCoreRunGuestThread();
- void MultiCoreRunGuestLoop();
void MultiCoreRunIdleThread();
- void MultiCoreRunSuspendThread();
- void MultiCorePause(bool paused);
void SingleCoreRunGuestThread();
- void SingleCoreRunGuestLoop();
void SingleCoreRunIdleThread();
- void SingleCoreRunSuspendThread();
- void SingleCorePause(bool paused);
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
- void RunThread(std::stop_token stop_token, std::size_t core);
+ void GuestActivate();
+ void HandleInterrupt();
+ void ShutdownThread();
+ void RunThread(std::size_t core);
struct CoreData {
std::shared_ptr<Common::Fiber> host_context;
- std::unique_ptr<Common::Event> enter_barrier;
- std::unique_ptr<Common::Event> exit_barrier;
- std::atomic<bool> is_running;
- std::atomic<bool> is_paused;
- std::atomic<bool> initialized;
std::jthread host_thread;
};
- std::atomic<bool> running_mode{};
- std::atomic<bool> paused_state{};
-
+ std::unique_ptr<Common::Barrier> gpu_barrier{};
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
bool is_async_gpu{};
bool is_multicore{};
std::atomic<std::size_t> current_core{};
std::size_t idle_count{};
+ std::size_t num_cores{};
static constexpr std::size_t max_cycle_runs = 5;
System& system;
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
index 85a666de9..cd7e15a58 100644
--- a/src/core/crypto/aes_util.cpp
+++ b/src/core/crypto/aes_util.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <mbedtls/cipher.h>
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
index 230451b8f..a67ba5352 100644
--- a/src/core/crypto/aes_util.h
+++ b/src/core/crypto/aes_util.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp
index 3a2af4f50..b48c3f041 100644
--- a/src/core/crypto/ctr_encryption_layer.cpp
+++ b/src/core/crypto/ctr_encryption_layer.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h
index f86f01b6f..77f08d776 100644
--- a/src/core/crypto/ctr_encryption_layer.h
+++ b/src/core/crypto/ctr_encryption_layer.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp
index 4c377d7d4..cd10551ec 100644
--- a/src/core/crypto/encryption_layer.cpp
+++ b/src/core/crypto/encryption_layer.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/crypto/encryption_layer.h"
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h
index 53619cb38..d3082ba53 100644
--- a/src/core/crypto/encryption_layer.h
+++ b/src/core/crypto/encryption_layer.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 9244907b5..443323390 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
@@ -141,7 +140,6 @@ u64 GetSignatureTypeDataSize(SignatureType type) {
return 0x3C;
}
UNREACHABLE();
- return 0;
}
u64 GetSignatureTypePaddingSize(SignatureType type) {
@@ -156,7 +154,6 @@ u64 GetSignatureTypePaddingSize(SignatureType type) {
return 0x40;
}
UNREACHABLE();
- return 0;
}
SignatureType Ticket::GetSignatureType() const {
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index ac1eb8962..dbf9ebfe4 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index d18252a54..97f5c8cea 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
// NOTE TO FUTURE MAINTAINERS:
// When a new version of switch cryptography is released,
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 7a7b5d038..057a70683 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/crypto/sha_util.cpp b/src/core/crypto/sha_util.cpp
index 180008a85..7a2c04838 100644
--- a/src/core/crypto/sha_util.cpp
+++ b/src/core/crypto/sha_util.cpp
@@ -1,5 +1,4 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
namespace Crypto {} // namespace Crypto
diff --git a/src/core/crypto/sha_util.h b/src/core/crypto/sha_util.h
index fa3fa9d33..5c2c43dbd 100644
--- a/src/core/crypto/sha_util.h
+++ b/src/core/crypto/sha_util.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/crypto/xts_encryption_layer.cpp b/src/core/crypto/xts_encryption_layer.cpp
index c2b7ea309..b60303412 100644
--- a/src/core/crypto/xts_encryption_layer.cpp
+++ b/src/core/crypto/xts_encryption_layer.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
diff --git a/src/core/crypto/xts_encryption_layer.h b/src/core/crypto/xts_encryption_layer.h
index 5f8f00fe7..735e660cb 100644
--- a/src/core/crypto/xts_encryption_layer.h
+++ b/src/core/crypto/xts_encryption_layer.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
new file mode 100644
index 000000000..339f971e6
--- /dev/null
+++ b/src/core/debugger/debugger.cpp
@@ -0,0 +1,316 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <algorithm>
+#include <mutex>
+#include <thread>
+
+#include <boost/asio.hpp>
+#include <boost/process/async_pipe.hpp>
+
+#include "common/logging/log.h"
+#include "common/thread.h"
+#include "core/core.h"
+#include "core/debugger/debugger.h"
+#include "core/debugger/debugger_interface.h"
+#include "core/debugger/gdbstub.h"
+#include "core/hle/kernel/global_scheduler_context.h"
+#include "core/hle/kernel/k_scheduler.h"
+
+template <typename Readable, typename Buffer, typename Callback>
+static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
+ static_assert(std::is_trivial_v<Buffer>);
+ auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
+ r.async_read_some(
+ boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) {
+ if (!error.failed()) {
+ const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
+ std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
+ c(received_data);
+ }
+
+ AsyncReceiveInto(r, buffer, c);
+ });
+}
+
+template <typename Readable, typename Buffer>
+static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) {
+ static_assert(std::is_trivial_v<Buffer>);
+ auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))};
+ size_t bytes_read = r.read_some(boost_buffer);
+ const u8* buffer_start = reinterpret_cast<const u8*>(&buffer);
+ std::span<const u8> received_data{buffer_start, buffer_start + bytes_read};
+ return received_data;
+}
+
+enum class SignalType {
+ Stopped,
+ Watchpoint,
+ ShuttingDown,
+};
+
+struct SignalInfo {
+ SignalType type;
+ Kernel::KThread* thread;
+ const Kernel::DebugWatchpoint* watchpoint;
+};
+
+namespace Core {
+
+class DebuggerImpl : public DebuggerBackend {
+public:
+ explicit DebuggerImpl(Core::System& system_, u16 port)
+ : system{system_}, signal_pipe{io_context}, client_socket{io_context} {
+ frontend = std::make_unique<GDBStub>(*this, system);
+ InitializeServer(port);
+ }
+
+ ~DebuggerImpl() override {
+ ShutdownServer();
+ }
+
+ bool SignalDebugger(SignalInfo signal_info) {
+ {
+ std::scoped_lock lk{connection_lock};
+
+ if (stopped) {
+ // Do not notify the debugger about another event.
+ // It should be ignored.
+ return false;
+ }
+
+ // Set up the state.
+ stopped = true;
+ info = signal_info;
+ }
+
+ // Write a single byte into the pipe to wake up the debug interface.
+ boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped)));
+ return true;
+ }
+
+ std::span<const u8> ReadFromClient() override {
+ return ReceiveInto(client_socket, client_data);
+ }
+
+ void WriteToClient(std::span<const u8> data) override {
+ boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes()));
+ }
+
+ void SetActiveThread(Kernel::KThread* thread) override {
+ active_thread = thread;
+ }
+
+ Kernel::KThread* GetActiveThread() override {
+ return active_thread;
+ }
+
+private:
+ void InitializeServer(u16 port) {
+ using boost::asio::ip::tcp;
+
+ LOG_INFO(Debug_GDBStub, "Starting server on port {}...", port);
+
+ // Run the connection thread.
+ connection_thread = std::jthread([&, port](std::stop_token stop_token) {
+ try {
+ // Initialize the listening socket and accept a new client.
+ tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port};
+ tcp::acceptor acceptor{io_context, endpoint};
+
+ acceptor.async_accept(client_socket, [](const auto&) {});
+ io_context.run_one();
+ io_context.restart();
+
+ if (stop_token.stop_requested()) {
+ return;
+ }
+
+ ThreadLoop(stop_token);
+ } catch (const std::exception& ex) {
+ LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what());
+ }
+ });
+ }
+
+ void ShutdownServer() {
+ connection_thread.request_stop();
+ io_context.stop();
+ connection_thread.join();
+ }
+
+ void ThreadLoop(std::stop_token stop_token) {
+ Common::SetCurrentThreadName("Debugger");
+
+ // Set up the client signals for new data.
+ AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); });
+ AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); });
+
+ // Set the active thread.
+ UpdateActiveThread();
+
+ // Set up the frontend.
+ frontend->Connected();
+
+ // Main event loop.
+ while (!stop_token.stop_requested() && io_context.run()) {
+ }
+ }
+
+ void PipeData(std::span<const u8> data) {
+ switch (info.type) {
+ case SignalType::Stopped:
+ case SignalType::Watchpoint:
+ // Stop emulation.
+ PauseEmulation();
+
+ // Notify the client.
+ active_thread = info.thread;
+ UpdateActiveThread();
+
+ if (info.type == SignalType::Watchpoint) {
+ frontend->Watchpoint(active_thread, *info.watchpoint);
+ } else {
+ frontend->Stopped(active_thread);
+ }
+
+ break;
+ case SignalType::ShuttingDown:
+ frontend->ShuttingDown();
+
+ // Wait for emulation to shut down gracefully now.
+ signal_pipe.close();
+ client_socket.shutdown(boost::asio::socket_base::shutdown_both);
+ LOG_INFO(Debug_GDBStub, "Shut down server");
+
+ break;
+ }
+ }
+
+ void ClientData(std::span<const u8> data) {
+ const auto actions{frontend->ClientData(data)};
+ for (const auto action : actions) {
+ switch (action) {
+ case DebuggerAction::Interrupt: {
+ {
+ std::scoped_lock lk{connection_lock};
+ stopped = true;
+ }
+ PauseEmulation();
+ UpdateActiveThread();
+ frontend->Stopped(active_thread);
+ break;
+ }
+ case DebuggerAction::Continue:
+ MarkResumed([&] { ResumeEmulation(); });
+ break;
+ case DebuggerAction::StepThreadUnlocked:
+ MarkResumed([&] {
+ active_thread->SetStepState(Kernel::StepState::StepPending);
+ active_thread->Resume(Kernel::SuspendType::Debug);
+ ResumeEmulation(active_thread);
+ });
+ break;
+ case DebuggerAction::StepThreadLocked: {
+ MarkResumed([&] {
+ active_thread->SetStepState(Kernel::StepState::StepPending);
+ active_thread->Resume(Kernel::SuspendType::Debug);
+ });
+ break;
+ }
+ case DebuggerAction::ShutdownEmulation: {
+ // Spawn another thread that will exit after shutdown,
+ // to avoid a deadlock
+ Core::System* system_ref{&system};
+ std::thread t([system_ref] { system_ref->Exit(); });
+ t.detach();
+ break;
+ }
+ }
+ }
+ }
+
+ void PauseEmulation() {
+ Kernel::KScopedSchedulerLock sl{system.Kernel()};
+
+ // Put all threads to sleep on next scheduler round.
+ for (auto* thread : ThreadList()) {
+ thread->RequestSuspend(Kernel::SuspendType::Debug);
+ }
+ }
+
+ void ResumeEmulation(Kernel::KThread* except = nullptr) {
+ // Wake up all threads.
+ for (auto* thread : ThreadList()) {
+ if (thread == except) {
+ continue;
+ }
+
+ thread->SetStepState(Kernel::StepState::NotStepping);
+ thread->Resume(Kernel::SuspendType::Debug);
+ }
+ }
+
+ template <typename Callback>
+ void MarkResumed(Callback&& cb) {
+ Kernel::KScopedSchedulerLock sl{system.Kernel()};
+ std::scoped_lock cl{connection_lock};
+ stopped = false;
+ cb();
+ }
+
+ void UpdateActiveThread() {
+ const auto& threads{ThreadList()};
+ if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) {
+ active_thread = threads[0];
+ }
+ }
+
+ const std::vector<Kernel::KThread*>& ThreadList() {
+ return system.GlobalSchedulerContext().GetThreadList();
+ }
+
+private:
+ System& system;
+ std::unique_ptr<DebuggerFrontend> frontend;
+
+ std::jthread connection_thread;
+ std::mutex connection_lock;
+ boost::asio::io_context io_context;
+ boost::process::async_pipe signal_pipe;
+ boost::asio::ip::tcp::socket client_socket;
+
+ SignalInfo info;
+ Kernel::KThread* active_thread;
+ bool pipe_data;
+ bool stopped;
+
+ std::array<u8, 4096> client_data;
+};
+
+Debugger::Debugger(Core::System& system, u16 port) {
+ try {
+ impl = std::make_unique<DebuggerImpl>(system, port);
+ } catch (const std::exception& ex) {
+ LOG_CRITICAL(Debug_GDBStub, "Failed to initialize debugger: {}", ex.what());
+ }
+}
+
+Debugger::~Debugger() = default;
+
+bool Debugger::NotifyThreadStopped(Kernel::KThread* thread) {
+ return impl && impl->SignalDebugger(SignalInfo{SignalType::Stopped, thread, nullptr});
+}
+
+bool Debugger::NotifyThreadWatchpoint(Kernel::KThread* thread,
+ const Kernel::DebugWatchpoint& watch) {
+ return impl && impl->SignalDebugger(SignalInfo{SignalType::Watchpoint, thread, &watch});
+}
+
+void Debugger::NotifyShutdown() {
+ if (impl) {
+ impl->SignalDebugger(SignalInfo{SignalType::ShuttingDown, nullptr, nullptr});
+ }
+}
+
+} // namespace Core
diff --git a/src/core/debugger/debugger.h b/src/core/debugger/debugger.h
new file mode 100644
index 000000000..b2f503376
--- /dev/null
+++ b/src/core/debugger/debugger.h
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+
+namespace Kernel {
+class KThread;
+struct DebugWatchpoint;
+} // namespace Kernel
+
+namespace Core {
+class System;
+
+class DebuggerImpl;
+
+class Debugger {
+public:
+ /**
+ * Blocks and waits for a connection on localhost, port `server_port`.
+ * Does not create the debugger if the port is already in use.
+ */
+ explicit Debugger(Core::System& system, u16 server_port);
+ ~Debugger();
+
+ /**
+ * Notify the debugger that the given thread is stopped
+ * (due to a breakpoint, or due to stopping after a successful step).
+ *
+ * The debugger will asynchronously halt emulation after the notification has
+ * occurred. If another thread attempts to notify before emulation has stopped,
+ * it is ignored and this method will return false. Otherwise it will return true.
+ */
+ bool NotifyThreadStopped(Kernel::KThread* thread);
+
+ /**
+ * Notify the debugger that a shutdown is being performed now and disconnect.
+ */
+ void NotifyShutdown();
+
+ /*
+ * Notify the debugger that the given thread has stopped due to hitting a watchpoint.
+ */
+ bool NotifyThreadWatchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch);
+
+private:
+ std::unique_ptr<DebuggerImpl> impl;
+};
+} // namespace Core
diff --git a/src/core/debugger/debugger_interface.h b/src/core/debugger/debugger_interface.h
new file mode 100644
index 000000000..5b31edc43
--- /dev/null
+++ b/src/core/debugger/debugger_interface.h
@@ -0,0 +1,90 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <functional>
+#include <span>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace Kernel {
+class KThread;
+struct DebugWatchpoint;
+} // namespace Kernel
+
+namespace Core {
+
+enum class DebuggerAction {
+ Interrupt, ///< Stop emulation as soon as possible.
+ Continue, ///< Resume emulation.
+ StepThreadLocked, ///< Step the currently-active thread without resuming others.
+ StepThreadUnlocked, ///< Step the currently-active thread and resume others.
+ ShutdownEmulation, ///< Shut down the emulator.
+};
+
+class DebuggerBackend {
+public:
+ virtual ~DebuggerBackend() = default;
+
+ /**
+ * Can be invoked from a callback to synchronously wait for more data.
+ * Will return as soon as least one byte is received. Reads up to 4096 bytes.
+ */
+ virtual std::span<const u8> ReadFromClient() = 0;
+
+ /**
+ * Can be invoked from a callback to write data to the client.
+ * Returns immediately after the data is sent.
+ */
+ virtual void WriteToClient(std::span<const u8> data) = 0;
+
+ /**
+ * Gets the currently active thread when the debugger is stopped.
+ */
+ virtual Kernel::KThread* GetActiveThread() = 0;
+
+ /**
+ * Sets the currently active thread when the debugger is stopped.
+ */
+ virtual void SetActiveThread(Kernel::KThread* thread) = 0;
+};
+
+class DebuggerFrontend {
+public:
+ explicit DebuggerFrontend(DebuggerBackend& backend_) : backend{backend_} {}
+
+ virtual ~DebuggerFrontend() = default;
+
+ /**
+ * Called after the client has successfully connected to the port.
+ */
+ virtual void Connected() = 0;
+
+ /**
+ * Called when emulation has stopped.
+ */
+ virtual void Stopped(Kernel::KThread* thread) = 0;
+
+ /**
+ * Called when emulation is shutting down.
+ */
+ virtual void ShuttingDown() = 0;
+
+ /*
+ * Called when emulation has stopped on a watchpoint.
+ */
+ virtual void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) = 0;
+
+ /**
+ * Called when new data is asynchronously received on the client socket.
+ * A list of actions to perform is returned.
+ */
+ [[nodiscard]] virtual std::vector<DebuggerAction> ClientData(std::span<const u8> data) = 0;
+
+protected:
+ DebuggerBackend& backend;
+};
+
+} // namespace Core
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
new file mode 100644
index 000000000..884229c77
--- /dev/null
+++ b/src/core/debugger/gdbstub.cpp
@@ -0,0 +1,718 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <atomic>
+#include <numeric>
+#include <optional>
+#include <thread>
+
+#include <boost/algorithm/string.hpp>
+
+#include "common/hex_util.h"
+#include "common/logging/log.h"
+#include "common/scope_exit.h"
+#include "core/arm/arm_interface.h"
+#include "core/core.h"
+#include "core/debugger/gdbstub.h"
+#include "core/debugger/gdbstub_arch.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_thread.h"
+#include "core/loader/loader.h"
+#include "core/memory.h"
+
+namespace Core {
+
+constexpr char GDB_STUB_START = '$';
+constexpr char GDB_STUB_END = '#';
+constexpr char GDB_STUB_ACK = '+';
+constexpr char GDB_STUB_NACK = '-';
+constexpr char GDB_STUB_INT3 = 0x03;
+constexpr int GDB_STUB_SIGTRAP = 5;
+
+constexpr char GDB_STUB_REPLY_ERR[] = "E01";
+constexpr char GDB_STUB_REPLY_OK[] = "OK";
+constexpr char GDB_STUB_REPLY_EMPTY[] = "";
+
+static u8 CalculateChecksum(std::string_view data) {
+ return std::accumulate(data.begin(), data.end(), u8{0},
+ [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); });
+}
+
+static std::string EscapeGDB(std::string_view data) {
+ std::string escaped;
+ escaped.reserve(data.size());
+
+ for (char c : data) {
+ switch (c) {
+ case '#':
+ escaped += "}\x03";
+ break;
+ case '$':
+ escaped += "}\x04";
+ break;
+ case '*':
+ escaped += "}\x0a";
+ break;
+ case '}':
+ escaped += "}\x5d";
+ break;
+ default:
+ escaped += c;
+ break;
+ }
+ }
+
+ return escaped;
+}
+
+static std::string EscapeXML(std::string_view data) {
+ std::string escaped;
+ escaped.reserve(data.size());
+
+ for (char c : data) {
+ switch (c) {
+ case '&':
+ escaped += "&amp;";
+ break;
+ case '"':
+ escaped += "&quot;";
+ break;
+ case '<':
+ escaped += "&lt;";
+ break;
+ case '>':
+ escaped += "&gt;";
+ break;
+ default:
+ escaped += c;
+ break;
+ }
+ }
+
+ return escaped;
+}
+
+GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_)
+ : DebuggerFrontend(backend_), system{system_} {
+ if (system.CurrentProcess()->Is64BitProcess()) {
+ arch = std::make_unique<GDBStubA64>();
+ } else {
+ arch = std::make_unique<GDBStubA32>();
+ }
+}
+
+GDBStub::~GDBStub() = default;
+
+void GDBStub::Connected() {}
+
+void GDBStub::ShuttingDown() {}
+
+void GDBStub::Stopped(Kernel::KThread* thread) {
+ SendReply(arch->ThreadStatus(thread, GDB_STUB_SIGTRAP));
+}
+
+void GDBStub::Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) {
+ const auto status{arch->ThreadStatus(thread, GDB_STUB_SIGTRAP)};
+
+ switch (watch.type) {
+ case Kernel::DebugWatchpointType::Read:
+ SendReply(fmt::format("{}rwatch:{:x};", status, watch.start_address));
+ break;
+ case Kernel::DebugWatchpointType::Write:
+ SendReply(fmt::format("{}watch:{:x};", status, watch.start_address));
+ break;
+ case Kernel::DebugWatchpointType::ReadOrWrite:
+ default:
+ SendReply(fmt::format("{}awatch:{:x};", status, watch.start_address));
+ break;
+ }
+}
+
+std::vector<DebuggerAction> GDBStub::ClientData(std::span<const u8> data) {
+ std::vector<DebuggerAction> actions;
+ current_command.insert(current_command.end(), data.begin(), data.end());
+
+ while (current_command.size() != 0) {
+ ProcessData(actions);
+ }
+
+ return actions;
+}
+
+void GDBStub::ProcessData(std::vector<DebuggerAction>& actions) {
+ const char c{current_command[0]};
+
+ // Acknowledgement
+ if (c == GDB_STUB_ACK || c == GDB_STUB_NACK) {
+ current_command.erase(current_command.begin());
+ return;
+ }
+
+ // Interrupt
+ if (c == GDB_STUB_INT3) {
+ LOG_INFO(Debug_GDBStub, "Received interrupt");
+ current_command.erase(current_command.begin());
+ actions.push_back(DebuggerAction::Interrupt);
+ SendStatus(GDB_STUB_ACK);
+ return;
+ }
+
+ // Otherwise, require the data to be the start of a command
+ if (c != GDB_STUB_START) {
+ LOG_ERROR(Debug_GDBStub, "Invalid command buffer contents: {}", current_command.data());
+ current_command.clear();
+ SendStatus(GDB_STUB_NACK);
+ return;
+ }
+
+ // Continue reading until command is complete
+ while (CommandEnd() == current_command.end()) {
+ const auto new_data{backend.ReadFromClient()};
+ current_command.insert(current_command.end(), new_data.begin(), new_data.end());
+ }
+
+ // Execute and respond to GDB
+ const auto command{DetachCommand()};
+
+ if (command) {
+ SendStatus(GDB_STUB_ACK);
+ ExecuteCommand(*command, actions);
+ } else {
+ SendStatus(GDB_STUB_NACK);
+ }
+}
+
+void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions) {
+ LOG_TRACE(Debug_GDBStub, "Executing command: {}", packet);
+
+ if (packet.length() == 0) {
+ SendReply(GDB_STUB_REPLY_ERR);
+ return;
+ }
+
+ if (packet.starts_with("vCont")) {
+ HandleVCont(packet.substr(5), actions);
+ return;
+ }
+
+ std::string_view command{packet.substr(1, packet.size())};
+
+ switch (packet[0]) {
+ case 'H': {
+ Kernel::KThread* thread{nullptr};
+ s64 thread_id{strtoll(command.data() + 1, nullptr, 16)};
+ if (thread_id >= 1) {
+ thread = GetThreadByID(thread_id);
+ } else {
+ thread = backend.GetActiveThread();
+ }
+
+ if (thread) {
+ SendReply(GDB_STUB_REPLY_OK);
+ backend.SetActiveThread(thread);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 'T': {
+ s64 thread_id{strtoll(command.data(), nullptr, 16)};
+ if (GetThreadByID(thread_id)) {
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 'Q':
+ case 'q':
+ HandleQuery(command);
+ break;
+ case '?':
+ SendReply(arch->ThreadStatus(backend.GetActiveThread(), GDB_STUB_SIGTRAP));
+ break;
+ case 'k':
+ LOG_INFO(Debug_GDBStub, "Shutting down emulation");
+ actions.push_back(DebuggerAction::ShutdownEmulation);
+ break;
+ case 'g':
+ SendReply(arch->ReadRegisters(backend.GetActiveThread()));
+ break;
+ case 'G':
+ arch->WriteRegisters(backend.GetActiveThread(), command);
+ SendReply(GDB_STUB_REPLY_OK);
+ break;
+ case 'p': {
+ const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ SendReply(arch->RegRead(backend.GetActiveThread(), reg));
+ break;
+ }
+ case 'P': {
+ const auto sep{std::find(command.begin(), command.end(), '=') - command.begin() + 1};
+ const size_t reg{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ arch->RegWrite(backend.GetActiveThread(), reg, std::string_view(command).substr(sep));
+ SendReply(GDB_STUB_REPLY_OK);
+ break;
+ }
+ case 'm': {
+ const auto sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ const size_t size{static_cast<size_t>(strtoll(command.data() + sep, nullptr, 16))};
+
+ if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
+ std::vector<u8> mem(size);
+ system.Memory().ReadBlock(addr, mem.data(), size);
+
+ SendReply(Common::HexToString(mem));
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 'M': {
+ const auto size_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const auto mem_sep{std::find(command.begin(), command.end(), ':') - command.begin() + 1};
+
+ const size_t addr{static_cast<size_t>(strtoll(command.data(), nullptr, 16))};
+ const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
+
+ const auto mem_substr{std::string_view(command).substr(mem_sep)};
+ const auto mem{Common::HexStringToVector(mem_substr, false)};
+
+ if (system.Memory().IsValidVirtualAddressRange(addr, size)) {
+ system.Memory().WriteBlock(addr, mem.data(), size);
+ system.InvalidateCpuInstructionCacheRange(addr, size);
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+ break;
+ }
+ case 's':
+ actions.push_back(DebuggerAction::StepThreadLocked);
+ break;
+ case 'C':
+ case 'c':
+ actions.push_back(DebuggerAction::Continue);
+ break;
+ case 'Z':
+ HandleBreakpointInsert(command);
+ break;
+ case 'z':
+ HandleBreakpointRemove(command);
+ break;
+ default:
+ SendReply(GDB_STUB_REPLY_EMPTY);
+ break;
+ }
+}
+
+enum class BreakpointType {
+ Software = 0,
+ Hardware = 1,
+ WriteWatch = 2,
+ ReadWatch = 3,
+ AccessWatch = 4,
+};
+
+void GDBStub::HandleBreakpointInsert(std::string_view command) {
+ const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
+ const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
+ command.begin() + 1};
+ const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
+ const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
+
+ if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
+ SendReply(GDB_STUB_REPLY_ERR);
+ return;
+ }
+
+ bool success{};
+
+ switch (type) {
+ case BreakpointType::Software:
+ replaced_instructions[addr] = system.Memory().Read32(addr);
+ system.Memory().Write32(addr, arch->BreakpointInstruction());
+ system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
+ success = true;
+ break;
+ case BreakpointType::WriteWatch:
+ success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Write);
+ break;
+ case BreakpointType::ReadWatch:
+ success = system.CurrentProcess()->InsertWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Read);
+ break;
+ case BreakpointType::AccessWatch:
+ success = system.CurrentProcess()->InsertWatchpoint(
+ system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
+ break;
+ case BreakpointType::Hardware:
+ default:
+ SendReply(GDB_STUB_REPLY_EMPTY);
+ return;
+ }
+
+ if (success) {
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+}
+
+void GDBStub::HandleBreakpointRemove(std::string_view command) {
+ const auto type{static_cast<BreakpointType>(strtoll(command.data(), nullptr, 16))};
+ const auto addr_sep{std::find(command.begin(), command.end(), ',') - command.begin() + 1};
+ const auto size_sep{std::find(command.begin() + addr_sep, command.end(), ',') -
+ command.begin() + 1};
+ const size_t addr{static_cast<size_t>(strtoll(command.data() + addr_sep, nullptr, 16))};
+ const size_t size{static_cast<size_t>(strtoll(command.data() + size_sep, nullptr, 16))};
+
+ if (!system.Memory().IsValidVirtualAddressRange(addr, size)) {
+ SendReply(GDB_STUB_REPLY_ERR);
+ return;
+ }
+
+ bool success{};
+
+ switch (type) {
+ case BreakpointType::Software: {
+ const auto orig_insn{replaced_instructions.find(addr)};
+ if (orig_insn != replaced_instructions.end()) {
+ system.Memory().Write32(addr, orig_insn->second);
+ system.InvalidateCpuInstructionCacheRange(addr, sizeof(u32));
+ replaced_instructions.erase(addr);
+ success = true;
+ }
+ break;
+ }
+ case BreakpointType::WriteWatch:
+ success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Write);
+ break;
+ case BreakpointType::ReadWatch:
+ success = system.CurrentProcess()->RemoveWatchpoint(system, addr, size,
+ Kernel::DebugWatchpointType::Read);
+ break;
+ case BreakpointType::AccessWatch:
+ success = system.CurrentProcess()->RemoveWatchpoint(
+ system, addr, size, Kernel::DebugWatchpointType::ReadOrWrite);
+ break;
+ case BreakpointType::Hardware:
+ default:
+ SendReply(GDB_STUB_REPLY_EMPTY);
+ return;
+ }
+
+ if (success) {
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_ERR);
+ }
+}
+
+// Structure offsets are from Atmosphere
+// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp
+
+static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
+ const Kernel::KThread* thread) {
+ // Read thread type from TLS
+ const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)};
+ const VAddr argument_thread_type{thread->GetArgument()};
+
+ if (argument_thread_type && tls_thread_type != argument_thread_type) {
+ // Probably not created by nnsdk, no name available.
+ return std::nullopt;
+ }
+
+ if (!tls_thread_type) {
+ return std::nullopt;
+ }
+
+ const u16 version{memory.Read16(tls_thread_type + 0x26)};
+ VAddr name_pointer{};
+ if (version == 1) {
+ name_pointer = memory.Read32(tls_thread_type + 0xe4);
+ } else {
+ name_pointer = memory.Read32(tls_thread_type + 0xe8);
+ }
+
+ if (!name_pointer) {
+ // No name provided.
+ return std::nullopt;
+ }
+
+ return memory.ReadCString(name_pointer, 256);
+}
+
+static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
+ const Kernel::KThread* thread) {
+ // Read thread type from TLS
+ const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)};
+ const VAddr argument_thread_type{thread->GetArgument()};
+
+ if (argument_thread_type && tls_thread_type != argument_thread_type) {
+ // Probably not created by nnsdk, no name available.
+ return std::nullopt;
+ }
+
+ if (!tls_thread_type) {
+ return std::nullopt;
+ }
+
+ const u16 version{memory.Read16(tls_thread_type + 0x46)};
+ VAddr name_pointer{};
+ if (version == 1) {
+ name_pointer = memory.Read64(tls_thread_type + 0x1a0);
+ } else {
+ name_pointer = memory.Read64(tls_thread_type + 0x1a8);
+ }
+
+ if (!name_pointer) {
+ // No name provided.
+ return std::nullopt;
+ }
+
+ return memory.ReadCString(name_pointer, 256);
+}
+
+static std::optional<std::string> GetThreadName(Core::System& system,
+ const Kernel::KThread* thread) {
+ if (system.CurrentProcess()->Is64BitProcess()) {
+ return GetNameFromThreadType64(system.Memory(), thread);
+ } else {
+ return GetNameFromThreadType32(system.Memory(), thread);
+ }
+}
+
+static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
+ switch (thread->GetWaitReasonForDebugging()) {
+ case Kernel::ThreadWaitReasonForDebugging::Sleep:
+ return "Sleep";
+ case Kernel::ThreadWaitReasonForDebugging::IPC:
+ return "IPC";
+ case Kernel::ThreadWaitReasonForDebugging::Synchronization:
+ return "Synchronization";
+ case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
+ return "ConditionVar";
+ case Kernel::ThreadWaitReasonForDebugging::Arbitration:
+ return "Arbitration";
+ case Kernel::ThreadWaitReasonForDebugging::Suspended:
+ return "Suspended";
+ default:
+ return "Unknown";
+ }
+}
+
+static std::string GetThreadState(const Kernel::KThread* thread) {
+ switch (thread->GetState()) {
+ case Kernel::ThreadState::Initialized:
+ return "Initialized";
+ case Kernel::ThreadState::Waiting:
+ return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
+ case Kernel::ThreadState::Runnable:
+ return "Runnable";
+ case Kernel::ThreadState::Terminated:
+ return "Terminated";
+ default:
+ return "Unknown";
+ }
+}
+
+static std::string PaginateBuffer(std::string_view buffer, std::string_view request) {
+ const auto amount{request.substr(request.find(',') + 1)};
+ const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))};
+ const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))};
+
+ if (offset_val + amount_val > buffer.size()) {
+ return fmt::format("l{}", buffer.substr(offset_val));
+ } else {
+ return fmt::format("m{}", buffer.substr(offset_val, amount_val));
+ }
+}
+
+void GDBStub::HandleQuery(std::string_view command) {
+ if (command.starts_with("TStatus")) {
+ // no tracepoint support
+ SendReply("T0");
+ } else if (command.starts_with("Supported")) {
+ SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;"
+ "vContSupported+;QStartNoAckMode+");
+ } else if (command.starts_with("Xfer:features:read:target.xml:")) {
+ const auto target_xml{arch->GetTargetXML()};
+ SendReply(PaginateBuffer(target_xml, command.substr(30)));
+ } else if (command.starts_with("Offsets")) {
+ Loader::AppLoader::Modules modules;
+ system.GetAppLoader().ReadNSOModules(modules);
+
+ const auto main = std::find_if(modules.begin(), modules.end(),
+ [](const auto& key) { return key.second == "main"; });
+ if (main != modules.end()) {
+ SendReply(fmt::format("TextSeg={:x}", main->first));
+ } else {
+ SendReply(fmt::format("TextSeg={:x}",
+ system.CurrentProcess()->PageTable().GetCodeRegionStart()));
+ }
+ } else if (command.starts_with("Xfer:libraries:read::")) {
+ Loader::AppLoader::Modules modules;
+ system.GetAppLoader().ReadNSOModules(modules);
+
+ std::string buffer;
+ buffer += R"(<?xml version="1.0"?>)";
+ buffer += "<library-list>";
+ for (const auto& [base, name] : modules) {
+ buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)",
+ EscapeXML(name), base);
+ }
+ buffer += "</library-list>";
+
+ SendReply(PaginateBuffer(buffer, command.substr(21)));
+ } else if (command.starts_with("fThreadInfo")) {
+ // beginning of list
+ const auto& threads = system.GlobalSchedulerContext().GetThreadList();
+ std::vector<std::string> thread_ids;
+ for (const auto& thread : threads) {
+ thread_ids.push_back(fmt::format("{:x}", thread->GetThreadID()));
+ }
+ SendReply(fmt::format("m{}", fmt::join(thread_ids, ",")));
+ } else if (command.starts_with("sThreadInfo")) {
+ // end of list
+ SendReply("l");
+ } else if (command.starts_with("Xfer:threads:read::")) {
+ std::string buffer;
+ buffer += R"(<?xml version="1.0"?>)";
+ buffer += "<threads>";
+
+ const auto& threads = system.GlobalSchedulerContext().GetThreadList();
+ for (const auto* thread : threads) {
+ auto thread_name{GetThreadName(system, thread)};
+ if (!thread_name) {
+ thread_name = fmt::format("Thread {:d}", thread->GetThreadID());
+ }
+
+ buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)",
+ thread->GetThreadID(), thread->GetActiveCore(),
+ EscapeXML(*thread_name), GetThreadState(thread));
+ }
+
+ buffer += "</threads>";
+
+ SendReply(PaginateBuffer(buffer, command.substr(19)));
+ } else if (command.starts_with("Attached")) {
+ SendReply("0");
+ } else if (command.starts_with("StartNoAckMode")) {
+ no_ack = true;
+ SendReply(GDB_STUB_REPLY_OK);
+ } else {
+ SendReply(GDB_STUB_REPLY_EMPTY);
+ }
+}
+
+void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions) {
+ if (command == "?") {
+ // Continuing and stepping are supported
+ // (signal is ignored, but required for GDB to use vCont)
+ SendReply("vCont;c;C;s;S");
+ return;
+ }
+
+ Kernel::KThread* stepped_thread{nullptr};
+ bool lock_execution{true};
+
+ std::vector<std::string> entries;
+ boost::split(entries, command.substr(1), boost::is_any_of(";"));
+ for (const auto& thread_action : entries) {
+ std::vector<std::string> parts;
+ boost::split(parts, thread_action, boost::is_any_of(":"));
+
+ if (parts.size() == 1 && (parts[0] == "c" || parts[0].starts_with("C"))) {
+ lock_execution = false;
+ }
+ if (parts.size() == 2 && (parts[0] == "s" || parts[0].starts_with("S"))) {
+ stepped_thread = GetThreadByID(strtoll(parts[1].data(), nullptr, 16));
+ }
+ }
+
+ if (stepped_thread) {
+ backend.SetActiveThread(stepped_thread);
+ actions.push_back(lock_execution ? DebuggerAction::StepThreadLocked
+ : DebuggerAction::StepThreadUnlocked);
+ } else {
+ actions.push_back(DebuggerAction::Continue);
+ }
+}
+
+Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) {
+ const auto& threads{system.GlobalSchedulerContext().GetThreadList()};
+ for (auto* thread : threads) {
+ if (thread->GetThreadID() == thread_id) {
+ return thread;
+ }
+ }
+
+ return nullptr;
+}
+
+std::vector<char>::const_iterator GDBStub::CommandEnd() const {
+ // Find the end marker
+ const auto end{std::find(current_command.begin(), current_command.end(), GDB_STUB_END)};
+
+ // Require the checksum to be present
+ return std::min(end + 2, current_command.end());
+}
+
+std::optional<std::string> GDBStub::DetachCommand() {
+ // Slice the string part from the beginning to the end marker
+ const auto end{CommandEnd()};
+
+ // Extract possible command data
+ std::string data(current_command.data(), end - current_command.begin() + 1);
+
+ // Shift over the remaining contents
+ current_command.erase(current_command.begin(), end + 1);
+
+ // Validate received command
+ if (data[0] != GDB_STUB_START) {
+ LOG_ERROR(Debug_GDBStub, "Invalid start data: {}", data[0]);
+ return std::nullopt;
+ }
+
+ u8 calculated = CalculateChecksum(std::string_view(data).substr(1, data.size() - 4));
+ u8 received = static_cast<u8>(strtoll(data.data() + data.size() - 2, nullptr, 16));
+
+ // Verify checksum
+ if (calculated != received) {
+ LOG_ERROR(Debug_GDBStub, "Checksum mismatch: calculated {:02x}, received {:02x}",
+ calculated, received);
+ return std::nullopt;
+ }
+
+ return data.substr(1, data.size() - 4);
+}
+
+void GDBStub::SendReply(std::string_view data) {
+ const auto escaped{EscapeGDB(data)};
+ const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END,
+ CalculateChecksum(escaped))};
+ LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output);
+
+ // C++ string support is complete rubbish
+ const u8* output_begin = reinterpret_cast<const u8*>(output.data());
+ const u8* output_end = output_begin + output.size();
+ backend.WriteToClient(std::span<const u8>(output_begin, output_end));
+}
+
+void GDBStub::SendStatus(char status) {
+ if (no_ack) {
+ return;
+ }
+
+ std::array<u8, 1> buf = {static_cast<u8>(status)};
+ LOG_TRACE(Debug_GDBStub, "Writing status: {}", status);
+ backend.WriteToClient(buf);
+}
+
+} // namespace Core
diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h
new file mode 100644
index 000000000..0b0f56e4b
--- /dev/null
+++ b/src/core/debugger/gdbstub.h
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <optional>
+#include <string_view>
+#include <vector>
+
+#include "core/debugger/debugger_interface.h"
+#include "core/debugger/gdbstub_arch.h"
+
+namespace Core {
+
+class System;
+
+class GDBStub : public DebuggerFrontend {
+public:
+ explicit GDBStub(DebuggerBackend& backend, Core::System& system);
+ ~GDBStub() override;
+
+ void Connected() override;
+ void Stopped(Kernel::KThread* thread) override;
+ void ShuttingDown() override;
+ void Watchpoint(Kernel::KThread* thread, const Kernel::DebugWatchpoint& watch) override;
+ std::vector<DebuggerAction> ClientData(std::span<const u8> data) override;
+
+private:
+ void ProcessData(std::vector<DebuggerAction>& actions);
+ void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions);
+ void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions);
+ void HandleQuery(std::string_view command);
+ void HandleBreakpointInsert(std::string_view command);
+ void HandleBreakpointRemove(std::string_view command);
+ std::vector<char>::const_iterator CommandEnd() const;
+ std::optional<std::string> DetachCommand();
+ Kernel::KThread* GetThreadByID(u64 thread_id);
+
+ void SendReply(std::string_view data);
+ void SendStatus(char status);
+
+private:
+ Core::System& system;
+ std::unique_ptr<GDBStubArch> arch;
+ std::vector<char> current_command;
+ std::map<VAddr, u32> replaced_instructions;
+ bool no_ack{};
+};
+
+} // namespace Core
diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp
new file mode 100644
index 000000000..4bef09bd7
--- /dev/null
+++ b/src/core/debugger/gdbstub_arch.cpp
@@ -0,0 +1,487 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/hex_util.h"
+#include "core/debugger/gdbstub_arch.h"
+#include "core/hle/kernel/k_thread.h"
+
+namespace Core {
+
+template <typename T>
+static T HexToValue(std::string_view hex) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ T value{};
+ const auto mem{Common::HexStringToVector(hex, false)};
+ std::memcpy(&value, mem.data(), std::min(mem.size(), sizeof(T)));
+ return value;
+}
+
+template <typename T>
+static std::string ValueToHex(const T value) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ std::array<u8, sizeof(T)> mem{};
+ std::memcpy(mem.data(), &value, sizeof(T));
+ return Common::HexToString(mem);
+}
+
+template <typename T>
+static T GetSIMDRegister(const std::array<u32, 64>& simd_regs, size_t offset) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ T value{};
+ std::memcpy(&value, reinterpret_cast<const u8*>(simd_regs.data()) + sizeof(T) * offset,
+ sizeof(T));
+ return value;
+}
+
+template <typename T>
+static void PutSIMDRegister(std::array<u32, 64>& simd_regs, size_t offset, const T value) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ std::memcpy(reinterpret_cast<u8*>(simd_regs.data()) + sizeof(T) * offset, &value, sizeof(T));
+}
+
+// For sample XML files see the GDB source /gdb/features
+// This XML defines what the registers are for this specific ARM device
+std::string GDBStubA64::GetTargetXML() const {
+ constexpr const char* target_xml =
+ R"(<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target version="1.0">
+ <architecture>aarch64</architecture>
+ <feature name="org.gnu.gdb.aarch64.core">
+ <reg name="x0" bitsize="64"/>
+ <reg name="x1" bitsize="64"/>
+ <reg name="x2" bitsize="64"/>
+ <reg name="x3" bitsize="64"/>
+ <reg name="x4" bitsize="64"/>
+ <reg name="x5" bitsize="64"/>
+ <reg name="x6" bitsize="64"/>
+ <reg name="x7" bitsize="64"/>
+ <reg name="x8" bitsize="64"/>
+ <reg name="x9" bitsize="64"/>
+ <reg name="x10" bitsize="64"/>
+ <reg name="x11" bitsize="64"/>
+ <reg name="x12" bitsize="64"/>
+ <reg name="x13" bitsize="64"/>
+ <reg name="x14" bitsize="64"/>
+ <reg name="x15" bitsize="64"/>
+ <reg name="x16" bitsize="64"/>
+ <reg name="x17" bitsize="64"/>
+ <reg name="x18" bitsize="64"/>
+ <reg name="x19" bitsize="64"/>
+ <reg name="x20" bitsize="64"/>
+ <reg name="x21" bitsize="64"/>
+ <reg name="x22" bitsize="64"/>
+ <reg name="x23" bitsize="64"/>
+ <reg name="x24" bitsize="64"/>
+ <reg name="x25" bitsize="64"/>
+ <reg name="x26" bitsize="64"/>
+ <reg name="x27" bitsize="64"/>
+ <reg name="x28" bitsize="64"/>
+ <reg name="x29" bitsize="64"/>
+ <reg name="x30" bitsize="64"/>
+ <reg name="sp" bitsize="64" type="data_ptr"/>
+ <reg name="pc" bitsize="64" type="code_ptr"/>
+ <flags id="cpsr_flags" size="4">
+ <field name="SP" start="0" end="0"/>
+ <field name="" start="1" end="1"/>
+ <field name="EL" start="2" end="3"/>
+ <field name="nRW" start="4" end="4"/>
+ <field name="" start="5" end="5"/>
+ <field name="F" start="6" end="6"/>
+ <field name="I" start="7" end="7"/>
+ <field name="A" start="8" end="8"/>
+ <field name="D" start="9" end="9"/>
+ <field name="IL" start="20" end="20"/>
+ <field name="SS" start="21" end="21"/>
+ <field name="V" start="28" end="28"/>
+ <field name="C" start="29" end="29"/>
+ <field name="Z" start="30" end="30"/>
+ <field name="N" start="31" end="31"/>
+ </flags>
+ <reg name="cpsr" bitsize="32" type="cpsr_flags"/>
+ </feature>
+ <feature name="org.gnu.gdb.aarch64.fpu">
+ <vector id="v2d" type="ieee_double" count="2"/>
+ <vector id="v2u" type="uint64" count="2"/>
+ <vector id="v2i" type="int64" count="2"/>
+ <vector id="v4f" type="ieee_single" count="4"/>
+ <vector id="v4u" type="uint32" count="4"/>
+ <vector id="v4i" type="int32" count="4"/>
+ <vector id="v8u" type="uint16" count="8"/>
+ <vector id="v8i" type="int16" count="8"/>
+ <vector id="v16u" type="uint8" count="16"/>
+ <vector id="v16i" type="int8" count="16"/>
+ <vector id="v1u" type="uint128" count="1"/>
+ <vector id="v1i" type="int128" count="1"/>
+ <union id="vnd">
+ <field name="f" type="v2d"/>
+ <field name="u" type="v2u"/>
+ <field name="s" type="v2i"/>
+ </union>
+ <union id="vns">
+ <field name="f" type="v4f"/>
+ <field name="u" type="v4u"/>
+ <field name="s" type="v4i"/>
+ </union>
+ <union id="vnh">
+ <field name="u" type="v8u"/>
+ <field name="s" type="v8i"/>
+ </union>
+ <union id="vnb">
+ <field name="u" type="v16u"/>
+ <field name="s" type="v16i"/>
+ </union>
+ <union id="vnq">
+ <field name="u" type="v1u"/>
+ <field name="s" type="v1i"/>
+ </union>
+ <union id="aarch64v">
+ <field name="d" type="vnd"/>
+ <field name="s" type="vns"/>
+ <field name="h" type="vnh"/>
+ <field name="b" type="vnb"/>
+ <field name="q" type="vnq"/>
+ </union>
+ <reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
+ <reg name="v1" bitsize="128" type="aarch64v" />
+ <reg name="v2" bitsize="128" type="aarch64v" />
+ <reg name="v3" bitsize="128" type="aarch64v" />
+ <reg name="v4" bitsize="128" type="aarch64v" />
+ <reg name="v5" bitsize="128" type="aarch64v" />
+ <reg name="v6" bitsize="128" type="aarch64v" />
+ <reg name="v7" bitsize="128" type="aarch64v" />
+ <reg name="v8" bitsize="128" type="aarch64v" />
+ <reg name="v9" bitsize="128" type="aarch64v" />
+ <reg name="v10" bitsize="128" type="aarch64v"/>
+ <reg name="v11" bitsize="128" type="aarch64v"/>
+ <reg name="v12" bitsize="128" type="aarch64v"/>
+ <reg name="v13" bitsize="128" type="aarch64v"/>
+ <reg name="v14" bitsize="128" type="aarch64v"/>
+ <reg name="v15" bitsize="128" type="aarch64v"/>
+ <reg name="v16" bitsize="128" type="aarch64v"/>
+ <reg name="v17" bitsize="128" type="aarch64v"/>
+ <reg name="v18" bitsize="128" type="aarch64v"/>
+ <reg name="v19" bitsize="128" type="aarch64v"/>
+ <reg name="v20" bitsize="128" type="aarch64v"/>
+ <reg name="v21" bitsize="128" type="aarch64v"/>
+ <reg name="v22" bitsize="128" type="aarch64v"/>
+ <reg name="v23" bitsize="128" type="aarch64v"/>
+ <reg name="v24" bitsize="128" type="aarch64v"/>
+ <reg name="v25" bitsize="128" type="aarch64v"/>
+ <reg name="v26" bitsize="128" type="aarch64v"/>
+ <reg name="v27" bitsize="128" type="aarch64v"/>
+ <reg name="v28" bitsize="128" type="aarch64v"/>
+ <reg name="v29" bitsize="128" type="aarch64v"/>
+ <reg name="v30" bitsize="128" type="aarch64v"/>
+ <reg name="v31" bitsize="128" type="aarch64v"/>
+ <reg name="fpsr" bitsize="32"/>
+ <reg name="fpcr" bitsize="32"/>
+ </feature>
+</target>)";
+
+ return target_xml;
+}
+
+std::string GDBStubA64::RegRead(const Kernel::KThread* thread, size_t id) const {
+ if (!thread) {
+ return "";
+ }
+
+ const auto& context{thread->GetContext64()};
+ const auto& gprs{context.cpu_registers};
+ const auto& fprs{context.vector_registers};
+
+ if (id < SP_REGISTER) {
+ return ValueToHex(gprs[id]);
+ } else if (id == SP_REGISTER) {
+ return ValueToHex(context.sp);
+ } else if (id == PC_REGISTER) {
+ return ValueToHex(context.pc);
+ } else if (id == PSTATE_REGISTER) {
+ return ValueToHex(context.pstate);
+ } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
+ return ValueToHex(fprs[id - Q0_REGISTER]);
+ } else if (id == FPSR_REGISTER) {
+ return ValueToHex(context.fpsr);
+ } else if (id == FPCR_REGISTER) {
+ return ValueToHex(context.fpcr);
+ } else {
+ return "";
+ }
+}
+
+void GDBStubA64::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
+ if (!thread) {
+ return;
+ }
+
+ auto& context{thread->GetContext64()};
+
+ if (id < SP_REGISTER) {
+ context.cpu_registers[id] = HexToValue<u64>(value);
+ } else if (id == SP_REGISTER) {
+ context.sp = HexToValue<u64>(value);
+ } else if (id == PC_REGISTER) {
+ context.pc = HexToValue<u64>(value);
+ } else if (id == PSTATE_REGISTER) {
+ context.pstate = HexToValue<u32>(value);
+ } else if (id >= Q0_REGISTER && id < FPSR_REGISTER) {
+ context.vector_registers[id - Q0_REGISTER] = HexToValue<u128>(value);
+ } else if (id == FPSR_REGISTER) {
+ context.fpsr = HexToValue<u32>(value);
+ } else if (id == FPCR_REGISTER) {
+ context.fpcr = HexToValue<u32>(value);
+ }
+}
+
+std::string GDBStubA64::ReadRegisters(const Kernel::KThread* thread) const {
+ std::string output;
+
+ for (size_t reg = 0; reg <= FPCR_REGISTER; reg++) {
+ output += RegRead(thread, reg);
+ }
+
+ return output;
+}
+
+void GDBStubA64::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
+ for (size_t i = 0, reg = 0; reg <= FPCR_REGISTER; reg++) {
+ if (reg <= SP_REGISTER || reg == PC_REGISTER) {
+ RegWrite(thread, reg, register_data.substr(i, 16));
+ i += 16;
+ } else if (reg == PSTATE_REGISTER || reg == FPCR_REGISTER || reg == FPSR_REGISTER) {
+ RegWrite(thread, reg, register_data.substr(i, 8));
+ i += 8;
+ } else if (reg >= Q0_REGISTER && reg < FPCR_REGISTER) {
+ RegWrite(thread, reg, register_data.substr(i, 32));
+ i += 32;
+ }
+ }
+}
+
+std::string GDBStubA64::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
+ return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
+ RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
+ LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
+}
+
+u32 GDBStubA64::BreakpointInstruction() const {
+ // A64: brk #0
+ return 0xd4200000;
+}
+
+std::string GDBStubA32::GetTargetXML() const {
+ constexpr const char* target_xml =
+ R"(<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<target version="1.0">
+ <architecture>arm</architecture>
+ <feature name="org.gnu.gdb.arm.core">
+ <reg name="r0" bitsize="32" type="uint32"/>
+ <reg name="r1" bitsize="32" type="uint32"/>
+ <reg name="r2" bitsize="32" type="uint32"/>
+ <reg name="r3" bitsize="32" type="uint32"/>
+ <reg name="r4" bitsize="32" type="uint32"/>
+ <reg name="r5" bitsize="32" type="uint32"/>
+ <reg name="r6" bitsize="32" type="uint32"/>
+ <reg name="r7" bitsize="32" type="uint32"/>
+ <reg name="r8" bitsize="32" type="uint32"/>
+ <reg name="r9" bitsize="32" type="uint32"/>
+ <reg name="r10" bitsize="32" type="uint32"/>
+ <reg name="r11" bitsize="32" type="uint32"/>
+ <reg name="r12" bitsize="32" type="uint32"/>
+ <reg name="sp" bitsize="32" type="data_ptr"/>
+ <reg name="lr" bitsize="32" type="code_ptr"/>
+ <reg name="pc" bitsize="32" type="code_ptr"/>
+ <!-- The CPSR is register 25, rather than register 16, because
+ the FPA registers historically were placed between the PC
+ and the CPSR in the "g" packet. -->
+ <reg name="cpsr" bitsize="32" regnum="25"/>
+ </feature>
+ <feature name="org.gnu.gdb.arm.vfp">
+ <vector id="neon_uint8x8" type="uint8" count="8"/>
+ <vector id="neon_uint16x4" type="uint16" count="4"/>
+ <vector id="neon_uint32x2" type="uint32" count="2"/>
+ <vector id="neon_float32x2" type="ieee_single" count="2"/>
+ <union id="neon_d">
+ <field name="u8" type="neon_uint8x8"/>
+ <field name="u16" type="neon_uint16x4"/>
+ <field name="u32" type="neon_uint32x2"/>
+ <field name="u64" type="uint64"/>
+ <field name="f32" type="neon_float32x2"/>
+ <field name="f64" type="ieee_double"/>
+ </union>
+ <vector id="neon_uint8x16" type="uint8" count="16"/>
+ <vector id="neon_uint16x8" type="uint16" count="8"/>
+ <vector id="neon_uint32x4" type="uint32" count="4"/>
+ <vector id="neon_uint64x2" type="uint64" count="2"/>
+ <vector id="neon_float32x4" type="ieee_single" count="4"/>
+ <vector id="neon_float64x2" type="ieee_double" count="2"/>
+ <union id="neon_q">
+ <field name="u8" type="neon_uint8x16"/>
+ <field name="u16" type="neon_uint16x8"/>
+ <field name="u32" type="neon_uint32x4"/>
+ <field name="u64" type="neon_uint64x2"/>
+ <field name="f32" type="neon_float32x4"/>
+ <field name="f64" type="neon_float64x2"/>
+ </union>
+ <reg name="d0" bitsize="64" type="neon_d" regnum="32"/>
+ <reg name="d1" bitsize="64" type="neon_d"/>
+ <reg name="d2" bitsize="64" type="neon_d"/>
+ <reg name="d3" bitsize="64" type="neon_d"/>
+ <reg name="d4" bitsize="64" type="neon_d"/>
+ <reg name="d5" bitsize="64" type="neon_d"/>
+ <reg name="d6" bitsize="64" type="neon_d"/>
+ <reg name="d7" bitsize="64" type="neon_d"/>
+ <reg name="d8" bitsize="64" type="neon_d"/>
+ <reg name="d9" bitsize="64" type="neon_d"/>
+ <reg name="d10" bitsize="64" type="neon_d"/>
+ <reg name="d11" bitsize="64" type="neon_d"/>
+ <reg name="d12" bitsize="64" type="neon_d"/>
+ <reg name="d13" bitsize="64" type="neon_d"/>
+ <reg name="d14" bitsize="64" type="neon_d"/>
+ <reg name="d15" bitsize="64" type="neon_d"/>
+ <reg name="d16" bitsize="64" type="neon_d"/>
+ <reg name="d17" bitsize="64" type="neon_d"/>
+ <reg name="d18" bitsize="64" type="neon_d"/>
+ <reg name="d19" bitsize="64" type="neon_d"/>
+ <reg name="d20" bitsize="64" type="neon_d"/>
+ <reg name="d21" bitsize="64" type="neon_d"/>
+ <reg name="d22" bitsize="64" type="neon_d"/>
+ <reg name="d23" bitsize="64" type="neon_d"/>
+ <reg name="d24" bitsize="64" type="neon_d"/>
+ <reg name="d25" bitsize="64" type="neon_d"/>
+ <reg name="d26" bitsize="64" type="neon_d"/>
+ <reg name="d27" bitsize="64" type="neon_d"/>
+ <reg name="d28" bitsize="64" type="neon_d"/>
+ <reg name="d29" bitsize="64" type="neon_d"/>
+ <reg name="d30" bitsize="64" type="neon_d"/>
+ <reg name="d31" bitsize="64" type="neon_d"/>
+
+ <reg name="q0" bitsize="128" type="neon_q" regnum="64"/>
+ <reg name="q1" bitsize="128" type="neon_q"/>
+ <reg name="q2" bitsize="128" type="neon_q"/>
+ <reg name="q3" bitsize="128" type="neon_q"/>
+ <reg name="q4" bitsize="128" type="neon_q"/>
+ <reg name="q5" bitsize="128" type="neon_q"/>
+ <reg name="q6" bitsize="128" type="neon_q"/>
+ <reg name="q7" bitsize="128" type="neon_q"/>
+ <reg name="q8" bitsize="128" type="neon_q"/>
+ <reg name="q9" bitsize="128" type="neon_q"/>
+ <reg name="q10" bitsize="128" type="neon_q"/>
+ <reg name="q10" bitsize="128" type="neon_q"/>
+ <reg name="q12" bitsize="128" type="neon_q"/>
+ <reg name="q13" bitsize="128" type="neon_q"/>
+ <reg name="q14" bitsize="128" type="neon_q"/>
+ <reg name="q15" bitsize="128" type="neon_q"/>
+
+ <reg name="fpscr" bitsize="32" type="int" group="float" regnum="80"/>
+ </feature>
+</target>)";
+
+ return target_xml;
+}
+
+std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const {
+ if (!thread) {
+ return "";
+ }
+
+ const auto& context{thread->GetContext32()};
+ const auto& gprs{context.cpu_registers};
+ const auto& fprs{context.extension_registers};
+
+ if (id <= PC_REGISTER) {
+ return ValueToHex(gprs[id]);
+ } else if (id == CPSR_REGISTER) {
+ return ValueToHex(context.cpsr);
+ } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
+ const u64 dN{GetSIMDRegister<u64>(fprs, id - D0_REGISTER)};
+ return ValueToHex(dN);
+ } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
+ const u128 qN{GetSIMDRegister<u128>(fprs, id - Q0_REGISTER)};
+ return ValueToHex(qN);
+ } else if (id == FPSCR_REGISTER) {
+ return ValueToHex(context.fpscr);
+ } else {
+ return "";
+ }
+}
+
+void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const {
+ if (!thread) {
+ return;
+ }
+
+ auto& context{thread->GetContext32()};
+ auto& fprs{context.extension_registers};
+
+ if (id <= PC_REGISTER) {
+ context.cpu_registers[id] = HexToValue<u32>(value);
+ } else if (id == CPSR_REGISTER) {
+ context.cpsr = HexToValue<u32>(value);
+ } else if (id >= D0_REGISTER && id < Q0_REGISTER) {
+ PutSIMDRegister(fprs, id - D0_REGISTER, HexToValue<u64>(value));
+ } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) {
+ PutSIMDRegister(fprs, id - Q0_REGISTER, HexToValue<u128>(value));
+ } else if (id == FPSCR_REGISTER) {
+ context.fpscr = HexToValue<u32>(value);
+ }
+}
+
+std::string GDBStubA32::ReadRegisters(const Kernel::KThread* thread) const {
+ std::string output;
+
+ for (size_t reg = 0; reg <= FPSCR_REGISTER; reg++) {
+ const bool gpr{reg <= PC_REGISTER};
+ const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
+ const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
+
+ if (!(gpr || dfpr || qfpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER)) {
+ continue;
+ }
+
+ output += RegRead(thread, reg);
+ }
+
+ return output;
+}
+
+void GDBStubA32::WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const {
+ for (size_t i = 0, reg = 0; reg <= FPSCR_REGISTER; reg++) {
+ const bool gpr{reg <= PC_REGISTER};
+ const bool dfpr{reg >= D0_REGISTER && reg < Q0_REGISTER};
+ const bool qfpr{reg >= Q0_REGISTER && reg < FPSCR_REGISTER};
+
+ if (gpr || reg == CPSR_REGISTER || reg == FPSCR_REGISTER) {
+ RegWrite(thread, reg, register_data.substr(i, 8));
+ i += 8;
+ } else if (dfpr) {
+ RegWrite(thread, reg, register_data.substr(i, 16));
+ i += 16;
+ } else if (qfpr) {
+ RegWrite(thread, reg, register_data.substr(i, 32));
+ i += 32;
+ }
+
+ if (reg == PC_REGISTER) {
+ reg = CPSR_REGISTER - 1;
+ } else if (reg == CPSR_REGISTER) {
+ reg = D0_REGISTER - 1;
+ }
+ }
+}
+
+std::string GDBStubA32::ThreadStatus(const Kernel::KThread* thread, u8 signal) const {
+ return fmt::format("T{:02x}{:02x}:{};{:02x}:{};{:02x}:{};thread:{:x};", signal, PC_REGISTER,
+ RegRead(thread, PC_REGISTER), SP_REGISTER, RegRead(thread, SP_REGISTER),
+ LR_REGISTER, RegRead(thread, LR_REGISTER), thread->GetThreadID());
+}
+
+u32 GDBStubA32::BreakpointInstruction() const {
+ // A32: trap
+ // T32: trap + b #4
+ return 0xe7ffdefe;
+}
+
+} // namespace Core
diff --git a/src/core/debugger/gdbstub_arch.h b/src/core/debugger/gdbstub_arch.h
new file mode 100644
index 000000000..2540d6456
--- /dev/null
+++ b/src/core/debugger/gdbstub_arch.h
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <string>
+
+#include "common/common_types.h"
+
+namespace Kernel {
+class KThread;
+}
+
+namespace Core {
+
+class GDBStubArch {
+public:
+ virtual ~GDBStubArch() = default;
+ virtual std::string GetTargetXML() const = 0;
+ virtual std::string RegRead(const Kernel::KThread* thread, size_t id) const = 0;
+ virtual void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const = 0;
+ virtual std::string ReadRegisters(const Kernel::KThread* thread) const = 0;
+ virtual void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const = 0;
+ virtual std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const = 0;
+ virtual u32 BreakpointInstruction() const = 0;
+};
+
+class GDBStubA64 final : public GDBStubArch {
+public:
+ std::string GetTargetXML() const override;
+ std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
+ void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
+ std::string ReadRegisters(const Kernel::KThread* thread) const override;
+ void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
+ std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
+ u32 BreakpointInstruction() const override;
+
+private:
+ static constexpr u32 LR_REGISTER = 30;
+ static constexpr u32 SP_REGISTER = 31;
+ static constexpr u32 PC_REGISTER = 32;
+ static constexpr u32 PSTATE_REGISTER = 33;
+ static constexpr u32 Q0_REGISTER = 34;
+ static constexpr u32 FPSR_REGISTER = 66;
+ static constexpr u32 FPCR_REGISTER = 67;
+};
+
+class GDBStubA32 final : public GDBStubArch {
+public:
+ std::string GetTargetXML() const override;
+ std::string RegRead(const Kernel::KThread* thread, size_t id) const override;
+ void RegWrite(Kernel::KThread* thread, size_t id, std::string_view value) const override;
+ std::string ReadRegisters(const Kernel::KThread* thread) const override;
+ void WriteRegisters(Kernel::KThread* thread, std::string_view register_data) const override;
+ std::string ThreadStatus(const Kernel::KThread* thread, u8 signal) const override;
+ u32 BreakpointInstruction() const override;
+
+private:
+ static constexpr u32 SP_REGISTER = 13;
+ static constexpr u32 LR_REGISTER = 14;
+ static constexpr u32 PC_REGISTER = 15;
+ static constexpr u32 CPSR_REGISTER = 25;
+ static constexpr u32 D0_REGISTER = 32;
+ static constexpr u32 Q0_REGISTER = 64;
+ static constexpr u32 FPSCR_REGISTER = 80;
+};
+
+} // namespace Core
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp
index f19c0515f..f8b5be2b4 100644
--- a/src/core/device_memory.cpp
+++ b/src/core/device_memory.cpp
@@ -1,12 +1,14 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/device_memory.h"
+#include "hle/kernel/board/nintendo/nx/k_system_control.h"
namespace Core {
-DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size, 1ULL << 39} {}
+DeviceMemory::DeviceMemory()
+ : buffer{Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize(),
+ 1ULL << 39} {}
DeviceMemory::~DeviceMemory() = default;
} // namespace Core
diff --git a/src/core/device_memory.h b/src/core/device_memory.h
index c4d17705f..df61b0c0b 100644
--- a/src/core/device_memory.h
+++ b/src/core/device_memory.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -12,12 +11,8 @@ namespace Core {
namespace DramMemoryMap {
enum : u64 {
Base = 0x80000000ULL,
- Size = 0x100000000ULL,
- End = Base + Size,
KernelReserveBase = Base + 0x60000,
SlabHeapBase = KernelReserveBase + 0x85000,
- SlapHeapSize = 0xa21000,
- SlabHeapEnd = SlabHeapBase + SlapHeapSize,
};
}; // namespace DramMemoryMap
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index f3891acf1..c750c0da7 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/fs/path_util.h"
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 136485881..26f0c6e5e 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index c6300be59..f23d9373b 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <string>
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 0fd9fa87c..1283f8216 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/common_funcs.h b/src/core/file_sys/common_funcs.h
index 7ed97aa50..3dd8f1244 100644
--- a/src/core/file_sys/common_funcs.h
+++ b/src/core/file_sys/common_funcs.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 7019a7a68..78e56bbbd 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -420,7 +419,7 @@ std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type
Core::Crypto::Mode::ECB);
cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
- Core::Crypto::Key128 out;
+ Core::Crypto::Key128 out{};
if (type == NCASectionCryptoType::XTS) {
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
} else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) {
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index e9eccdea3..7fdc45ea7 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 05936f3c3..be25da2f6 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/string_util.h"
#include "common/swap.h"
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 3e0b45630..75295519c 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 21c7aefc8..a853c00f3 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -1,11 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
-#include <iterator>
#include "common/common_funcs.h"
#include "common/common_types.h"
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index 1a920b45d..7cee0c7df 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,14 +7,14 @@
namespace FileSys {
-constexpr ResultCode ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
-constexpr ResultCode ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2};
-constexpr ResultCode ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
-constexpr ResultCode ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
-constexpr ResultCode ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
-constexpr ResultCode ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
-constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
-constexpr ResultCode ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
-constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
+constexpr Result ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
+constexpr Result ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2};
+constexpr Result ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
+constexpr Result ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
+constexpr Result ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
+constexpr Result ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
+constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
+constexpr Result ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
+constexpr Result ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
} // namespace FileSys
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index 1ca1d536f..1ff83c08c 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -1,26 +1,5 @@
-/*
- * Copyright (c) 2018 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Adapted by DarkLordZach for use/interaction with yuzu
- *
- * Modifications Copyright 2018 yuzu emulator team
- * Licensed under GPLv2 or any later version
- * Refer to the license.txt file included.
- */
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <string_view>
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
index 8d4d89fab..06e5d5a47 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.h
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -1,26 +1,5 @@
-/*
- * Copyright (c) 2018 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Adapted by DarkLordZach for use/interaction with yuzu
- *
- * Modifications Copyright 2018 yuzu emulator team
- * Licensed under GPLv2 or any later version
- * Refer to the license.txt file included.
- */
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index a6101f1c0..5aab428bb 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -218,9 +217,7 @@ void IPSwitchCompiler::Parse() {
break;
} else if (StartsWith(line, "@nsobid-")) {
// NSO Build ID Specifier
- auto raw_build_id = line.substr(8);
- if (raw_build_id.size() != 0x40)
- raw_build_id.resize(0x40, '0');
+ const auto raw_build_id = fmt::format("{:0<64}", line.substr(8));
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
} else if (StartsWith(line, "#")) {
// Mandatory Comment
@@ -288,7 +285,8 @@ void IPSwitchCompiler::Parse() {
std::copy(value.begin(), value.end(), std::back_inserter(replace));
} else {
// hex replacement
- const auto value = patch_line.substr(9);
+ const auto value =
+ patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9);
replace = Common::HexStringToVector(value, is_little_endian);
}
diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h
index 450b2f71e..f2717bae7 100644
--- a/src/core/file_sys/ips_layer.h
+++ b/src/core/file_sys/ips_layer.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
index ef93ef3ed..70c062f4c 100644
--- a/src/core/file_sys/kernel_executable.cpp
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h
index 79ca82f8b..d5b9199b5 100644
--- a/src/core/file_sys/kernel_executable.h
+++ b/src/core/file_sys/kernel_executable.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h
index 6c49a64e2..9596ef4fd 100644
--- a/src/core/file_sys/mode.h
+++ b/src/core/file_sys/mode.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index f5cb4aa8c..52c78020c 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index 75c74ae28..c59ece010 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index b36827b75..2735d053b 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
@@ -51,7 +50,7 @@ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockTyp
low = mid + 1;
}
}
- UNREACHABLE_MSG("Offset could not be found in BKTR block.");
+ ASSERT_MSG(false, "Offset could not be found in BKTR block.");
return {0, 0};
}
} // Anonymous namespace
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h
index 503cf473e..595e3ef09 100644
--- a/src/core/file_sys/nca_patch.h
+++ b/src/core/file_sys/nca_patch.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index c5967049e..2527ae606 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstddef>
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index 0f831148e..b6e3a2b0c 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 7c0950bb0..4c80e13a9 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
@@ -10,7 +9,10 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/settings.h"
+#ifndef _WIN32
#include "common/string_util.h"
+#endif
+
#include "core/core.h"
#include "core/file_sys/common_funcs.h"
#include "core/file_sys/content_archive.h"
@@ -128,15 +130,6 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (exefs == nullptr)
return exefs;
- if (Settings::values.dump_exefs) {
- LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
- const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
- if (dump_dir != nullptr) {
- const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
- VfsRawCopyD(exefs, exefs_dir);
- }
- }
-
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
@@ -154,28 +147,41 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
// LayeredExeFS
const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
+ const auto sdmc_load_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
+
+ std::vector<VirtualDir> patch_dirs = {sdmc_load_dir};
if (load_dir != nullptr && load_dir->GetSize() > 0) {
- auto patch_dirs = load_dir->GetSubdirectories();
- std::sort(
- patch_dirs.begin(), patch_dirs.end(),
- [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
-
- std::vector<VirtualDir> layers;
- layers.reserve(patch_dirs.size() + 1);
- for (const auto& subdir : patch_dirs) {
- if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
- continue;
+ const auto load_patch_dirs = load_dir->GetSubdirectories();
+ patch_dirs.insert(patch_dirs.end(), load_patch_dirs.begin(), load_patch_dirs.end());
+ }
- auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
- if (exefs_dir != nullptr)
- layers.push_back(std::move(exefs_dir));
- }
- layers.push_back(exefs);
+ std::sort(patch_dirs.begin(), patch_dirs.end(),
+ [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
- auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
- if (layered != nullptr) {
- LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
- exefs = std::move(layered);
+ std::vector<VirtualDir> layers;
+ layers.reserve(patch_dirs.size() + 1);
+ for (const auto& subdir : patch_dirs) {
+ if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end())
+ continue;
+
+ auto exefs_dir = FindSubdirectoryCaseless(subdir, "exefs");
+ if (exefs_dir != nullptr)
+ layers.push_back(std::move(exefs_dir));
+ }
+ layers.push_back(exefs);
+
+ auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
+ if (layered != nullptr) {
+ LOG_INFO(Loader, " ExeFS: LayeredExeFS patches applied successfully");
+ exefs = std::move(layered);
+ }
+
+ if (Settings::values.dump_exefs) {
+ LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
+ const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
+ if (dump_dir != nullptr) {
+ const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
+ VfsRawCopyD(exefs, exefs_dir);
}
}
@@ -185,6 +191,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const {
const auto& disabled = Settings::values.disabled_addons[title_id];
+ const auto nso_build_id = fmt::format("{:0<64}", build_id);
std::vector<VirtualFile> out;
out.reserve(patch_dirs.size());
@@ -197,21 +204,18 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips") {
auto name = file->GetName();
- const auto p1 = name.substr(0, name.find('.'));
- const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
- if (build_id == this_build_id)
+ const auto this_build_id =
+ fmt::format("{:0<64}", name.substr(0, name.find('.')));
+ if (nso_build_id == this_build_id)
out.push_back(file);
} else if (file->GetExtension() == "pchtxt") {
IPSwitchCompiler compiler{file};
if (!compiler.IsValid())
continue;
- auto this_build_id = Common::HexToString(compiler.GetBuildID());
- this_build_id =
- this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
-
- if (build_id == this_build_id)
+ const auto this_build_id = Common::HexToString(compiler.GetBuildID());
+ if (nso_build_id == this_build_id)
out.push_back(file);
}
}
@@ -533,11 +537,20 @@ PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile u
// SDMC mod directory (RomFS LayeredFS)
const auto sdmc_mod_dir = fs_controller.GetSDMCModificationLoadRoot(title_id);
- if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0 &&
- IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
- const auto mod_disabled =
- std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
- out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", "LayeredFS");
+ if (sdmc_mod_dir != nullptr && sdmc_mod_dir->GetSize() > 0) {
+ std::string types;
+ if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "exefs"))) {
+ AppendCommaIfNotEmpty(types, "LayeredExeFS");
+ }
+ if (IsDirValidAndNonEmpty(FindSubdirectoryCaseless(sdmc_mod_dir, "romfs"))) {
+ AppendCommaIfNotEmpty(types, "LayeredFS");
+ }
+
+ if (!types.empty()) {
+ const auto mod_disabled =
+ std::find(disabled.begin(), disabled.end(), "SDMC") != disabled.end();
+ out.insert_or_assign(mod_disabled ? "[D] SDMC" : "SDMC", types);
+ }
}
// DLC
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 3be871f35..69d15e2f8 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 484d4baea..08d489eab 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstddef>
#include <vector>
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index c89a1c445..2e8960b07 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -1,12 +1,13 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
+
#include "common/bit_field.h"
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#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..878d832c2 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <random>
@@ -109,7 +108,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
// TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
return ContentRecordType::HtmlDocument;
default:
- UNREACHABLE_MSG("Invalid NCAContentType={:02X}", type);
+ ASSERT_MSG(false, "Invalid NCAContentType={:02X}", type);
return ContentRecordType{};
}
}
@@ -387,15 +386,17 @@ std::vector<NcaID> RegisteredCache::AccumulateFiles() const {
continue;
for (const auto& nca_dir : d2_dir->GetSubdirectories()) {
- if (!FollowsNcaIdFormat(nca_dir->GetName()))
+ if (nca_dir == nullptr || !FollowsNcaIdFormat(nca_dir->GetName())) {
continue;
+ }
ids.push_back(Common::HexStringToArray<0x10, true>(nca_dir->GetName().substr(0, 0x20)));
}
for (const auto& nca_file : d2_dir->GetFiles()) {
- if (!FollowsNcaIdFormat(nca_file->GetName()))
+ if (nca_file == nullptr || !FollowsNcaIdFormat(nca_file->GetName())) {
continue;
+ }
ids.push_back(
Common::HexStringToArray<0x10, true>(nca_file->GetName().substr(0, 0x20)));
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index d042aef90..587f8cae8 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index 120032134..ddcfe5980 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index 82e683782..5d7f0c2a8 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 291b746b6..ae7a3511b 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include "common/assert.h"
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index 2c93a49a5..14936031f 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index e6f8514c9..8c1b2523c 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include "common/assert.h"
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index de415b0c4..a763b94c8 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index c0e13e56f..1df022c9e 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include "core/file_sys/registered_cache.h"
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 3a3d11f3a..3aebfb25e 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index f03124e3d..c90e6e372 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 030f36c09..3226b884a 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/data/font_chinese_simplified.cpp b/src/core/file_sys/system_archive/data/font_chinese_simplified.cpp
index a676867e5..a1daac3a9 100644
--- a/src/core/file_sys/system_archive/data/font_chinese_simplified.cpp
+++ b/src/core/file_sys/system_archive/data/font_chinese_simplified.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/data/font_chinese_simplified.h"
diff --git a/src/core/file_sys/system_archive/data/font_chinese_simplified.h b/src/core/file_sys/system_archive/data/font_chinese_simplified.h
index 161de4542..33932c456 100644
--- a/src/core/file_sys/system_archive/data/font_chinese_simplified.h
+++ b/src/core/file_sys/system_archive/data/font_chinese_simplified.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/data/font_chinese_traditional.cpp b/src/core/file_sys/system_archive/data/font_chinese_traditional.cpp
index cc70a4032..9b92ec0ff 100644
--- a/src/core/file_sys/system_archive/data/font_chinese_traditional.cpp
+++ b/src/core/file_sys/system_archive/data/font_chinese_traditional.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/data/font_chinese_traditional.h"
diff --git a/src/core/file_sys/system_archive/data/font_chinese_traditional.h b/src/core/file_sys/system_archive/data/font_chinese_traditional.h
index b16c8e550..c2ebd4d96 100644
--- a/src/core/file_sys/system_archive/data/font_chinese_traditional.h
+++ b/src/core/file_sys/system_archive/data/font_chinese_traditional.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp b/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp
index 18b8c87e7..9e43ef2c1 100644
--- a/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp
+++ b/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/data/font_extended_chinese_simplified.h"
diff --git a/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h b/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h
index de8ac57ac..d640cf755 100644
--- a/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h
+++ b/src/core/file_sys/system_archive/data/font_extended_chinese_simplified.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/data/font_korean.cpp b/src/core/file_sys/system_archive/data/font_korean.cpp
index ae9228958..cba7fe0f8 100644
--- a/src/core/file_sys/system_archive/data/font_korean.cpp
+++ b/src/core/file_sys/system_archive/data/font_korean.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/data/font_korean.h"
diff --git a/src/core/file_sys/system_archive/data/font_korean.h b/src/core/file_sys/system_archive/data/font_korean.h
index e1b02c4e5..1d9ab4b1f 100644
--- a/src/core/file_sys/system_archive/data/font_korean.h
+++ b/src/core/file_sys/system_archive/data/font_korean.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/data/font_nintendo_extended.cpp b/src/core/file_sys/system_archive/data/font_nintendo_extended.cpp
index 29ef110a6..eaa8ec254 100644
--- a/src/core/file_sys/system_archive/data/font_nintendo_extended.cpp
+++ b/src/core/file_sys/system_archive/data/font_nintendo_extended.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/data/font_nintendo_extended.h"
diff --git a/src/core/file_sys/system_archive/data/font_nintendo_extended.h b/src/core/file_sys/system_archive/data/font_nintendo_extended.h
index edb9df914..247ebb5af 100644
--- a/src/core/file_sys/system_archive/data/font_nintendo_extended.h
+++ b/src/core/file_sys/system_archive/data/font_nintendo_extended.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/data/font_standard.cpp b/src/core/file_sys/system_archive/data/font_standard.cpp
index 8f4d8448e..b71ba8a83 100644
--- a/src/core/file_sys/system_archive/data/font_standard.cpp
+++ b/src/core/file_sys/system_archive/data/font_standard.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/data/font_standard.h"
diff --git a/src/core/file_sys/system_archive/data/font_standard.h b/src/core/file_sys/system_archive/data/font_standard.h
index 757593c4b..ab44108d6 100644
--- a/src/core/file_sys/system_archive/data/font_standard.h
+++ b/src/core/file_sys/system_archive/data/font_standard.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp
index d65c7d234..5c87b42f8 100644
--- a/src/core/file_sys/system_archive/mii_model.cpp
+++ b/src/core/file_sys/system_archive/mii_model.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/mii_model.h"
#include "core/file_sys/vfs_vector.h"
diff --git a/src/core/file_sys/system_archive/mii_model.h b/src/core/file_sys/system_archive/mii_model.h
index 6c2d9398b..b6cbefe24 100644
--- a/src/core/file_sys/system_archive/mii_model.h
+++ b/src/core/file_sys/system_archive/mii_model.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp
index 8d86d563a..5cf6749da 100644
--- a/src/core/file_sys/system_archive/ng_word.cpp
+++ b/src/core/file_sys/system_archive/ng_word.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/common_types.h"
diff --git a/src/core/file_sys/system_archive/ng_word.h b/src/core/file_sys/system_archive/ng_word.h
index cd81e0abb..1d7b49532 100644
--- a/src/core/file_sys/system_archive/ng_word.h
+++ b/src/core/file_sys/system_archive/ng_word.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp
index c5cdf7d9b..3210583f0 100644
--- a/src/core/file_sys/system_archive/shared_font.cpp
+++ b/src/core/file_sys/system_archive/shared_font.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/data/font_chinese_simplified.h"
#include "core/file_sys/system_archive/data/font_chinese_traditional.h"
@@ -10,7 +9,7 @@
#include "core/file_sys/system_archive/data/font_standard.h"
#include "core/file_sys/system_archive/shared_font.h"
#include "core/file_sys/vfs_vector.h"
-#include "core/hle/service/ns/pl_u.h"
+#include "core/hle/service/ns/iplatform_service_manager.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/file_sys/system_archive/shared_font.h b/src/core/file_sys/system_archive/shared_font.h
index 6d8de565b..d1cd1dc44 100644
--- a/src/core/file_sys/system_archive/shared_font.h
+++ b/src/core/file_sys/system_archive/shared_font.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp
index a6696024e..6abac793b 100644
--- a/src/core/file_sys/system_archive/system_archive.cpp
+++ b/src/core/file_sys/system_archive/system_archive.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/file_sys/romfs.h"
diff --git a/src/core/file_sys/system_archive/system_archive.h b/src/core/file_sys/system_archive/system_archive.h
index 724a8eb17..02d9157bb 100644
--- a/src/core/file_sys/system_archive/system_archive.h
+++ b/src/core/file_sys/system_archive/system_archive.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/system_version.cpp b/src/core/file_sys/system_archive/system_version.cpp
index 9b76d007e..bd493ecca 100644
--- a/src/core/file_sys/system_archive/system_version.cpp
+++ b/src/core/file_sys/system_archive/system_version.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/system_archive/system_version.h"
#include "core/file_sys/vfs_vector.h"
diff --git a/src/core/file_sys/system_archive/system_version.h b/src/core/file_sys/system_archive/system_version.h
index deed79b26..21b5514a9 100644
--- a/src/core/file_sys/system_archive/system_version.h
+++ b/src/core/file_sys/system_archive/system_version.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp
index 8fd005012..85383998d 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.cpp
+++ b/src/core/file_sys/system_archive/time_zone_binary.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <vector>
diff --git a/src/core/file_sys/system_archive/time_zone_binary.h b/src/core/file_sys/system_archive/time_zone_binary.h
index 266c23537..d0e1a4acd 100644
--- a/src/core/file_sys/system_archive/time_zone_binary.h
+++ b/src/core/file_sys/system_archive/time_zone_binary.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index f5ad10b15..0f6618b31 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <numeric>
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 1b9365853..8fc1738a4 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index 5f8c09124..d23623aa0 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <utility>
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index bd091451e..9be0261b6 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
index e093c4db2..da05dd395 100644
--- a/src/core/file_sys/vfs_layered.cpp
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <utility>
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h
index cd6baf28c..a62112e9d 100644
--- a/src/core/file_sys/vfs_layered.h
+++ b/src/core/file_sys/vfs_layered.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index 870ed1cf8..d950a6633 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <utility>
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index 7ce1eb336..6c051ca00 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index f4073b76a..cc0076238 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstddef>
@@ -145,7 +144,7 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_
LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path);
}
} else {
- UNREACHABLE();
+ ASSERT(false);
return nullptr;
}
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 746e624cb..acde1ac89 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index f5b66cf71..ca3f989ef 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/vfs_types.h b/src/core/file_sys/vfs_types.h
index ed0724717..4a583ed64 100644
--- a/src/core/file_sys/vfs_types.h
+++ b/src/core/file_sys/vfs_types.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index f64b88639..251d9d7c9 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <utility>
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index 73f180070..bfedb6e42 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index d6fe1af47..ede0aa11a 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 63a032b68..abbe5f716 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
index e1033b634..6c230f619 100644
--- a/src/core/frontend/applets/controller.cpp
+++ b/src/core/frontend/applets/controller.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
@@ -66,7 +65,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
controller->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
controller->Connect(true);
} else {
- UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
+ ASSERT_MSG(false, "Unable to add a new controller based on the given parameters!");
}
}
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index 014bc8901..1d2850ad5 100644
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/frontend/applets/error.cpp b/src/core/frontend/applets/error.cpp
index dceb20ff8..f8b961098 100644
--- a/src/core/frontend/applets/error.cpp
+++ b/src/core/frontend/applets/error.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/frontend/applets/error.h"
@@ -9,12 +8,12 @@ namespace Core::Frontend {
ErrorApplet::~ErrorApplet() = default;
-void DefaultErrorApplet::ShowError(ResultCode error, std::function<void()> finished) const {
+void DefaultErrorApplet::ShowError(Result error, std::function<void()> finished) const {
LOG_CRITICAL(Service_Fatal, "Application requested error display: {:04}-{:04} (raw={:08X})",
error.module.Value(), error.description.Value(), error.raw);
}
-void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+void DefaultErrorApplet::ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const {
LOG_CRITICAL(
Service_Fatal,
@@ -22,7 +21,7 @@ void DefaultErrorApplet::ShowErrorWithTimestamp(ResultCode error, std::chrono::s
error.module.Value(), error.description.Value(), error.raw, time.count());
}
-void DefaultErrorApplet::ShowCustomErrorText(ResultCode error, std::string main_text,
+void DefaultErrorApplet::ShowCustomErrorText(Result error, std::string main_text,
std::string detail_text,
std::function<void()> finished) const {
LOG_CRITICAL(Service_Fatal,
diff --git a/src/core/frontend/applets/error.h b/src/core/frontend/applets/error.h
index 699df940d..f378f8805 100644
--- a/src/core/frontend/applets/error.h
+++ b/src/core/frontend/applets/error.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -15,22 +14,22 @@ class ErrorApplet {
public:
virtual ~ErrorApplet();
- virtual void ShowError(ResultCode error, std::function<void()> finished) const = 0;
+ virtual void ShowError(Result error, std::function<void()> finished) const = 0;
- virtual void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+ virtual void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const = 0;
- virtual void ShowCustomErrorText(ResultCode error, std::string dialog_text,
+ virtual void ShowCustomErrorText(Result error, std::string dialog_text,
std::string fullscreen_text,
std::function<void()> finished) const = 0;
};
class DefaultErrorApplet final : public ErrorApplet {
public:
- void ShowError(ResultCode error, std::function<void()> finished) const override;
- void ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
+ void ShowError(Result error, std::function<void()> finished) const override;
+ void ShowErrorWithTimestamp(Result error, std::chrono::seconds time,
std::function<void()> finished) const override;
- void ShowCustomErrorText(ResultCode error, std::string main_text, std::string detail_text,
+ void ShowCustomErrorText(Result error, std::string main_text, std::string detail_text,
std::function<void()> finished) const override;
};
diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp
index 7483ffb76..29a00fb6f 100644
--- a/src/core/frontend/applets/general_frontend.cpp
+++ b/src/core/frontend/applets/general_frontend.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/frontend/applets/general_frontend.h"
diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h
index 1647aa975..cbec8b4ad 100644
--- a/src/core/frontend/applets/general_frontend.h
+++ b/src/core/frontend/applets/general_frontend.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/frontend/applets/mii_edit.cpp b/src/core/frontend/applets/mii_edit.cpp
new file mode 100644
index 000000000..d37b5368a
--- /dev/null
+++ b/src/core/frontend/applets/mii_edit.cpp
@@ -0,0 +1,17 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "core/frontend/applets/mii_edit.h"
+
+namespace Core::Frontend {
+
+MiiEditApplet::~MiiEditApplet() = default;
+
+void DefaultMiiEditApplet::ShowMiiEdit(const std::function<void()>& callback) const {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ callback();
+}
+
+} // 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..58fa2039b
--- /dev/null
+++ b/src/core/frontend/applets/mii_edit.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <functional>
+
+namespace Core::Frontend {
+
+class MiiEditApplet {
+public:
+ virtual ~MiiEditApplet();
+
+ virtual void ShowMiiEdit(const std::function<void()>& callback) const = 0;
+};
+
+class DefaultMiiEditApplet final : public MiiEditApplet {
+public:
+ void ShowMiiEdit(const std::function<void()>& callback) const override;
+};
+
+} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/profile_select.cpp b/src/core/frontend/applets/profile_select.cpp
index 4c58c310f..d11fbce0a 100644
--- a/src/core/frontend/applets/profile_select.cpp
+++ b/src/core/frontend/applets/profile_select.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/frontend/applets/profile_select.h"
diff --git a/src/core/frontend/applets/profile_select.h b/src/core/frontend/applets/profile_select.h
index 3506b9885..8d6ee5279 100644
--- a/src/core/frontend/applets/profile_select.h
+++ b/src/core/frontend/applets/profile_select.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/frontend/applets/software_keyboard.cpp b/src/core/frontend/applets/software_keyboard.cpp
index c4863ee73..020c7fa5e 100644
--- a/src/core/frontend/applets/software_keyboard.cpp
+++ b/src/core/frontend/applets/software_keyboard.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
index 490c55cc2..094d1e713 100644
--- a/src/core/frontend/applets/software_keyboard.h
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -18,6 +17,8 @@ struct KeyboardInitializeParameters {
std::u16string sub_text;
std::u16string guide_text;
std::u16string initial_text;
+ char16_t left_optional_symbol_key;
+ char16_t right_optional_symbol_key;
u32 max_text_length;
u32 min_text_length;
s32 initial_cursor_position;
diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp
index be4736f47..27c7086be 100644
--- a/src/core/frontend/applets/web_browser.cpp
+++ b/src/core/frontend/applets/web_browser.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/frontend/applets/web_browser.h"
diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h
index b6a60c994..1411274f8 100644
--- a/src/core/frontend/applets/web_browser.h
+++ b/src/core/frontend/applets/web_browser.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 57c6ffc43..1be2dccb0 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "core/frontend/emu_window.h"
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index e413a520a..ac1906d5e 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -42,11 +41,20 @@ public:
context.MakeCurrent();
}
~Scoped() {
- context.DoneCurrent();
+ if (active) {
+ context.DoneCurrent();
+ }
+ }
+
+ /// In the event that context was destroyed before the Scoped is destroyed, this provides a
+ /// mechanism to prevent calling a destroyed object's method during the deconstructor
+ void Cancel() {
+ active = false;
}
private:
GraphicsContext& context;
+ bool active{true};
};
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 26a5b12aa..90dd68ff1 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cmath>
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 8e341e4e2..1561d994e 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp
deleted file mode 100644
index 290db505e..000000000
--- a/src/core/hardware_interrupt_manager.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2019 Yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "core/core.h"
-#include "core/core_timing.h"
-#include "core/hardware_interrupt_manager.h"
-#include "core/hle/service/nvdrv/nvdrv_interface.h"
-#include "core/hle/service/sm/sm.h"
-
-namespace Core::Hardware {
-
-InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
- gpu_interrupt_event = Core::Timing::CreateEvent(
- "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) {
- auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
- const u32 syncpt = static_cast<u32>(message >> 32);
- const u32 value = static_cast<u32>(message);
- nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
- });
-}
-
-InterruptManager::~InterruptManager() = default;
-
-void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
- const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value;
- system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg);
-}
-
-} // namespace Core::Hardware
diff --git a/src/core/hardware_interrupt_manager.h b/src/core/hardware_interrupt_manager.h
deleted file mode 100644
index 5fa306ae0..000000000
--- a/src/core/hardware_interrupt_manager.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2019 Yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-
-#include "common/common_types.h"
-
-namespace Core {
-class System;
-}
-
-namespace Core::Timing {
-struct EventType;
-}
-
-namespace Core::Hardware {
-
-class InterruptManager {
-public:
- explicit InterruptManager(Core::System& system);
- ~InterruptManager();
-
- void GPUInterruptSyncpt(u32 syncpoint_id, u32 value);
-
-private:
- Core::System& system;
- std::shared_ptr<Core::Timing::EventType> gpu_interrupt_event;
-};
-
-} // namespace Core::Hardware
diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h
index 176a72c67..13cbdb734 100644
--- a/src/core/hardware_properties.h
+++ b/src/core/hardware_properties.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -26,6 +25,9 @@ constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
};
+// Cortex-A57 supports 4 memory watchpoints
+constexpr u64 NUM_WATCHPOINTS = 4;
+
} // namespace Hardware
} // namespace Core
diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index eef0ff493..aac45907d 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/settings.h"
#include "core/hid/emulated_console.h"
@@ -28,12 +27,19 @@ void EmulatedConsole::SetTouchParams() {
// We can't use mouse as touch if native mouse is enabled
touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
}
- touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0"};
- touch_params[index++] = Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1"};
+
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
+ touch_params[index++] =
+ Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
touch_params[index++] =
- Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
touch_params[index++] =
- Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
+ Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
const auto button_index =
static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
@@ -132,7 +138,7 @@ void EmulatedConsole::SetMotionParam(Common::ParamPackage param) {
}
void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
auto& raw_status = console.motion_values.raw_status;
auto& emulated = console.motion_values.emulated;
@@ -151,6 +157,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
emulated.UpdateOrientation(raw_status.delta_timestamp);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
return;
}
@@ -166,6 +173,7 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
// Find what is this value
motion.verticalization_error = 0.0f;
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Motion);
}
@@ -173,11 +181,12 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
if (index >= console.touch_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
console.touch_values[index] = TransformToTouch(callback);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
return;
}
@@ -189,26 +198,32 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
.pressed = console.touch_values[index].pressed.value,
};
+ lock.unlock();
TriggerOnChange(ConsoleTriggerType::Touch);
}
ConsoleMotionValues EmulatedConsole::GetMotionValues() const {
+ std::scoped_lock lock{mutex};
return console.motion_values;
}
TouchValues EmulatedConsole::GetTouchValues() const {
+ std::scoped_lock lock{mutex};
return console.touch_values;
}
ConsoleMotion EmulatedConsole::GetMotion() const {
+ std::scoped_lock lock{mutex};
return console.motion_state;
}
TouchFingerState EmulatedConsole::GetTouch() const {
+ std::scoped_lock lock{mutex};
return console.touch_state;
}
void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ConsoleUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
@@ -218,13 +233,13 @@ void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
}
int EmulatedConsole::SetCallback(ConsoleUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, update_callback);
return last_callback_key++;
}
void EmulatedConsole::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
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..1c510cd19 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -183,6 +182,7 @@ private:
TouchDevices touch_devices;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, ConsoleUpdateCallback> callback_list;
int last_callback_key = 0;
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 2bee173b3..025f1c78e 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -1,7 +1,7 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/thread.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/input_converter.h"
@@ -85,23 +85,26 @@ void EmulatedController::ReloadFromSettings() {
motion_params[index] = Common::ParamPackage(player.motions[index]);
}
+ controller.colors_state.fullkey = {
+ .body = GetNpadColor(player.body_color_left),
+ .button = GetNpadColor(player.button_color_left),
+ };
controller.colors_state.left = {
- .body = player.body_color_left,
- .button = player.button_color_left,
+ .body = GetNpadColor(player.body_color_left),
+ .button = GetNpadColor(player.button_color_left),
};
-
controller.colors_state.right = {
- .body = player.body_color_right,
- .button = player.button_color_right,
+ .body = GetNpadColor(player.body_color_right),
+ .button = GetNpadColor(player.button_color_right),
};
- controller.colors_state.fullkey = controller.colors_state.left;
-
// Other or debug controller should always be a pro controller
if (npad_id_type != NpadIdType::Other) {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
+ original_npad_type = npad_type;
} else {
SetNpadStyleIndex(NpadStyleIndex::ProController);
+ original_npad_type = npad_type;
}
if (player.connected) {
@@ -127,10 +130,17 @@ void EmulatedController::LoadDevices() {
battery_params[LeftIndex].Set("battery", true);
battery_params[RightIndex].Set("battery", true);
+ camera_params = Common::ParamPackage{"engine:camera,camera:1"};
+ nfc_params = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};
+
output_params[LeftIndex] = left_joycon;
output_params[RightIndex] = right_joycon;
+ output_params[2] = camera_params;
+ output_params[3] = nfc_params;
output_params[LeftIndex].Set("output", true);
output_params[RightIndex].Set("output", true);
+ output_params[2].Set("output", true);
+ output_params[3].Set("output", true);
LoadTASParams();
@@ -147,6 +157,8 @@ void EmulatedController::LoadDevices() {
Common::Input::CreateDevice<Common::Input::InputDevice>);
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
Common::Input::CreateDevice<Common::Input::InputDevice>);
+ camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
+ nfc_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(nfc_params);
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
Common::Input::CreateDevice<Common::Input::OutputDevice>);
@@ -268,6 +280,24 @@ void EmulatedController::ReloadInput() {
motion_devices[index]->ForceUpdate();
}
+ if (camera_devices) {
+ camera_devices->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
+ });
+ camera_devices->ForceUpdate();
+ }
+
+ if (nfc_devices) {
+ if (npad_id_type == NpadIdType::Handheld || npad_id_type == NpadIdType::Player1) {
+ nfc_devices->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetNfc(callback); },
+ });
+ nfc_devices->ForceUpdate();
+ }
+ }
+
// Use a common UUID for TAS
static constexpr Common::UUID TAS_UUID = Common::UUID{
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
@@ -323,6 +353,8 @@ void EmulatedController::UnloadInput() {
for (auto& stick : tas_stick_devices) {
stick.reset();
}
+ camera_devices.reset();
+ nfc_devices.reset();
}
void EmulatedController::EnableConfiguration() {
@@ -340,6 +372,7 @@ void EmulatedController::DisableConfiguration() {
Disconnect();
}
SetNpadStyleIndex(tmp_npad_type);
+ original_npad_type = tmp_npad_type;
}
// Apply temporary connected status to the real controller
@@ -353,14 +386,17 @@ void EmulatedController::DisableConfiguration() {
}
void EmulatedController::EnableSystemButtons() {
+ std::scoped_lock lock{mutex};
system_buttons_enabled = true;
}
void EmulatedController::DisableSystemButtons() {
+ std::scoped_lock lock{mutex};
system_buttons_enabled = false;
}
void EmulatedController::ResetSystemButtons() {
+ std::scoped_lock lock{mutex};
controller.home_button_state.home.Assign(false);
controller.capture_button_state.capture.Assign(false);
}
@@ -494,139 +530,151 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
if (index >= controller.button_values.size()) {
return;
}
- {
- std::lock_guard lock{mutex};
- bool value_changed = false;
- const auto new_status = TransformToButton(callback);
- auto& current_status = controller.button_values[index];
+ std::unique_lock lock{mutex};
+ bool value_changed = false;
+ const auto new_status = TransformToButton(callback);
+ auto& current_status = controller.button_values[index];
- // Only read button values that have the same uuid or are pressed once
- if (current_status.uuid != uuid) {
- if (!new_status.value) {
- return;
- }
+ // Only read button values that have the same uuid or are pressed once
+ if (current_status.uuid != uuid) {
+ if (!new_status.value) {
+ return;
}
+ }
- current_status.toggle = new_status.toggle;
- current_status.uuid = uuid;
+ current_status.toggle = new_status.toggle;
+ current_status.uuid = uuid;
- // Update button status with current
- if (!current_status.toggle) {
- current_status.locked = false;
- if (current_status.value != new_status.value) {
- current_status.value = new_status.value;
- value_changed = true;
- }
- } else {
- // Toggle button and lock status
- if (new_status.value && !current_status.locked) {
- current_status.locked = true;
- current_status.value = !current_status.value;
- value_changed = true;
- }
+ // Update button status with current
+ if (!current_status.toggle) {
+ current_status.locked = false;
+ if (current_status.value != new_status.value) {
+ current_status.value = new_status.value;
+ value_changed = true;
+ }
+ } else {
+ // Toggle button and lock status
+ if (new_status.value && !current_status.locked) {
+ current_status.locked = true;
+ current_status.value = !current_status.value;
+ value_changed = true;
+ }
- // Unlock button ready for next press
- if (!new_status.value && current_status.locked) {
- current_status.locked = false;
- }
+ // Unlock button ready for next press
+ if (!new_status.value && current_status.locked) {
+ current_status.locked = false;
}
+ }
+
+ if (!value_changed) {
+ return;
+ }
- if (!value_changed) {
+ if (is_configuring) {
+ controller.npad_button_state.raw = NpadButton::None;
+ controller.debug_pad_button_state.raw = 0;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Button, false);
+ return;
+ }
+
+ // GC controllers have triggers not buttons
+ if (npad_type == NpadStyleIndex::GameCube) {
+ if (index == Settings::NativeButton::ZR) {
return;
}
-
- if (is_configuring) {
- controller.npad_button_state.raw = NpadButton::None;
- controller.debug_pad_button_state.raw = 0;
- TriggerOnChange(ControllerTriggerType::Button, false);
+ if (index == Settings::NativeButton::ZL) {
return;
}
+ }
- switch (index) {
- case Settings::NativeButton::A:
- controller.npad_button_state.a.Assign(current_status.value);
- controller.debug_pad_button_state.a.Assign(current_status.value);
- break;
- case Settings::NativeButton::B:
- controller.npad_button_state.b.Assign(current_status.value);
- controller.debug_pad_button_state.b.Assign(current_status.value);
- break;
- case Settings::NativeButton::X:
- controller.npad_button_state.x.Assign(current_status.value);
- controller.debug_pad_button_state.x.Assign(current_status.value);
- break;
- case Settings::NativeButton::Y:
- controller.npad_button_state.y.Assign(current_status.value);
- controller.debug_pad_button_state.y.Assign(current_status.value);
- break;
- case Settings::NativeButton::LStick:
- controller.npad_button_state.stick_l.Assign(current_status.value);
- break;
- case Settings::NativeButton::RStick:
- controller.npad_button_state.stick_r.Assign(current_status.value);
- break;
- case Settings::NativeButton::L:
- controller.npad_button_state.l.Assign(current_status.value);
- controller.debug_pad_button_state.l.Assign(current_status.value);
- break;
- case Settings::NativeButton::R:
- controller.npad_button_state.r.Assign(current_status.value);
- controller.debug_pad_button_state.r.Assign(current_status.value);
- break;
- case Settings::NativeButton::ZL:
- controller.npad_button_state.zl.Assign(current_status.value);
- controller.debug_pad_button_state.zl.Assign(current_status.value);
- break;
- case Settings::NativeButton::ZR:
- controller.npad_button_state.zr.Assign(current_status.value);
- controller.debug_pad_button_state.zr.Assign(current_status.value);
- break;
- case Settings::NativeButton::Plus:
- controller.npad_button_state.plus.Assign(current_status.value);
- controller.debug_pad_button_state.plus.Assign(current_status.value);
- break;
- case Settings::NativeButton::Minus:
- controller.npad_button_state.minus.Assign(current_status.value);
- controller.debug_pad_button_state.minus.Assign(current_status.value);
- break;
- case Settings::NativeButton::DLeft:
- controller.npad_button_state.left.Assign(current_status.value);
- controller.debug_pad_button_state.d_left.Assign(current_status.value);
- break;
- case Settings::NativeButton::DUp:
- controller.npad_button_state.up.Assign(current_status.value);
- controller.debug_pad_button_state.d_up.Assign(current_status.value);
- break;
- case Settings::NativeButton::DRight:
- controller.npad_button_state.right.Assign(current_status.value);
- controller.debug_pad_button_state.d_right.Assign(current_status.value);
- break;
- case Settings::NativeButton::DDown:
- controller.npad_button_state.down.Assign(current_status.value);
- controller.debug_pad_button_state.d_down.Assign(current_status.value);
- break;
- case Settings::NativeButton::SL:
- controller.npad_button_state.left_sl.Assign(current_status.value);
- controller.npad_button_state.right_sl.Assign(current_status.value);
- break;
- case Settings::NativeButton::SR:
- controller.npad_button_state.left_sr.Assign(current_status.value);
- controller.npad_button_state.right_sr.Assign(current_status.value);
- break;
- case Settings::NativeButton::Home:
- if (!system_buttons_enabled) {
- break;
- }
- controller.home_button_state.home.Assign(current_status.value);
+ switch (index) {
+ case Settings::NativeButton::A:
+ controller.npad_button_state.a.Assign(current_status.value);
+ controller.debug_pad_button_state.a.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::B:
+ controller.npad_button_state.b.Assign(current_status.value);
+ controller.debug_pad_button_state.b.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::X:
+ controller.npad_button_state.x.Assign(current_status.value);
+ controller.debug_pad_button_state.x.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Y:
+ controller.npad_button_state.y.Assign(current_status.value);
+ controller.debug_pad_button_state.y.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::LStick:
+ controller.npad_button_state.stick_l.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::RStick:
+ controller.npad_button_state.stick_r.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::L:
+ controller.npad_button_state.l.Assign(current_status.value);
+ controller.debug_pad_button_state.l.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::R:
+ controller.npad_button_state.r.Assign(current_status.value);
+ controller.debug_pad_button_state.r.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::ZL:
+ controller.npad_button_state.zl.Assign(current_status.value);
+ controller.debug_pad_button_state.zl.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::ZR:
+ controller.npad_button_state.zr.Assign(current_status.value);
+ controller.debug_pad_button_state.zr.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Plus:
+ controller.npad_button_state.plus.Assign(current_status.value);
+ controller.debug_pad_button_state.plus.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Minus:
+ controller.npad_button_state.minus.Assign(current_status.value);
+ controller.debug_pad_button_state.minus.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DLeft:
+ controller.npad_button_state.left.Assign(current_status.value);
+ controller.debug_pad_button_state.d_left.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DUp:
+ controller.npad_button_state.up.Assign(current_status.value);
+ controller.debug_pad_button_state.d_up.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DRight:
+ controller.npad_button_state.right.Assign(current_status.value);
+ controller.debug_pad_button_state.d_right.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::DDown:
+ controller.npad_button_state.down.Assign(current_status.value);
+ controller.debug_pad_button_state.d_down.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::SL:
+ controller.npad_button_state.left_sl.Assign(current_status.value);
+ controller.npad_button_state.right_sl.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::SR:
+ controller.npad_button_state.left_sr.Assign(current_status.value);
+ controller.npad_button_state.right_sr.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Home:
+ if (!system_buttons_enabled) {
break;
- case Settings::NativeButton::Screenshot:
- if (!system_buttons_enabled) {
- break;
- }
- controller.capture_button_state.capture.Assign(current_status.value);
+ }
+ controller.home_button_state.home.Assign(current_status.value);
+ break;
+ case Settings::NativeButton::Screenshot:
+ if (!system_buttons_enabled) {
break;
}
+ controller.capture_button_state.capture.Assign(current_status.value);
+ break;
}
+
+ lock.unlock();
+
if (!is_connected) {
if (npad_id_type == NpadIdType::Player1 && npad_type != NpadStyleIndex::Handheld) {
Connect();
@@ -643,7 +691,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (index >= controller.stick_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto stick_value = TransformToStick(callback);
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
@@ -659,6 +707,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
if (is_configuring) {
controller.analog_stick_state.left = {};
controller.analog_stick_state.right = {};
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Stick, false);
return;
}
@@ -685,6 +734,7 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
break;
}
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Stick, true);
}
@@ -693,7 +743,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (index >= controller.trigger_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto trigger_value = TransformToTrigger(callback);
// Only read trigger values that have the same uuid or are pressed once
@@ -709,10 +759,16 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
if (is_configuring) {
controller.gc_trigger_state.left = 0;
controller.gc_trigger_state.right = 0;
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Trigger, false);
return;
}
+ // Only GC controllers have analog triggers
+ if (npad_type != NpadStyleIndex::GameCube) {
+ return;
+ }
+
const auto& trigger = controller.trigger_values[index];
switch (index) {
@@ -727,6 +783,7 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
break;
}
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Trigger, true);
}
@@ -735,7 +792,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
if (index >= controller.motion_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
auto& raw_status = controller.motion_values[index].raw_status;
auto& emulated = controller.motion_values[index].emulated;
@@ -756,6 +813,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
force_update_motion = raw_status.force_update;
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Motion, false);
return;
}
@@ -767,6 +825,7 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
motion.orientation = emulated.GetOrientation();
motion.is_at_rest = !emulated.IsMoving(motion_sensitivity);
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Motion, true);
}
@@ -775,10 +834,11 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
if (index >= controller.battery_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
controller.battery_values[index] = TransformToBattery(callback);
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Battery, false);
return;
}
@@ -835,9 +895,49 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
};
break;
}
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Battery, true);
}
+void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
+ std::unique_lock lock{mutex};
+ controller.camera_values = TransformToCamera(callback);
+
+ if (is_configuring) {
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::IrSensor, false);
+ return;
+ }
+
+ controller.camera_state.sample++;
+ controller.camera_state.format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
+ controller.camera_state.data = controller.camera_values.data;
+
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::IrSensor, true);
+}
+
+void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
+ std::unique_lock lock{mutex};
+ controller.nfc_values = TransformToNfc(callback);
+
+ if (is_configuring) {
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Nfc, false);
+ return;
+ }
+
+ controller.nfc_state = {
+ controller.nfc_values.state,
+ controller.nfc_values.data,
+ };
+
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Nfc, true);
+}
+
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
if (device_index >= output_devices.size()) {
return false;
@@ -871,18 +971,100 @@ bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue v
}
bool EmulatedController::TestVibration(std::size_t device_index) {
- static constexpr VibrationValue test_vibration = {
+ if (device_index >= output_devices.size()) {
+ return false;
+ }
+ if (!output_devices[device_index]) {
+ return false;
+ }
+
+ const auto player_index = NpadIdTypeToIndex(npad_id_type);
+ const auto& player = Settings::values.players.GetValue()[player_index];
+
+ if (!player.vibration_enabled) {
+ return false;
+ }
+
+ const Common::Input::VibrationStatus test_vibration = {
.low_amplitude = 0.001f,
- .low_frequency = 160.0f,
+ .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
.high_amplitude = 0.001f,
- .high_frequency = 320.0f,
+ .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
+ .type = Common::Input::VibrationAmplificationType::Test,
+ };
+
+ const Common::Input::VibrationStatus zero_vibration = {
+ .low_amplitude = DEFAULT_VIBRATION_VALUE.low_amplitude,
+ .low_frequency = DEFAULT_VIBRATION_VALUE.low_frequency,
+ .high_amplitude = DEFAULT_VIBRATION_VALUE.high_amplitude,
+ .high_frequency = DEFAULT_VIBRATION_VALUE.high_frequency,
+ .type = Common::Input::VibrationAmplificationType::Test,
};
// Send a slight vibration to test for rumble support
- SetVibration(device_index, test_vibration);
+ output_devices[device_index]->SetVibration(test_vibration);
+
+ // Wait for about 15ms to ensure the controller is ready for the stop command
+ std::this_thread::sleep_for(std::chrono::milliseconds(15));
// Stop any vibration and return the result
- return SetVibration(device_index, DEFAULT_VIBRATION_VALUE);
+ return output_devices[device_index]->SetVibration(zero_vibration) ==
+ Common::Input::VibrationError::None;
+}
+
+bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode) {
+ LOG_INFO(Service_HID, "Set polling mode {}", polling_mode);
+ auto& output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
+ auto& nfc_output_device = output_devices[3];
+
+ const auto virtual_nfc_result = nfc_output_device->SetPollingMode(polling_mode);
+ const auto mapped_nfc_result = output_device->SetPollingMode(polling_mode);
+
+ return virtual_nfc_result == Common::Input::PollingError::None ||
+ mapped_nfc_result == Common::Input::PollingError::None;
+}
+
+bool EmulatedController::SetCameraFormat(
+ Core::IrSensor::ImageTransferProcessorFormat camera_format) {
+ LOG_INFO(Service_HID, "Set camera format {}", camera_format);
+
+ auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
+ auto& camera_output_device = output_devices[2];
+
+ if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
+ camera_format)) == Common::Input::CameraError::None) {
+ return true;
+ }
+
+ // Fallback to Qt camera if native device doesn't have support
+ return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
+ camera_format)) == Common::Input::CameraError::None;
+}
+
+bool EmulatedController::HasNfc() const {
+ const auto& nfc_output_device = output_devices[3];
+
+ switch (npad_type) {
+ case NpadStyleIndex::JoyconRight:
+ case NpadStyleIndex::JoyconDual:
+ case NpadStyleIndex::ProController:
+ break;
+ default:
+ return false;
+ }
+
+ const bool has_virtual_nfc =
+ npad_id_type == NpadIdType::Player1 || npad_id_type == NpadIdType::Handheld;
+ const bool is_virtual_nfc_supported =
+ nfc_output_device->SupportsNfc() != Common::Input::NfcState::NotSupported;
+
+ return is_connected && (has_virtual_nfc && is_virtual_nfc_supported);
+}
+
+bool EmulatedController::WriteNfc(const std::vector<u8>& data) {
+ auto& nfc_output_device = output_devices[3];
+
+ return nfc_output_device->WriteNfcData(data) == Common::Input::NfcState::Success;
}
void EmulatedController::SetLedPattern() {
@@ -907,13 +1089,27 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
if (!is_connected) {
return;
}
+
+ // Attempt to reconnect with the original type
+ if (npad_type != original_npad_type) {
+ Disconnect();
+ const auto current_npad_type = npad_type;
+ SetNpadStyleIndex(original_npad_type);
+ if (IsControllerSupported()) {
+ Connect();
+ return;
+ }
+ SetNpadStyleIndex(current_npad_type);
+ Connect();
+ }
+
if (IsControllerSupported()) {
return;
}
Disconnect();
- // Fallback fullkey controllers to Pro controllers
+ // Fallback Fullkey controllers to Pro controllers
if (IsControllerFullkey() && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
SetNpadStyleIndex(NpadStyleIndex::ProController);
@@ -921,11 +1117,28 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
return;
}
+ // Fallback Dual joycon controllers to Pro controllers
+ if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
+ LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
+ SetNpadStyleIndex(NpadStyleIndex::ProController);
+ Connect();
+ return;
+ }
+
+ // Fallback Pro controllers to Dual joycon
+ if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) {
+ LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
+ SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
+ Connect();
+ return;
+ }
+
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
npad_type);
}
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
+ std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
@@ -941,6 +1154,7 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
}
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
+ std::scoped_lock lock{mutex};
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
switch (type) {
case NpadStyleIndex::ProController:
@@ -976,40 +1190,44 @@ void EmulatedController::Connect(bool use_temporary_value) {
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
return;
}
- {
- std::lock_guard lock{mutex};
- if (is_configuring) {
- tmp_is_connected = true;
- TriggerOnChange(ControllerTriggerType::Connected, false);
- return;
- }
- if (is_connected) {
- return;
- }
- is_connected = true;
+ std::unique_lock lock{mutex};
+ if (is_configuring) {
+ tmp_is_connected = true;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Connected, false);
+ return;
+ }
+
+ if (is_connected) {
+ return;
}
+ is_connected = true;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Connected, true);
}
void EmulatedController::Disconnect() {
- {
- std::lock_guard lock{mutex};
- if (is_configuring) {
- tmp_is_connected = false;
- TriggerOnChange(ControllerTriggerType::Disconnected, false);
- return;
- }
+ std::unique_lock lock{mutex};
+ if (is_configuring) {
+ tmp_is_connected = false;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Disconnected, false);
+ return;
+ }
- if (!is_connected) {
- return;
- }
- is_connected = false;
+ if (!is_connected) {
+ return;
}
+ is_connected = false;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Disconnected, true);
}
bool EmulatedController::IsConnected(bool get_temporary_value) const {
+ std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_is_connected;
}
@@ -1023,10 +1241,12 @@ bool EmulatedController::IsVibrationEnabled() const {
}
NpadIdType EmulatedController::GetNpadIdType() const {
+ std::scoped_lock lock{mutex};
return npad_id_type;
}
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
+ std::scoped_lock lock{mutex};
if (get_temporary_value && is_configuring) {
return tmp_npad_type;
}
@@ -1034,27 +1254,28 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
}
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
- {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
- if (is_configuring) {
- if (tmp_npad_type == npad_type_) {
- return;
- }
- tmp_npad_type = npad_type_;
- TriggerOnChange(ControllerTriggerType::Type, false);
+ if (is_configuring) {
+ if (tmp_npad_type == npad_type_) {
return;
}
+ tmp_npad_type = npad_type_;
+ lock.unlock();
+ TriggerOnChange(ControllerTriggerType::Type, false);
+ return;
+ }
- if (npad_type == npad_type_) {
- return;
- }
- if (is_connected) {
- LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
- NpadIdTypeToIndex(npad_id_type));
- }
- npad_type = npad_type_;
+ if (npad_type == npad_type_) {
+ return;
+ }
+ if (is_connected) {
+ LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
+ NpadIdTypeToIndex(npad_id_type));
}
+ npad_type = npad_type_;
+
+ lock.unlock();
TriggerOnChange(ControllerTriggerType::Type, true);
}
@@ -1082,30 +1303,42 @@ LedPattern EmulatedController::GetLedPattern() const {
}
ButtonValues EmulatedController::GetButtonsValues() const {
+ std::scoped_lock lock{mutex};
return controller.button_values;
}
SticksValues EmulatedController::GetSticksValues() const {
+ std::scoped_lock lock{mutex};
return controller.stick_values;
}
TriggerValues EmulatedController::GetTriggersValues() const {
+ std::scoped_lock lock{mutex};
return controller.trigger_values;
}
ControllerMotionValues EmulatedController::GetMotionValues() const {
+ std::scoped_lock lock{mutex};
return controller.motion_values;
}
ColorValues EmulatedController::GetColorsValues() const {
+ std::scoped_lock lock{mutex};
return controller.color_values;
}
BatteryValues EmulatedController::GetBatteryValues() const {
+ std::scoped_lock lock{mutex};
return controller.battery_values;
}
+CameraValues EmulatedController::GetCameraValues() const {
+ std::scoped_lock lock{mutex};
+ return controller.camera_values;
+}
+
HomeButtonState EmulatedController::GetHomeButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1113,6 +1346,7 @@ HomeButtonState EmulatedController::GetHomeButtons() const {
}
CaptureButtonState EmulatedController::GetCaptureButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1120,6 +1354,7 @@ CaptureButtonState EmulatedController::GetCaptureButtons() const {
}
NpadButtonState EmulatedController::GetNpadButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1127,6 +1362,7 @@ NpadButtonState EmulatedController::GetNpadButtons() const {
}
DebugPadButton EmulatedController::GetDebugPadButtons() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1134,20 +1370,27 @@ DebugPadButton EmulatedController::GetDebugPadButtons() const {
}
AnalogSticks EmulatedController::GetSticks() const {
+ std::unique_lock lock{mutex};
+
if (is_configuring) {
return {};
}
+
// Some drivers like stick from buttons need constant refreshing
for (auto& device : stick_devices) {
if (!device) {
continue;
}
+ lock.unlock();
device->SoftUpdate();
+ lock.lock();
}
+
return controller.analog_stick_state;
}
NpadGcTriggerState EmulatedController::GetTriggers() const {
+ std::scoped_lock lock{mutex};
if (is_configuring) {
return {};
}
@@ -1155,26 +1398,54 @@ NpadGcTriggerState EmulatedController::GetTriggers() const {
}
MotionState EmulatedController::GetMotions() const {
+ std::unique_lock lock{mutex};
+
+ // Some drivers like mouse motion need constant refreshing
if (force_update_motion) {
for (auto& device : motion_devices) {
if (!device) {
continue;
}
+ lock.unlock();
device->ForceUpdate();
+ lock.lock();
}
}
+
return controller.motion_state;
}
ControllerColors EmulatedController::GetColors() const {
+ std::scoped_lock lock{mutex};
return controller.colors_state;
}
BatteryLevelState EmulatedController::GetBattery() const {
+ std::scoped_lock lock{mutex};
return controller.battery_state;
}
+const CameraState& EmulatedController::GetCamera() const {
+ std::scoped_lock lock{mutex};
+ return controller.camera_state;
+}
+
+const NfcState& EmulatedController::GetNfc() const {
+ std::scoped_lock lock{mutex};
+ return controller.nfc_state;
+}
+
+NpadColor EmulatedController::GetNpadColor(u32 color) {
+ return {
+ .r = static_cast<u8>((color >> 16) & 0xFF),
+ .g = static_cast<u8>((color >> 8) & 0xFF),
+ .b = static_cast<u8>(color & 0xFF),
+ .a = 0xff,
+ };
+}
+
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const ControllerUpdateCallback& poller = poller_pair.second;
if (!is_npad_service_update && poller.is_npad_service) {
@@ -1187,13 +1458,13 @@ void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npa
}
int EmulatedController::SetCallback(ControllerUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedController::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
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 d8642c5b3..319226bf8 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -16,10 +15,12 @@
#include "common/settings.h"
#include "common/vector_math.h"
#include "core/hid/hid_types.h"
+#include "core/hid/irs_types.h"
#include "core/hid/motion_input.h"
namespace Core::HID {
const std::size_t max_emulated_controllers = 2;
+const std::size_t output_devices_size = 4;
struct ControllerMotionInfo {
Common::Input::MotionStatus raw_status{};
MotionInput emulated{};
@@ -35,15 +36,18 @@ using TriggerDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
using BatteryDevices =
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
-using OutputDevices =
- std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
+using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
+using NfcDevices = std::unique_ptr<Common::Input::InputDevice>;
+using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices_size>;
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
-using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
+using CameraParams = Common::ParamPackage;
+using NfcParams = Common::ParamPackage;
+using OutputParams = std::array<Common::ParamPackage, output_devices_size>;
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
@@ -52,6 +56,8 @@ using TriggerValues =
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
+using CameraValues = Common::Input::CameraStatus;
+using NfcValues = Common::Input::NfcStatus;
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
struct AnalogSticks {
@@ -71,6 +77,17 @@ struct BatteryLevelState {
NpadPowerInfo right{};
};
+struct CameraState {
+ Core::IrSensor::ImageTransferProcessorFormat format{};
+ std::vector<u8> data{};
+ std::size_t sample{};
+};
+
+struct NfcState {
+ Common::Input::NfcState state{};
+ std::vector<u8> data{};
+};
+
struct ControllerMotion {
Common::Vec3f accel{};
Common::Vec3f gyro{};
@@ -97,6 +114,8 @@ struct ControllerStatus {
ColorValues color_values{};
BatteryValues battery_values{};
VibrationValues vibration_values{};
+ CameraValues camera_values{};
+ NfcValues nfc_values{};
// Data for HID serices
HomeButtonState home_button_state{};
@@ -108,6 +127,8 @@ struct ControllerStatus {
NpadGcTriggerState gc_trigger_state{};
ControllerColors colors_state{};
BatteryLevelState battery_state{};
+ CameraState camera_state{};
+ NfcState nfc_state{};
};
enum class ControllerTriggerType {
@@ -118,6 +139,8 @@ enum class ControllerTriggerType {
Color,
Battery,
Vibration,
+ IrSensor,
+ Nfc,
Connected,
Disconnected,
Type,
@@ -270,6 +293,9 @@ public:
/// Returns the latest battery status from the controller with parameters
BatteryValues GetBatteryValues() const;
+ /// Returns the latest camera status from the controller with parameters
+ CameraValues GetCameraValues() const;
+
/// Returns the latest status of button input for the hid::HomeButton service
HomeButtonState GetHomeButtons() const;
@@ -297,18 +323,44 @@ public:
/// Returns the latest battery status from the controller
BatteryLevelState GetBattery() const;
+ /// Returns the latest camera status from the controller
+ const CameraState& GetCamera() const;
+
+ /// Returns the latest ntag status from the controller
+ const NfcState& GetNfc() const;
+
/**
* Sends a specific vibration to the output device
- * @return returns true if vibration had no errors
+ * @return true if vibration had no errors
*/
bool SetVibration(std::size_t device_index, VibrationValue vibration);
/**
* Sends a small vibration to the output device
- * @return returns true if SetVibration was successfull
+ * @return true if SetVibration was successfull
*/
bool TestVibration(std::size_t device_index);
+ /**
+ * Sets the desired data to be polled from a controller
+ * @param polling_mode type of input desired buttons, gyro, nfc, ir, etc.
+ * @return true if SetPollingMode was successfull
+ */
+ bool SetPollingMode(Common::Input::PollingMode polling_mode);
+
+ /**
+ * Sets the desired camera format to be polled from a controller
+ * @param camera_format size of each frame
+ * @return true if SetCameraFormat was successfull
+ */
+ bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
+
+ /// Returns true if the device has nfc support
+ bool HasNfc() const;
+
+ /// Returns true if the nfc tag was written
+ bool WriteNfc(const std::vector<u8>& data);
+
/// Returns the led pattern corresponding to this emulated controller
LedPattern GetLedPattern() const;
@@ -387,14 +439,34 @@ private:
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
/**
+ * Updates the camera status of the controller
+ * @param callback A CallbackStatus containing the camera status
+ */
+ void SetCamera(const Common::Input::CallbackStatus& callback);
+
+ /**
+ * Updates the nfc status of the controller
+ * @param callback A CallbackStatus containing the nfc status
+ */
+ void SetNfc(const Common::Input::CallbackStatus& callback);
+
+ /**
+ * Converts a color format from bgra to rgba
+ * @param color in bgra format
+ * @return NpadColor in rgba format
+ */
+ NpadColor GetNpadColor(u32 color);
+
+ /**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
* @param is_service_update indicates if this event should only be sent to HID services
*/
void TriggerOnChange(ControllerTriggerType type, bool is_service_update);
- NpadIdType npad_id_type;
+ const NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
+ NpadStyleIndex original_npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
bool is_configuring{false};
@@ -411,6 +483,8 @@ private:
ControllerMotionParams motion_params;
TriggerParams trigger_params;
BatteryParams battery_params;
+ CameraParams camera_params;
+ NfcParams nfc_params;
OutputParams output_params;
ButtonDevices button_devices;
@@ -418,6 +492,8 @@ private:
ControllerMotionDevices motion_devices;
TriggerDevices trigger_devices;
BatteryDevices battery_devices;
+ CameraDevices camera_devices;
+ NfcDevices nfc_devices;
OutputDevices output_devices;
// TAS related variables
@@ -427,6 +503,7 @@ private:
StickDevices tas_stick_devices;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0;
diff --git a/src/core/hid/emulated_devices.cpp b/src/core/hid/emulated_devices.cpp
index 708480f2d..8d367b546 100644
--- a/src/core/hid/emulated_devices.cpp
+++ b/src/core/hid/emulated_devices.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <fmt/format.h>
@@ -15,6 +14,7 @@ EmulatedDevices::EmulatedDevices() = default;
EmulatedDevices::~EmulatedDevices() = default;
void EmulatedDevices::ReloadFromSettings() {
+ ring_params = Common::ParamPackage(Settings::values.ringcon_analogs);
ReloadInput();
}
@@ -66,6 +66,8 @@ void EmulatedDevices::ReloadInput() {
key_index++;
}
+ ring_analog_device = Common::Input::CreateDevice<Common::Input::InputDevice>(ring_params);
+
for (std::size_t index = 0; index < mouse_button_devices.size(); ++index) {
if (!mouse_button_devices[index]) {
continue;
@@ -120,6 +122,13 @@ void EmulatedDevices::ReloadInput() {
},
});
}
+
+ if (ring_analog_device) {
+ ring_analog_device->SetCallback({
+ .on_change =
+ [this](const Common::Input::CallbackStatus& callback) { SetRingAnalog(callback); },
+ });
+ }
}
void EmulatedDevices::UnloadInput() {
@@ -155,6 +164,7 @@ void EmulatedDevices::SaveCurrentConfig() {
if (!is_configuring) {
return;
}
+ Settings::values.ringcon_analogs = ring_params.Serialize();
}
void EmulatedDevices::RestoreConfig() {
@@ -164,12 +174,21 @@ void EmulatedDevices::RestoreConfig() {
ReloadFromSettings();
}
+Common::ParamPackage EmulatedDevices::GetRingParam() const {
+ return ring_params;
+}
+
+void EmulatedDevices::SetRingParam(Common::ParamPackage param) {
+ ring_params = std::move(param);
+ ReloadInput();
+}
+
void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& callback,
std::size_t index) {
if (index >= device_status.keyboard_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_values[index];
@@ -201,6 +220,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
return;
}
@@ -208,6 +228,7 @@ void EmulatedDevices::SetKeyboardButton(const Common::Input::CallbackStatus& cal
// Index should be converted from NativeKeyboard to KeyboardKeyIndex
UpdateKey(index, current_status.value);
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Keyboard);
}
@@ -227,7 +248,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
if (index >= device_status.keyboard_moddifier_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.keyboard_moddifier_values[index];
@@ -259,6 +280,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
return;
}
@@ -289,6 +311,7 @@ void EmulatedDevices::SetKeyboardModifier(const Common::Input::CallbackStatus& c
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::KeyboardModdifier);
}
@@ -297,7 +320,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
if (index >= device_status.mouse_button_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
bool value_changed = false;
const auto new_status = TransformToButton(callback);
auto& current_status = device_status.mouse_button_values[index];
@@ -329,6 +352,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
}
if (is_configuring) {
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -351,6 +375,7 @@ void EmulatedDevices::SetMouseButton(const Common::Input::CallbackStatus& callba
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
@@ -359,13 +384,14 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
if (index >= device_status.mouse_analog_values.size()) {
return;
}
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto analog_value = TransformToAnalog(callback);
device_status.mouse_analog_values[index] = analog_value;
if (is_configuring) {
device_status.mouse_position_state = {};
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -379,17 +405,19 @@ void EmulatedDevices::SetMouseAnalog(const Common::Input::CallbackStatus& callba
break;
}
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callback) {
- std::lock_guard lock{mutex};
+ std::unique_lock lock{mutex};
const auto touch_value = TransformToTouch(callback);
device_status.mouse_stick_value = touch_value;
if (is_configuring) {
device_status.mouse_position_state = {};
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
return;
}
@@ -397,42 +425,77 @@ void EmulatedDevices::SetMouseStick(const Common::Input::CallbackStatus& callbac
device_status.mouse_position_state.x = touch_value.x.value;
device_status.mouse_position_state.y = touch_value.y.value;
+ lock.unlock();
TriggerOnChange(DeviceTriggerType::Mouse);
}
+void EmulatedDevices::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
+ std::lock_guard lock{mutex};
+ const auto force_value = TransformToStick(callback);
+
+ device_status.ring_analog_value = force_value.x;
+
+ if (is_configuring) {
+ device_status.ring_analog_value = {};
+ TriggerOnChange(DeviceTriggerType::RingController);
+ return;
+ }
+
+ device_status.ring_analog_state.force = force_value.x.value;
+
+ TriggerOnChange(DeviceTriggerType::RingController);
+}
+
KeyboardValues EmulatedDevices::GetKeyboardValues() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_values;
}
KeyboardModifierValues EmulatedDevices::GetKeyboardModdifierValues() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_values;
}
MouseButtonValues EmulatedDevices::GetMouseButtonsValues() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_button_values;
}
+RingAnalogValue EmulatedDevices::GetRingSensorValues() const {
+ return device_status.ring_analog_value;
+}
+
KeyboardKey EmulatedDevices::GetKeyboard() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_state;
}
KeyboardModifier EmulatedDevices::GetKeyboardModifier() const {
+ std::scoped_lock lock{mutex};
return device_status.keyboard_moddifier_state;
}
MouseButton EmulatedDevices::GetMouseButtons() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_button_state;
}
MousePosition EmulatedDevices::GetMousePosition() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_position_state;
}
AnalogStickState EmulatedDevices::GetMouseWheel() const {
+ std::scoped_lock lock{mutex};
return device_status.mouse_wheel_state;
}
+RingSensorForce EmulatedDevices::GetRingSensorForce() const {
+ return device_status.ring_analog_state;
+}
+
void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
+ std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
const InterfaceUpdateCallback& poller = poller_pair.second;
if (poller.on_change) {
@@ -442,13 +505,13 @@ void EmulatedDevices::TriggerOnChange(DeviceTriggerType type) {
}
int EmulatedDevices::SetCallback(InterfaceUpdateCallback update_callback) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
callback_list.insert_or_assign(last_callback_key, std::move(update_callback));
return last_callback_key++;
}
void EmulatedDevices::DeleteCallback(int key) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{callback_mutex};
const auto& iterator = callback_list.find(key);
if (iterator == callback_list.end()) {
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..4149eeced 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -26,9 +25,11 @@ using MouseButtonDevices = std::array<std::unique_ptr<Common::Input::InputDevice
using MouseAnalogDevices = std::array<std::unique_ptr<Common::Input::InputDevice>,
Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickDevice = std::unique_ptr<Common::Input::InputDevice>;
+using RingAnalogDevice = std::unique_ptr<Common::Input::InputDevice>;
using MouseButtonParams =
std::array<Common::ParamPackage, Settings::NativeMouseButton::NumMouseButtons>;
+using RingAnalogParams = Common::ParamPackage;
using KeyboardValues =
std::array<Common::Input::ButtonStatus, Settings::NativeKeyboard::NumKeyboardKeys>;
@@ -39,12 +40,17 @@ using MouseButtonValues =
using MouseAnalogValues =
std::array<Common::Input::AnalogStatus, Settings::NativeMouseWheel::NumMouseWheels>;
using MouseStickValue = Common::Input::TouchStatus;
+using RingAnalogValue = Common::Input::AnalogStatus;
struct MousePosition {
f32 x;
f32 y;
};
+struct RingSensorForce {
+ f32 force;
+};
+
struct DeviceStatus {
// Data from input_common
KeyboardValues keyboard_values{};
@@ -52,6 +58,7 @@ struct DeviceStatus {
MouseButtonValues mouse_button_values{};
MouseAnalogValues mouse_analog_values{};
MouseStickValue mouse_stick_value{};
+ RingAnalogValue ring_analog_value{};
// Data for HID serices
KeyboardKey keyboard_state{};
@@ -59,12 +66,14 @@ struct DeviceStatus {
MouseButton mouse_button_state{};
MousePosition mouse_position_state{};
AnalogStickState mouse_wheel_state{};
+ RingSensorForce ring_analog_state{};
};
enum class DeviceTriggerType {
Keyboard,
KeyboardModdifier,
Mouse,
+ RingController,
};
struct InterfaceUpdateCallback {
@@ -110,6 +119,15 @@ public:
/// Reverts any mapped changes made that weren't saved
void RestoreConfig();
+ // Returns the current mapped ring device
+ Common::ParamPackage GetRingParam() const;
+
+ /**
+ * Updates the current mapped ring device
+ * @param param ParamPackage with ring sensor data to be mapped
+ */
+ void SetRingParam(Common::ParamPackage param);
+
/// Returns the latest status of button input from the keyboard with parameters
KeyboardValues GetKeyboardValues() const;
@@ -119,6 +137,9 @@ public:
/// Returns the latest status of button input from the mouse with parameters
MouseButtonValues GetMouseButtonsValues() const;
+ /// Returns the latest status of analog input from the ring sensor with parameters
+ RingAnalogValue GetRingSensorValues() const;
+
/// Returns the latest status of button input from the keyboard
KeyboardKey GetKeyboard() const;
@@ -134,6 +155,9 @@ public:
/// Returns the latest mouse wheel change
AnalogStickState GetMouseWheel() const;
+ /// Returns the latest ringcon force sensor value
+ RingSensorForce GetRingSensorForce() const;
+
/**
* Adds a callback to the list of events
* @param update_callback InterfaceUpdateCallback that will be triggered
@@ -186,6 +210,12 @@ private:
void SetMouseStick(const Common::Input::CallbackStatus& callback);
/**
+ * Updates the ring analog sensor status of the ring controller
+ * @param callback A CallbackStatus containing the force status
+ */
+ void SetRingAnalog(const Common::Input::CallbackStatus& callback);
+
+ /**
* Triggers a callback that something has changed on the device status
* @param type Input type of the event to trigger
*/
@@ -193,13 +223,17 @@ private:
bool is_configuring{false};
+ RingAnalogParams ring_params;
+
KeyboardDevices keyboard_devices;
KeyboardModifierDevices keyboard_modifier_devices;
MouseButtonDevices mouse_button_devices;
MouseAnalogDevices mouse_analog_devices;
MouseStickDevice mouse_stick_device;
+ RingAnalogDevice ring_analog_device;
mutable std::mutex mutex;
+ mutable std::mutex callback_mutex;
std::unordered_map<int, InterfaceUpdateCallback> callback_list;
int last_callback_key = 0;
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index a1c3bbb57..7d6373414 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/hid/emulated_console.h"
@@ -49,7 +48,7 @@ EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type) {
return handheld.get();
case NpadIdType::Invalid:
default:
- UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
+ ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
@@ -78,7 +77,7 @@ const EmulatedController* HIDCore::GetEmulatedController(NpadIdType npad_id_type
return handheld.get();
case NpadIdType::Invalid:
default:
- UNREACHABLE_MSG("Invalid NpadIdType={}", npad_id_type);
+ ASSERT_MSG(false, "Invalid NpadIdType={}", npad_id_type);
return nullptr;
}
}
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
index 717f605e7..5fe36551e 100644
--- a/src/core/hid/hid_core.h
+++ b/src/core/hid/hid_core.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index 778b328b9..e3b1cfbc6 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -273,6 +272,7 @@ enum class VibrationDeviceType : u32 {
Unknown = 0,
LinearResonantActuator = 1,
GcErm = 2,
+ N64 = 3,
};
// This is nn::hid::VibrationGcErmCommand
@@ -317,27 +317,35 @@ static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"
// This is nn::hid::TouchState
struct TouchState {
- u64 delta_time;
- TouchAttribute attribute;
- u32 finger;
- Common::Point<u32> position;
- u32 diameter_x;
- u32 diameter_y;
- u32 rotation_angle;
+ u64 delta_time{};
+ TouchAttribute attribute{};
+ u32 finger{};
+ Common::Point<u32> position{};
+ u32 diameter_x{};
+ u32 diameter_y{};
+ u32 rotation_angle{};
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
+struct NpadColor {
+ u8 r{};
+ u8 g{};
+ u8 b{};
+ u8 a{};
+};
+static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");
+
// This is nn::hid::NpadControllerColor
struct NpadControllerColor {
- u32 body;
- u32 button;
+ NpadColor body{};
+ NpadColor button{};
};
static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
// This is nn::hid::AnalogStickState
struct AnalogStickState {
- s32 x;
- s32 y;
+ s32 x{};
+ s32 y{};
};
static_assert(sizeof(AnalogStickState) == 8, "AnalogStickState is an invalid size");
@@ -355,10 +363,10 @@ static_assert(sizeof(NpadBatteryLevel) == 0x4, "NpadBatteryLevel is an invalid s
// This is nn::hid::system::NpadPowerInfo
struct NpadPowerInfo {
- bool is_powered;
- bool is_charging;
+ bool is_powered{};
+ bool is_charging{};
INSERT_PADDING_BYTES(0x6);
- NpadBatteryLevel battery_level;
+ NpadBatteryLevel battery_level{8};
};
static_assert(sizeof(NpadPowerInfo) == 0xC, "NpadPowerInfo is an invalid size");
@@ -475,8 +483,8 @@ static_assert(sizeof(DebugPadButton) == 0x4, "DebugPadButton is an invalid size"
// This is nn::hid::ConsoleSixAxisSensorHandle
struct ConsoleSixAxisSensorHandle {
- u8 unknown_1;
- u8 unknown_2;
+ u8 unknown_1{};
+ u8 unknown_2{};
INSERT_PADDING_BYTES_NOINIT(2);
};
static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
@@ -484,35 +492,79 @@ static_assert(sizeof(ConsoleSixAxisSensorHandle) == 4,
// This is nn::hid::SixAxisSensorHandle
struct SixAxisSensorHandle {
- NpadStyleIndex npad_type;
- u8 npad_id;
- DeviceIndex device_index;
+ NpadStyleIndex npad_type{NpadStyleIndex::None};
+ u8 npad_id{};
+ DeviceIndex device_index{DeviceIndex::None};
INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(SixAxisSensorHandle) == 4, "SixAxisSensorHandle is an invalid size");
+// These parameters seem related to how much gyro/accelerometer is used
struct SixAxisSensorFusionParameters {
- f32 parameter1;
- f32 parameter2;
+ f32 parameter1{0.03f}; // Range 0.0 to 1.0, default 0.03
+ f32 parameter2{0.4f}; // Default 0.4
};
static_assert(sizeof(SixAxisSensorFusionParameters) == 8,
"SixAxisSensorFusionParameters is an invalid size");
+// This is nn::hid::server::SixAxisSensorProperties
+struct SixAxisSensorProperties {
+ union {
+ u8 raw{};
+ BitField<0, 1, u8> is_newly_assigned;
+ BitField<1, 1, u8> is_firmware_update_available;
+ };
+};
+static_assert(sizeof(SixAxisSensorProperties) == 1, "SixAxisSensorProperties is an invalid size");
+
+// This is nn::hid::SixAxisSensorCalibrationParameter
+struct SixAxisSensorCalibrationParameter {
+ std::array<u8, 0x744> unknown_data{};
+};
+static_assert(sizeof(SixAxisSensorCalibrationParameter) == 0x744,
+ "SixAxisSensorCalibrationParameter is an invalid size");
+
+// This is nn::hid::SixAxisSensorIcInformation
+struct SixAxisSensorIcInformation {
+ f32 angular_rate{2000.0f}; // dps
+ std::array<f32, 6> unknown_gyro_data1{
+ -10.0f, -10.0f, -10.0f, 10.0f, 10.0f, 10.0f,
+ }; // dps
+ std::array<f32, 9> unknown_gyro_data2{
+ 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
+ };
+ std::array<f32, 9> unknown_gyro_data3{
+ 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
+ };
+ f32 acceleration_range{8.0f}; // g force
+ std::array<f32, 6> unknown_accel_data1{
+ -0.0612f, -0.0612f, -0.0612f, 0.0612f, 0.0612f, 0.0612f,
+ }; // g force
+ std::array<f32, 9> unknown_accel_data2{
+ 0.95f, -0.003f, -0.003f, -0.003f, 0.95f, -0.003f, -0.003f, -0.003f, 0.95f,
+ };
+ std::array<f32, 9> unknown_accel_data3{
+ 1.05f, 0.003f, 0.003f, 0.003f, 1.05f, 0.003f, 0.003f, 0.003f, 1.05f,
+ };
+};
+static_assert(sizeof(SixAxisSensorIcInformation) == 0xC8,
+ "SixAxisSensorIcInformation is an invalid size");
+
// This is nn::hid::VibrationDeviceHandle
struct VibrationDeviceHandle {
- NpadStyleIndex npad_type;
- u8 npad_id;
- DeviceIndex device_index;
+ NpadStyleIndex npad_type{NpadStyleIndex::None};
+ u8 npad_id{};
+ DeviceIndex device_index{DeviceIndex::None};
INSERT_PADDING_BYTES_NOINIT(1);
};
static_assert(sizeof(VibrationDeviceHandle) == 4, "SixAxisSensorHandle is an invalid size");
// This is nn::hid::VibrationValue
struct VibrationValue {
- f32 low_amplitude;
- f32 low_frequency;
- f32 high_amplitude;
- f32 high_frequency;
+ f32 low_amplitude{};
+ f32 low_frequency{};
+ f32 high_amplitude{};
+ f32 high_frequency{};
};
static_assert(sizeof(VibrationValue) == 0x10, "VibrationValue has incorrect size.");
@@ -561,7 +613,7 @@ static_assert(sizeof(KeyboardAttribute) == 0x4, "KeyboardAttribute is an invalid
// This is nn::hid::KeyboardKey
struct KeyboardKey {
// This should be a 256 bit flag
- std::array<u8, 32> key;
+ std::array<u8, 32> key{};
};
static_assert(sizeof(KeyboardKey) == 0x20, "KeyboardKey is an invalid size");
@@ -590,16 +642,16 @@ static_assert(sizeof(MouseAttribute) == 0x4, "MouseAttribute is an invalid size"
// This is nn::hid::detail::MouseState
struct MouseState {
- s64 sampling_number;
- s32 x;
- s32 y;
- s32 delta_x;
- s32 delta_y;
+ s64 sampling_number{};
+ s32 x{};
+ s32 y{};
+ s32 delta_x{};
+ s32 delta_y{};
// Axis Order in HW is switched for the wheel
- s32 delta_wheel_y;
- s32 delta_wheel_x;
- MouseButton button;
- MouseAttribute attribute;
+ s32 delta_wheel_y{};
+ s32 delta_wheel_x{};
+ MouseButton button{};
+ MouseAttribute attribute{};
};
static_assert(sizeof(MouseState) == 0x28, "MouseState is an invalid size");
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index cd41607a7..fe9915abe 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -1,7 +1,7 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <algorithm>
#include <random>
#include "common/input.h"
@@ -52,6 +52,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
Common::Input::ButtonStatus status{};
switch (callback.type) {
case Common::Input::InputType::Analog:
+ status.value = TransformToTrigger(callback).pressed.value;
+ status.toggle = callback.analog_status.properties.toggle;
+ break;
case Common::Input::InputType::Trigger:
status.value = TransformToTrigger(callback).pressed.value;
break;
@@ -197,6 +200,9 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
x = std::clamp(x, 0.0f, 1.0f);
y = std::clamp(y, 0.0f, 1.0f);
+ // Limit id to maximum number of fingers
+ status.id = std::clamp(status.id, 0, 16);
+
if (status.pressed.inverted) {
status.pressed.value = !status.pressed.value;
}
@@ -267,6 +273,34 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
return status;
}
+Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
+ Common::Input::CameraStatus camera{};
+ switch (callback.type) {
+ case Common::Input::InputType::IrSensor:
+ camera = callback.camera_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
+ break;
+ }
+
+ return camera;
+}
+
+Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback) {
+ Common::Input::NfcStatus nfc{};
+ switch (callback.type) {
+ case Common::Input::InputType::Nfc:
+ return callback.nfc_status;
+ break;
+ default:
+ LOG_ERROR(Input, "Conversion from type {} to NFC not implemented", callback.type);
+ break;
+ }
+
+ return nfc;
+}
+
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
const auto& properties = analog.properties;
float& raw_value = analog.raw_value;
@@ -328,7 +362,7 @@ void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogS
raw_y += properties_y.offset;
// Apply X scale correction from offset
- if (std::abs(properties_x.offset) < 0.5f) {
+ if (std::abs(properties_x.offset) < 0.75f) {
if (raw_x > 0) {
raw_x /= 1 + properties_x.offset;
} else {
@@ -337,7 +371,7 @@ void SanitizeStick(Common::Input::AnalogStatus& analog_x, Common::Input::AnalogS
}
// Apply Y scale correction from offset
- if (std::abs(properties_y.offset) < 0.5f) {
+ if (std::abs(properties_y.offset) < 0.75f) {
if (raw_y > 0) {
raw_y /= 1 + properties_y.offset;
} else {
diff --git a/src/core/hid/input_converter.h b/src/core/hid/input_converter.h
index d24582226..b7eb6e660 100644
--- a/src/core/hid/input_converter.h
+++ b/src/core/hid/input_converter.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -78,6 +77,22 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
/**
+ * Converts raw input data into a valid camera status.
+ *
+ * @param callback Supported callbacks: Camera.
+ * @return A valid CameraObject object.
+ */
+Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
+
+/**
+ * Converts raw input data into a valid nfc status.
+ *
+ * @param callback Supported callbacks: Nfc.
+ * @return A valid CameraObject object.
+ */
+Common::Input::NfcStatus TransformToNfc(const Common::Input::CallbackStatus& callback);
+
+/**
* Converts raw analog data into a valid analog value
* @param analog An analog object containing raw data and properties
* @param clamp_value determines if the value needs to be clamped between -1.0f and 1.0f.
diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp
index 2dbda8814..76d6b8ab0 100644
--- a/src/core/hid/input_interpreter.cpp
+++ b/src/core/hid/input_interpreter.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hid/hid_types.h"
diff --git a/src/core/hid/input_interpreter.h b/src/core/hid/input_interpreter.h
index 70c34d474..8c521b381 100644
--- a/src/core/hid/input_interpreter.h
+++ b/src/core/hid/input_interpreter.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hid/irs_types.h b/src/core/hid/irs_types.h
new file mode 100644
index 000000000..88c5b016d
--- /dev/null
+++ b/src/core/hid/irs_types.h
@@ -0,0 +1,301 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hid/hid_types.h"
+
+namespace Core::IrSensor {
+
+// This is nn::irsensor::CameraAmbientNoiseLevel
+enum class CameraAmbientNoiseLevel : u32 {
+ Low,
+ Medium,
+ High,
+ Unkown3, // This level can't be reached
+};
+
+// This is nn::irsensor::CameraLightTarget
+enum class CameraLightTarget : u32 {
+ AllLeds,
+ BrightLeds,
+ DimLeds,
+ None,
+};
+
+// This is nn::irsensor::PackedCameraLightTarget
+enum class PackedCameraLightTarget : u8 {
+ AllLeds,
+ BrightLeds,
+ DimLeds,
+ None,
+};
+
+// This is nn::irsensor::AdaptiveClusteringMode
+enum class AdaptiveClusteringMode : u32 {
+ StaticFov,
+ DynamicFov,
+};
+
+// This is nn::irsensor::AdaptiveClusteringTargetDistance
+enum class AdaptiveClusteringTargetDistance : u32 {
+ Near,
+ Middle,
+ Far,
+};
+
+// This is nn::irsensor::ImageTransferProcessorFormat
+enum class ImageTransferProcessorFormat : u32 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+};
+
+// This is nn::irsensor::PackedImageTransferProcessorFormat
+enum class PackedImageTransferProcessorFormat : u8 {
+ Size320x240,
+ Size160x120,
+ Size80x60,
+ Size40x30,
+ Size20x15,
+};
+
+// This is nn::irsensor::IrCameraStatus
+enum class IrCameraStatus : u32 {
+ Available,
+ Unsupported,
+ Unconnected,
+};
+
+// This is nn::irsensor::IrCameraInternalStatus
+enum class IrCameraInternalStatus : u32 {
+ Stopped,
+ FirmwareUpdateNeeded,
+ Unkown2,
+ Unkown3,
+ Unkown4,
+ FirmwareVersionRequested,
+ FirmwareVersionIsInvalid,
+ Ready,
+ Setting,
+};
+
+// This is nn::irsensor::detail::StatusManager::IrSensorMode
+enum class IrSensorMode : u64 {
+ None,
+ MomentProcessor,
+ ClusteringProcessor,
+ ImageTransferProcessor,
+ PointingProcessorMarker,
+ TeraPluginProcessor,
+ IrLedProcessor,
+};
+
+// This is nn::irsensor::ImageProcessorStatus
+enum ImageProcessorStatus : u32 {
+ Stopped,
+ Running,
+};
+
+// This is nn::irsensor::HandAnalysisMode
+enum class HandAnalysisMode : u32 {
+ None,
+ Silhouette,
+ Image,
+ SilhoueteAndImage,
+ SilhuetteOnly,
+};
+
+// This is nn::irsensor::IrSensorFunctionLevel
+enum class IrSensorFunctionLevel : u8 {
+ unknown0,
+ unknown1,
+ unknown2,
+ unknown3,
+ unknown4,
+};
+
+// This is nn::irsensor::MomentProcessorPreprocess
+enum class MomentProcessorPreprocess : u32 {
+ Unkown0,
+ Unkown1,
+};
+
+// This is nn::irsensor::PackedMomentProcessorPreprocess
+enum class PackedMomentProcessorPreprocess : u8 {
+ Unkown0,
+ Unkown1,
+};
+
+// This is nn::irsensor::PointingStatus
+enum class PointingStatus : u32 {
+ Unkown0,
+ Unkown1,
+};
+
+struct IrsRect {
+ s16 x;
+ s16 y;
+ s16 width;
+ s16 height;
+};
+
+struct IrsCentroid {
+ f32 x;
+ f32 y;
+};
+
+struct CameraConfig {
+ u64 exposure_time;
+ CameraLightTarget light_target;
+ u32 gain;
+ bool is_negative_used;
+ INSERT_PADDING_BYTES(7);
+};
+static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
+
+struct PackedCameraConfig {
+ u64 exposure_time;
+ PackedCameraLightTarget light_target;
+ u8 gain;
+ bool is_negative_used;
+ INSERT_PADDING_BYTES(5);
+};
+static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
+
+// This is nn::irsensor::IrCameraHandle
+struct IrCameraHandle {
+ u8 npad_id{};
+ Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
+
+// This is nn::irsensor::PackedMcuVersion
+struct PackedMcuVersion {
+ u16 major;
+ u16 minor;
+};
+static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
+
+// This is nn::irsensor::PackedMomentProcessorConfig
+struct PackedMomentProcessorConfig {
+ PackedCameraConfig camera_config;
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ PackedMomentProcessorPreprocess preprocess;
+ u8 preprocess_intensity_threshold;
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
+ "PackedMomentProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedClusteringProcessorConfig
+struct PackedClusteringProcessorConfig {
+ PackedCameraConfig camera_config;
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u8 object_intensity_min;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
+ "PackedClusteringProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedImageTransferProcessorConfig
+struct PackedImageTransferProcessorConfig {
+ PackedCameraConfig camera_config;
+ PackedMcuVersion required_mcu_version;
+ PackedImageTransferProcessorFormat format;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
+ "PackedImageTransferProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedTeraPluginProcessorConfig
+struct PackedTeraPluginProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 mode;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+};
+static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
+ "PackedTeraPluginProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedPointingProcessorConfig
+struct PackedPointingProcessorConfig {
+ IrsRect window_of_interest;
+ PackedMcuVersion required_mcu_version;
+};
+static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
+ "PackedPointingProcessorConfig is an invalid size");
+
+// This is nn::irsensor::PackedFunctionLevel
+struct PackedFunctionLevel {
+ IrSensorFunctionLevel function_level;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
+
+// This is nn::irsensor::PackedImageTransferProcessorExConfig
+struct PackedImageTransferProcessorExConfig {
+ PackedCameraConfig camera_config;
+ PackedMcuVersion required_mcu_version;
+ PackedImageTransferProcessorFormat origin_format;
+ PackedImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(5);
+};
+static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
+ "PackedImageTransferProcessorExConfig is an invalid size");
+
+// This is nn::irsensor::PackedIrLedProcessorConfig
+struct PackedIrLedProcessorConfig {
+ PackedMcuVersion required_mcu_version;
+ u8 light_target;
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
+ "PackedIrLedProcessorConfig is an invalid size");
+
+// This is nn::irsensor::HandAnalysisConfig
+struct HandAnalysisConfig {
+ HandAnalysisMode mode;
+};
+static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
+
+// This is nn::irsensor::detail::ProcessorState contents are different for each processor
+struct ProcessorState {
+ std::array<u8, 0xE20> processor_raw_data{};
+};
+static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
+
+// This is nn::irsensor::detail::DeviceFormat
+struct DeviceFormat {
+ Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
+ Core::IrSensor::IrCameraInternalStatus camera_internal_status{
+ Core::IrSensor::IrCameraInternalStatus::Ready};
+ Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
+ ProcessorState state{};
+};
+static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
+
+// This is nn::irsensor::ImageTransferProcessorState
+struct ImageTransferProcessorState {
+ u64 sampling_number;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ INSERT_PADDING_BYTES(4);
+};
+static_assert(sizeof(ImageTransferProcessorState) == 0x10,
+ "ImageTransferProcessorState is an invalid size");
+
+} // namespace Core::IrSensor
diff --git a/src/core/hid/motion_input.cpp b/src/core/hid/motion_input.cpp
index 05042fd99..b1f658e62 100644
--- a/src/core/hid/motion_input.cpp
+++ b/src/core/hid/motion_input.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/math_util.h"
#include "core/hid/motion_input.h"
diff --git a/src/core/hid/motion_input.h b/src/core/hid/motion_input.h
index bca4520fa..f5fd90db5 100644
--- a/src/core/hid/motion_input.h
+++ b/src/core/hid/motion_input.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/api_version.h b/src/core/hle/api_version.h
index 626e30753..bd15606e1 100644
--- a/src/core/hle/api_version.h
+++ b/src/core/hle/api_version.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 602e12606..416da15ec 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 026257115..d631c0357 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -19,7 +18,7 @@
namespace IPC {
-constexpr ResultCode ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
+constexpr Result ERR_REMOTE_PROCESS_DEAD{ErrorModule::HIPC, 301};
class RequestHelperBase {
protected:
@@ -176,7 +175,7 @@ public:
void PushImpl(float value);
void PushImpl(double value);
void PushImpl(bool value);
- void PushImpl(ResultCode value);
+ void PushImpl(Result value);
template <typename T>
void Push(T value) {
@@ -251,7 +250,7 @@ void ResponseBuilder::PushRaw(const T& value) {
index += (sizeof(T) + 3) / 4; // round up to word length
}
-inline void ResponseBuilder::PushImpl(ResultCode value) {
+inline void ResponseBuilder::PushImpl(Result value) {
// Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded.
Push(value.raw);
Push<u32>(0);
@@ -385,7 +384,7 @@ public:
T PopRaw();
template <class T>
- std::shared_ptr<T> PopIpcInterface() {
+ std::weak_ptr<T> PopIpcInterface() {
ASSERT(context->Session()->IsDomain());
ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainHandler<T>(Pop<u32>() - 1);
@@ -481,8 +480,8 @@ inline bool RequestParser::Pop() {
}
template <>
-inline ResultCode RequestParser::Pop() {
- return ResultCode{Pop<u32>()};
+inline Result RequestParser::Pop() {
+ return Result{Pop<u32>()};
}
template <typename T>
diff --git a/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc b/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc
index 857b512ba..2a0f2d5f7 100644
--- a/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc
+++ b/src/core/hle/kernel/arch/arm64/k_memory_region_device_types.inc
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
// All architectures must define NumArchitectureDeviceRegions.
constexpr inline const auto NumArchitectureDeviceRegions = 3;
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h
new file mode 100644
index 000000000..d02ee61c3
--- /dev/null
+++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_layout.h
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Kernel {
+
+constexpr inline PAddr MainMemoryAddress = 0x80000000;
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc b/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc
index 58d6c0b16..5f5ec6d5b 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc
+++ b/src/core/hle/kernel/board/nintendo/nx/k_memory_region_device_types.inc
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
// All architectures must define NumBoardDeviceRegions.
constexpr inline const auto NumBoardDeviceRegions = 6;
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 6f335c251..c10b7bf30 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
@@ -1,10 +1,10 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <random>
#include "common/literals.h"
+#include "common/settings.h"
#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
#include "core/hle/kernel/board/nintendo/nx/secure_monitor.h"
@@ -28,33 +28,20 @@ namespace {
using namespace Common::Literals;
-u32 GetMemoryModeForInit() {
- return 0x01;
-}
-
u32 GetMemorySizeForInit() {
- return 0;
+ return Settings::values.use_extended_memory_layout ? Smc::MemorySize_6GB : Smc::MemorySize_4GB;
}
Smc::MemoryArrangement GetMemoryArrangeForInit() {
- switch (GetMemoryModeForInit() & 0x3F) {
- case 0x01:
- default:
- return Smc::MemoryArrangement_4GB;
- case 0x02:
- return Smc::MemoryArrangement_4GBForAppletDev;
- case 0x03:
- return Smc::MemoryArrangement_4GBForSystemDev;
- case 0x11:
- return Smc::MemoryArrangement_6GB;
- case 0x12:
- return Smc::MemoryArrangement_6GBForAppletDev;
- case 0x21:
- return Smc::MemoryArrangement_8GB;
- }
+ return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_6GB
+ : Smc::MemoryArrangement_4GB;
}
} // namespace
+size_t KSystemControl::Init::GetRealMemorySize() {
+ return GetIntendedMemorySize();
+}
+
// Initialization.
size_t KSystemControl::Init::GetIntendedMemorySize() {
switch (GetMemorySizeForInit()) {
@@ -69,7 +56,13 @@ size_t KSystemControl::Init::GetIntendedMemorySize() {
}
PAddr KSystemControl::Init::GetKernelPhysicalBaseAddress(u64 base_address) {
- return base_address;
+ const size_t real_dram_size = KSystemControl::Init::GetRealMemorySize();
+ const size_t intended_dram_size = KSystemControl::Init::GetIntendedMemorySize();
+ if (intended_dram_size * 2 < real_dram_size) {
+ return base_address;
+ } else {
+ return base_address + ((real_dram_size - intended_dram_size) / 2);
+ }
}
bool KSystemControl::Init::ShouldIncreaseThreadResourceLimit() {
@@ -154,9 +147,9 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) {
} // Anonymous namespace
u64 KSystemControl::GenerateRandomU64() {
- static std::random_device device;
- static std::mt19937 gen(device());
- static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
+ std::random_device device;
+ std::mt19937 gen(device());
+ std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
return distribution(gen);
}
diff --git a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
index 52f230ced..fe375769e 100644
--- a/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
+++ b/src/core/hle/kernel/board/nintendo/nx/k_system_control.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -13,6 +12,7 @@ public:
class Init {
public:
// Initialization.
+ static std::size_t GetRealMemorySize();
static std::size_t GetIntendedMemorySize();
static PAddr GetKernelPhysicalBaseAddress(u64 base_address);
static bool ShouldIncreaseThreadResourceLimit();
diff --git a/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h b/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h
index f77a91dec..b0e4123f0 100644
--- a/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h
+++ b/src/core/hle/kernel/board/nintendo/nx/secure_monitor.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/code_set.cpp b/src/core/hle/kernel/code_set.cpp
index 1f434e9af..41386048b 100644
--- a/src/core/hle/kernel/code_set.cpp
+++ b/src/core/hle/kernel/code_set.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/code_set.h"
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h
index 5cc3b9829..5220dbcb6 100644
--- a/src/core/hle/kernel/code_set.h
+++ b/src/core/hle/kernel/code_set.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp
index baad2c5d6..65576b8c4 100644
--- a/src/core/hle/kernel/global_scheduler_context.cpp
+++ b/src/core/hle/kernel/global_scheduler_context.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
@@ -42,12 +41,7 @@ void GlobalSchedulerContext::PreemptThreads() {
ASSERT(IsLocked());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
const u32 priority = preemption_priorities[core_id];
- kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority);
-
- // Signal an interrupt occurred. For core 3, this is a certainty, as preemption will result
- // in the rotator thread being scheduled. For cores 0-2, this is to simulate or system
- // interrupts that may have occurred.
- kernel.PhysicalCore(core_id).Interrupt();
+ KScheduler::RotateScheduledQueue(kernel, core_id, priority);
}
}
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h
index 6f44b534f..67bb9852d 100644
--- a/src/core/hle/kernel/global_scheduler_context.h
+++ b/src/core/hle/kernel/global_scheduler_context.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,7 +7,6 @@
#include <vector>
#include "common/common_types.h"
-#include "common/spin_lock.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.h"
@@ -80,7 +78,7 @@ private:
/// Lists all thread ids that aren't deleted/etc.
std::vector<KThread*> thread_list;
- Common::SpinLock global_list_guard{};
+ std::mutex global_list_guard;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e19544c54..5b3feec66 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
@@ -17,7 +16,6 @@
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_process.h"
-#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_server_session.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
@@ -25,8 +23,15 @@
namespace Kernel {
-SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_)
- : kernel{kernel_}, service_thread{kernel.CreateServiceThread(service_name_)} {}
+SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
+ ServiceThreadType thread_type)
+ : kernel{kernel_} {
+ if (thread_type == ServiceThreadType::CreateNew) {
+ service_thread = kernel.CreateServiceThread(service_name_);
+ } else {
+ service_thread = kernel.GetDefaultServiceThread();
+ }
+}
SessionRequestHandler::~SessionRequestHandler() {
kernel.ReleaseServiceThread(service_thread);
@@ -45,7 +50,7 @@ bool SessionRequestManager::HasSessionRequestHandler(const HLERequestContext& co
LOG_CRITICAL(IPC, "object_id {} is too big!", object_id);
return false;
}
- return DomainHandler(object_id - 1) != nullptr;
+ return !DomainHandler(object_id - 1).expired();
} else {
return session_handler != nullptr;
}
@@ -55,7 +60,7 @@ void SessionRequestHandler::ClientConnected(KServerSession* session) {
session->ClientConnected(shared_from_this());
// Ensure our server session is tracked globally.
- kernel.RegisterServerSession(session);
+ kernel.RegisterServerObject(session);
}
void SessionRequestHandler::ClientDisconnected(KServerSession* session) {
@@ -183,8 +188,8 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
}
-ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
- u32_le* src_cmdbuf) {
+Result HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
+ u32_le* src_cmdbuf) {
ParseCommandBuffer(handle_table, src_cmdbuf, true);
if (command_header->IsCloseCommand()) {
@@ -197,7 +202,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const KHandleTab
return ResultSuccess;
}
-ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) {
+Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_thread) {
auto current_offset = handles_offset;
auto& owner_process = *requesting_thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
@@ -282,15 +287,49 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
BufferDescriptorB().size() > buffer_index &&
BufferDescriptorB()[buffer_index].Size() >= size,
{ return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size);
- memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+ WriteBufferB(buffer, size, buffer_index);
} else {
ASSERT_OR_EXECUTE_MSG(
BufferDescriptorC().size() > buffer_index &&
BufferDescriptorC()[buffer_index].Size() >= size,
{ return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size);
- memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
+ WriteBufferC(buffer, size, buffer_index);
+ }
+
+ return size;
+}
+
+std::size_t HLERequestContext::WriteBufferB(const void* buffer, std::size_t size,
+ std::size_t buffer_index) const {
+ if (buffer_index >= BufferDescriptorB().size() || size == 0) {
+ return 0;
+ }
+
+ const auto buffer_size{BufferDescriptorB()[buffer_index].Size()};
+ if (size > buffer_size) {
+ LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
+ buffer_size);
+ size = buffer_size; // TODO(bunnei): This needs to be HW tested
+ }
+
+ memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
+ return size;
+}
+
+std::size_t HLERequestContext::WriteBufferC(const void* buffer, std::size_t size,
+ std::size_t buffer_index) const {
+ if (buffer_index >= BufferDescriptorC().size() || size == 0) {
+ return 0;
+ }
+
+ const auto buffer_size{BufferDescriptorC()[buffer_index].Size()};
+ if (size > buffer_size) {
+ LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size,
+ buffer_size);
+ size = buffer_size; // TODO(bunnei): This needs to be HW tested
}
+ memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);
return size;
}
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 754b41ff6..99265ce90 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -19,7 +18,7 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/svc_common.h"
-union ResultCode;
+union Result;
namespace Core::Memory {
class Memory;
@@ -33,6 +32,11 @@ namespace Service {
class ServiceFrameworkBase;
}
+enum class ServiceThreadType {
+ Default,
+ CreateNew,
+};
+
namespace Kernel {
class Domain;
@@ -57,7 +61,8 @@ enum class ThreadWakeupReason;
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
- SessionRequestHandler(KernelCore& kernel, const char* service_name_);
+ SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
+ ServiceThreadType thread_type);
virtual ~SessionRequestHandler();
/**
@@ -66,10 +71,10 @@ public:
* it should be used to differentiate which client (As in ClientSession) we're answering to.
* TODO(Subv): Use a wrapper structure to hold all the information relevant to
* this request (ServerSession, Originator thread, Translated command buffer, etc).
- * @returns ResultCode the result code of the translate operation.
+ * @returns Result the result code of the translate operation.
*/
- virtual ResultCode HandleSyncRequest(Kernel::KServerSession& session,
- Kernel::HLERequestContext& context) = 0;
+ virtual Result HandleSyncRequest(Kernel::KServerSession& session,
+ Kernel::HLERequestContext& context) = 0;
/**
* Signals that a client has just connected to this HLE handler and keeps the
@@ -94,6 +99,7 @@ protected:
std::weak_ptr<ServiceThread> service_thread;
};
+using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
/**
@@ -135,11 +141,11 @@ public:
if (index < DomainHandlerCount()) {
domain_handlers[index] = nullptr;
} else {
- UNREACHABLE_MSG("Unexpected handler index {}", index);
+ ASSERT_MSG(false, "Unexpected handler index {}", index);
}
}
- SessionRequestHandlerPtr DomainHandler(std::size_t index) const {
+ SessionRequestHandlerWeakPtr DomainHandler(std::size_t index) const {
ASSERT_MSG(index < DomainHandlerCount(), "Unexpected handler index {}", index);
return domain_handlers.at(index);
}
@@ -206,11 +212,10 @@ public:
}
/// Populates this context with data from the requesting process/thread.
- ResultCode PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table,
- u32_le* src_cmdbuf);
+ Result PopulateFromIncomingCommandBuffer(const KHandleTable& handle_table, u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
- ResultCode WriteToOutgoingCommandBuffer(KThread& requesting_thread);
+ Result WriteToOutgoingCommandBuffer(KThread& requesting_thread);
u32_le GetHipcCommand() const {
return command;
@@ -272,6 +277,14 @@ public:
std::size_t WriteBuffer(const void* buffer, std::size_t size,
std::size_t buffer_index = 0) const;
+ /// Helper function to write buffer B
+ std::size_t WriteBufferB(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
+
+ /// Helper function to write buffer C
+ std::size_t WriteBufferC(const void* buffer, std::size_t size,
+ std::size_t buffer_index = 0) const;
+
/* Helper function to write a buffer using the appropriate buffer descriptor
*
* @tparam T an arbitrary container that satisfies the
@@ -328,10 +341,10 @@ public:
template <typename T>
std::shared_ptr<T> GetDomainHandler(std::size_t index) const {
- return std::static_pointer_cast<T>(manager->DomainHandler(index));
+ return std::static_pointer_cast<T>(manager.lock()->DomainHandler(index).lock());
}
- void SetSessionRequestManager(std::shared_ptr<SessionRequestManager> manager_) {
+ void SetSessionRequestManager(std::weak_ptr<SessionRequestManager> manager_) {
manager = std::move(manager_);
}
@@ -374,7 +387,7 @@ private:
u32 handles_offset{};
u32 domain_offset{};
- std::shared_ptr<SessionRequestManager> manager;
+ std::weak_ptr<SessionRequestManager> manager;
KernelCore& kernel;
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..9b6b284d0 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -1,25 +1,28 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/core.h"
+#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
+#include "core/hle/kernel/k_page_buffer.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_session.h"
#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_shared_memory_info.h"
#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/k_thread.h"
+#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_transfer_memory.h"
namespace Kernel::Init {
@@ -32,9 +35,13 @@ namespace Kernel::Init {
HANDLER(KEvent, (SLAB_COUNT(KEvent)), ##__VA_ARGS__) \
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
+ HANDLER(KSharedMemoryInfo, (SLAB_COUNT(KSharedMemory) * 8), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
+ HANDLER(KThreadLocalPage, \
+ (SLAB_COUNT(KProcess) + (SLAB_COUNT(KProcess) + SLAB_COUNT(KThread)) / 8), \
+ ##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
namespace {
@@ -50,38 +57,46 @@ enum KSlabType : u32 {
// Constexpr counts.
constexpr size_t SlabCountKProcess = 80;
constexpr size_t SlabCountKThread = 800;
-constexpr size_t SlabCountKEvent = 700;
+constexpr size_t SlabCountKEvent = 900;
constexpr size_t SlabCountKInterruptEvent = 100;
-constexpr size_t SlabCountKPort = 256 + 0x20; // Extra 0x20 ports over Nintendo for homebrew.
+constexpr size_t SlabCountKPort = 384;
constexpr size_t SlabCountKSharedMemory = 80;
constexpr size_t SlabCountKTransferMemory = 200;
constexpr size_t SlabCountKCodeMemory = 10;
constexpr size_t SlabCountKDeviceAddressSpace = 300;
-constexpr size_t SlabCountKSession = 933;
+constexpr size_t SlabCountKSession = 1133;
constexpr size_t SlabCountKLightSession = 100;
constexpr size_t SlabCountKObjectName = 7;
constexpr size_t SlabCountKResourceLimit = 5;
constexpr size_t SlabCountKDebug = Core::Hardware::NUM_CPU_CORES;
-constexpr size_t SlabCountKAlpha = 1;
-constexpr size_t SlabCountKBeta = 6;
+constexpr size_t SlabCountKIoPool = 1;
+constexpr size_t SlabCountKIoRegion = 6;
constexpr size_t SlabCountExtraKThread = 160;
+/// Helper function to translate from the slab virtual address to the reserved location in physical
+/// memory.
+static PAddr TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout, VAddr slab_addr) {
+ slab_addr -= memory_layout.GetSlabRegionAddress();
+ return slab_addr + Core::DramMemoryMap::SlabHeapBase;
+}
+
template <typename T>
VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAddr address,
size_t num_objects) {
- // TODO(bunnei): This is just a place holder. We should initialize the appropriate KSlabHeap for
- // kernel object type T with the backing kernel memory pointer once we emulate kernel memory.
const size_t size = Common::AlignUp(sizeof(T) * num_objects, alignof(void*));
VAddr start = Common::AlignUp(address, alignof(T));
- // This is intentionally empty. Once KSlabHeap is fully implemented, we can replace this with
- // the pointer to emulated memory to pass along. Until then, KSlabHeap will just allocate/free
- // host memory.
- void* backing_kernel_memory{};
+ // This should use the virtual memory address passed in, but currently, we do not setup the
+ // kernel virtual memory layout. Instead, we simply map these at a region of physical memory
+ // that we reserve for the slab heaps.
+ // TODO(bunnei): Fix this once we support the kernel virtual memory layout.
if (size > 0) {
+ void* backing_kernel_memory{
+ system.DeviceMemory().GetPointer(TranslateSlabAddrToPhysical(memory_layout, start))};
+
const KMemoryRegion* region = memory_layout.FindVirtual(start + size - 1);
ASSERT(region != nullptr);
ASSERT(region->IsDerivedFrom(KMemoryRegionType_KernelSlab));
@@ -91,6 +106,12 @@ VAddr InitializeSlabHeap(Core::System& system, KMemoryLayout& memory_layout, VAd
return start + size;
}
+size_t CalculateSlabHeapGapSize() {
+ constexpr size_t KernelSlabHeapGapSize = 2_MiB - 296_KiB;
+ static_assert(KernelSlabHeapGapSize <= KernelSlabHeapGapsSizeMax);
+ return KernelSlabHeapGapSize;
+}
+
} // namespace
KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
@@ -109,8 +130,8 @@ KSlabResourceCounts KSlabResourceCounts::CreateDefault() {
.num_KObjectName = SlabCountKObjectName,
.num_KResourceLimit = SlabCountKResourceLimit,
.num_KDebug = SlabCountKDebug,
- .num_KAlpha = SlabCountKAlpha,
- .num_KBeta = SlabCountKBeta,
+ .num_KIoPool = SlabCountKIoPool,
+ .num_KIoRegion = SlabCountKIoRegion,
};
}
@@ -136,11 +157,34 @@ size_t CalculateTotalSlabHeapSize(const KernelCore& kernel) {
#undef ADD_SLAB_SIZE
// Add the reserved size.
- size += KernelSlabHeapGapsSize;
+ size += CalculateSlabHeapGapSize();
return size;
}
+void InitializeKPageBufferSlabHeap(Core::System& system) {
+ auto& kernel = system.Kernel();
+
+ const auto& counts = kernel.SlabResourceCounts();
+ const size_t num_pages =
+ counts.num_KProcess + counts.num_KThread + (counts.num_KProcess + counts.num_KThread) / 8;
+ const size_t slab_size = num_pages * PageSize;
+
+ // Reserve memory from the system resource limit.
+ ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size));
+
+ // Allocate memory for the slab.
+ constexpr auto AllocateOption = KMemoryManager::EncodeOption(
+ KMemoryManager::Pool::System, KMemoryManager::Direction::FromFront);
+ const PAddr slab_address =
+ kernel.MemoryManager().AllocateAndOpenContinuous(num_pages, 1, AllocateOption);
+ ASSERT(slab_address != 0);
+
+ // Initialize the slabheap.
+ KPageBuffer::InitializeSlabHeap(kernel, system.DeviceMemory().GetPointer(slab_address),
+ slab_size);
+}
+
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
auto& kernel = system.Kernel();
@@ -160,13 +204,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
}
// Create an array to represent the gaps between the slabs.
- const size_t total_gap_size = KernelSlabHeapGapsSize;
+ const size_t total_gap_size = CalculateSlabHeapGapSize();
std::array<size_t, slab_types.size()> slab_gaps;
- for (size_t i = 0; i < slab_gaps.size(); i++) {
+ for (auto& slab_gap : slab_gaps) {
// Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange
// is inclusive. However, Nintendo also has the off-by-one error, and it's "harmless", so we
// will include it ourselves.
- slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size);
+ slab_gap = KSystemControl::GenerateRandomRange(0, total_gap_size);
}
// Sort the array, so that we can treat differences between values as offsets to the starts of
@@ -177,13 +221,21 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
}
}
- for (size_t i = 0; i < slab_types.size(); i++) {
+ // Track the gaps, so that we can free them to the unused slab tree.
+ VAddr gap_start = address;
+ size_t gap_size = 0;
+
+ for (size_t i = 0; i < slab_gaps.size(); i++) {
// Add the random gap to the address.
- address += (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
+ const auto cur_gap = (i == 0) ? slab_gaps[0] : slab_gaps[i] - slab_gaps[i - 1];
+ address += cur_gap;
+ gap_size += cur_gap;
#define INITIALIZE_SLAB_HEAP(NAME, COUNT, ...) \
case KSlabType_##NAME: \
- address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
+ if (COUNT > 0) { \
+ address = InitializeSlabHeap<NAME>(system, memory_layout, address, COUNT); \
+ } \
break;
// Initialize the slabheap.
@@ -192,7 +244,13 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
FOREACH_SLAB_TYPE(INITIALIZE_SLAB_HEAP)
// If we somehow get an invalid type, abort.
default:
- UNREACHABLE();
+ ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]);
+ }
+
+ // If we've hit the end of a gap, free it.
+ if (gap_start + gap_size != address) {
+ gap_start = address;
+ gap_size = 0;
}
}
}
diff --git a/src/core/hle/kernel/init/init_slab_setup.h b/src/core/hle/kernel/init/init_slab_setup.h
index a8f7e0918..13be63c87 100644
--- a/src/core/hle/kernel/init/init_slab_setup.h
+++ b/src/core/hle/kernel/init/init_slab_setup.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -32,12 +31,13 @@ struct KSlabResourceCounts {
size_t num_KObjectName;
size_t num_KResourceLimit;
size_t num_KDebug;
- size_t num_KAlpha;
- size_t num_KBeta;
+ size_t num_KIoPool;
+ size_t num_KIoRegion;
};
void InitializeSlabResourceCounts(KernelCore& kernel);
size_t CalculateTotalSlabHeapSize(const KernelCore& kernel);
+void InitializeKPageBufferSlabHeap(Core::System& system);
void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout);
} // namespace Kernel::Init
diff --git a/src/core/hle/kernel/initial_process.h b/src/core/hle/kernel/initial_process.h
new file mode 100644
index 000000000..af0fb23b6
--- /dev/null
+++ b/src/core/hle/kernel/initial_process.h
@@ -0,0 +1,22 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/literals.h"
+#include "core/hle/kernel/board/nintendo/nx/k_memory_layout.h"
+#include "core/hle/kernel/board/nintendo/nx/k_system_control.h"
+
+namespace Kernel {
+
+using namespace Common::Literals;
+
+constexpr std::size_t InitialProcessBinarySizeMax = 12_MiB;
+
+static inline PAddr GetInitialProcessBinaryPhysicalAddress() {
+ return Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetKernelPhysicalBaseAddress(
+ MainMemoryAddress);
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp
index 783c69858..f85b11557 100644
--- a/src/core/hle/kernel/k_address_arbiter.cpp
+++ b/src/core/hle/kernel/k_address_arbiter.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
@@ -49,7 +48,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
}
} else {
// Otherwise, clear our exclusive hold and finish
- monitor.ClearExclusive();
+ monitor.ClearExclusive(current_core);
}
// We're done.
@@ -78,7 +77,7 @@ bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32
}
} else {
// Otherwise, clear our exclusive hold and finish.
- monitor.ClearExclusive();
+ monitor.ClearExclusive(current_core);
}
// We're done.
@@ -91,8 +90,7 @@ public:
explicit ThreadQueueImplForKAddressArbiter(KernelCore& kernel_, KAddressArbiter::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// If the thread is waiting on an address arbiter, remove it from the tree.
if (waiting_thread->IsWaitingForAddressArbiter()) {
m_tree->erase(m_tree->iterator_to(*waiting_thread));
@@ -109,13 +107,13 @@ private:
} // namespace
-ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
+Result KAddressArbiter::Signal(VAddr addr, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
KScopedSchedulerLock sl(kernel);
- auto it = thread_tree.nfind_light({addr, -1});
+ auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
@@ -132,7 +130,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
return ResultSuccess;
}
-ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
+Result KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
@@ -148,7 +146,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
return ResultInvalidState;
}
- auto it = thread_tree.nfind_light({addr, -1});
+ auto it = thread_tree.nfind_key({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) {
// End the thread's wait.
@@ -165,13 +163,13 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
return ResultSuccess;
}
-ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
+Result KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count) {
// Perform signaling.
s32 num_waiters{};
{
[[maybe_unused]] const KScopedSchedulerLock sl(kernel);
- auto it = thread_tree.nfind_light({addr, -1});
+ auto it = thread_tree.nfind_key({addr, -1});
// Determine the updated value.
s32 new_value{};
if (count <= 0) {
@@ -233,9 +231,9 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
return ResultSuccess;
}
-ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
+Result KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait.
- KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
@@ -286,9 +284,9 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
return cur_thread->GetWaitResult();
}
-ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
+Result KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait.
- KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKAddressArbiter wait_queue(kernel, std::addressof(thread_tree));
{
diff --git a/src/core/hle/kernel/k_address_arbiter.h b/src/core/hle/kernel/k_address_arbiter.h
index bf8b46665..e4085ae22 100644
--- a/src/core/hle/kernel/k_address_arbiter.h
+++ b/src/core/hle/kernel/k_address_arbiter.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -9,7 +8,7 @@
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/svc_types.h"
-union ResultCode;
+union Result;
namespace Core {
class System;
@@ -26,8 +25,7 @@ public:
explicit KAddressArbiter(Core::System& system_);
~KAddressArbiter();
- [[nodiscard]] ResultCode SignalToAddress(VAddr addr, Svc::SignalType type, s32 value,
- s32 count) {
+ [[nodiscard]] Result SignalToAddress(VAddr addr, Svc::SignalType type, s32 value, s32 count) {
switch (type) {
case Svc::SignalType::Signal:
return Signal(addr, count);
@@ -36,12 +34,12 @@ public:
case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual:
return SignalAndModifyByWaitingCountIfEqual(addr, value, count);
}
- UNREACHABLE();
+ ASSERT(false);
return ResultUnknown;
}
- [[nodiscard]] ResultCode WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
- s64 timeout) {
+ [[nodiscard]] Result WaitForAddress(VAddr addr, Svc::ArbitrationType type, s32 value,
+ s64 timeout) {
switch (type) {
case Svc::ArbitrationType::WaitIfLessThan:
return WaitIfLessThan(addr, value, false, timeout);
@@ -50,16 +48,16 @@ public:
case Svc::ArbitrationType::WaitIfEqual:
return WaitIfEqual(addr, value, timeout);
}
- UNREACHABLE();
+ ASSERT(false);
return ResultUnknown;
}
private:
- [[nodiscard]] ResultCode Signal(VAddr addr, s32 count);
- [[nodiscard]] ResultCode SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
- [[nodiscard]] ResultCode SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
- [[nodiscard]] ResultCode WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
- [[nodiscard]] ResultCode WaitIfEqual(VAddr addr, s32 value, s64 timeout);
+ [[nodiscard]] Result Signal(VAddr addr, s32 count);
+ [[nodiscard]] Result SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 count);
+ [[nodiscard]] Result SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 value, s32 count);
+ [[nodiscard]] Result WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout);
+ [[nodiscard]] Result WaitIfEqual(VAddr addr, s32 value, s64 timeout);
ThreadTree thread_tree;
diff --git a/src/core/hle/kernel/k_address_space_info.cpp b/src/core/hle/kernel/k_address_space_info.cpp
index ca29edc88..3e612a207 100644
--- a/src/core/hle/kernel/k_address_space_info.cpp
+++ b/src/core/hle/kernel/k_address_space_info.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
@@ -85,7 +84,7 @@ u64 KAddressSpaceInfo::GetAddressSpaceStart(std::size_t width, Type type) {
ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index]));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].address;
}
- UNREACHABLE();
+ ASSERT(false);
return 0;
}
@@ -102,7 +101,7 @@ std::size_t KAddressSpaceInfo::GetAddressSpaceSize(std::size_t width, Type type)
ASSERT(IsAllowed39BitType(type));
return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].size;
}
- UNREACHABLE();
+ ASSERT(false);
return 0;
}
diff --git a/src/core/hle/kernel/k_address_space_info.h b/src/core/hle/kernel/k_address_space_info.h
index 06f31c6d5..69e9d77f2 100644
--- a/src/core/hle/kernel/k_address_space_info.h
+++ b/src/core/hle/kernel/k_address_space_info.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_affinity_mask.h b/src/core/hle/kernel/k_affinity_mask.h
index cf704ce87..b58716e90 100644
--- a/src/core/hle/kernel/k_affinity_mask.h
+++ b/src/core/hle/kernel/k_affinity_mask.h
@@ -1,9 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// This file references various implementation details from Atmosphere, an open-source firmware for
-// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_auto_object.cpp b/src/core/hle/kernel/k_auto_object.cpp
index c99a9ebb7..691af8ccb 100644
--- a/src/core/hle/kernel/k_auto_object.cpp
+++ b/src/core/hle/kernel/k_auto_object.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/kernel.h"
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h
index 05779f2d5..2827763d5 100644
--- a/src/core/hle/kernel/k_auto_object.h
+++ b/src/core/hle/kernel/k_auto_object.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -19,7 +18,7 @@ namespace Kernel {
class KernelCore;
class KProcess;
-#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
+#define KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, ATTRIBUTE) \
\
private: \
friend class ::Kernel::KClassTokenGenerator; \
@@ -41,16 +40,19 @@ public:
static constexpr const char* GetStaticTypeName() { \
return TypeName; \
} \
- virtual TypeObj GetTypeObj() const { \
+ virtual TypeObj GetTypeObj() ATTRIBUTE { \
return GetStaticTypeObj(); \
} \
- virtual const char* GetTypeName() const { \
+ virtual const char* GetTypeName() ATTRIBUTE { \
return GetStaticTypeName(); \
} \
\
private: \
constexpr bool operator!=(const TypeObj& rhs)
+#define KERNEL_AUTOOBJECT_TRAITS(CLASS, BASE_CLASS) \
+ KERNEL_AUTOOBJECT_TRAITS_IMPL(CLASS, BASE_CLASS, const override)
+
class KAutoObject {
protected:
class TypeObj {
@@ -83,15 +85,13 @@ protected:
};
private:
- KERNEL_AUTOOBJECT_TRAITS(KAutoObject, KAutoObject);
+ KERNEL_AUTOOBJECT_TRAITS_IMPL(KAutoObject, KAutoObject, const);
public:
explicit KAutoObject(KernelCore& kernel_) : kernel(kernel_) {
RegisterWithKernel();
}
- virtual ~KAutoObject() {
- UnregisterWithKernel();
- }
+ virtual ~KAutoObject() = default;
static KAutoObject* Create(KAutoObject* ptr);
@@ -163,11 +163,12 @@ public:
do {
ASSERT(cur_ref_count > 0);
} while (!m_ref_count.compare_exchange_weak(cur_ref_count, cur_ref_count - 1,
- std::memory_order_relaxed));
+ std::memory_order_acq_rel));
// If ref count hits zero, destroy the object.
if (cur_ref_count - 1 == 0) {
this->Destroy();
+ this->UnregisterWithKernel();
}
}
diff --git a/src/core/hle/kernel/k_auto_object_container.cpp b/src/core/hle/kernel/k_auto_object_container.cpp
index d5f80d5b2..636b3f993 100644
--- a/src/core/hle/kernel/k_auto_object_container.cpp
+++ b/src/core/hle/kernel/k_auto_object_container.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
diff --git a/src/core/hle/kernel/k_auto_object_container.h b/src/core/hle/kernel/k_auto_object_container.h
index 697cc4289..badd75d2a 100644
--- a/src/core/hle/kernel/k_auto_object_container.h
+++ b/src/core/hle/kernel/k_auto_object_container.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
index 21e2fe494..cc2a0f7ca 100644
--- a/src/core/hle/kernel/k_class_token.cpp
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_class_token.h"
diff --git a/src/core/hle/kernel/k_class_token.h b/src/core/hle/kernel/k_class_token.h
index 980010150..c9001ae3d 100644
--- a/src/core/hle/kernel/k_class_token.h
+++ b/src/core/hle/kernel/k_class_token.h
@@ -1,11 +1,8 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <atomic>
-
#include "common/bit_util.h"
#include "common/common_types.h"
@@ -52,6 +49,7 @@ private:
}
}
}
+ UNREACHABLE();
}();
template <typename T>
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index ef168fe87..3cb22ff4d 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -59,8 +58,8 @@ bool KClientPort::IsSignaled() const {
return num_sessions < max_sessions;
}
-ResultCode KClientPort::CreateSession(KClientSession** out,
- std::shared_ptr<SessionRequestManager> session_manager) {
+Result KClientPort::CreateSession(KClientSession** out,
+ std::shared_ptr<SessionRequestManager> session_manager) {
// Reserve a new session from the resource limit.
KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(),
LimitableResource::Sessions);
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index 54bb05e20..e17eff28f 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -53,8 +52,8 @@ public:
void Destroy() override;
bool IsSignaled() const override;
- ResultCode CreateSession(KClientSession** out,
- std::shared_ptr<SessionRequestManager> session_manager = nullptr);
+ Result CreateSession(KClientSession** out,
+ std::shared_ptr<SessionRequestManager> session_manager = nullptr);
private:
std::atomic<s32> num_sessions{};
diff --git a/src/core/hle/kernel/k_client_session.cpp b/src/core/hle/kernel/k_client_session.cpp
index 242582f8f..b2a887b14 100644
--- a/src/core/hle/kernel/k_client_session.cpp
+++ b/src/core/hle/kernel/k_client_session.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_client_session.h"
@@ -22,8 +21,8 @@ void KClientSession::Destroy() {
void KClientSession::OnServerClosed() {}
-ResultCode KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
- Core::Timing::CoreTiming& core_timing) {
+Result KClientSession::SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing) {
// Signal the server session that new data is available
return parent->GetServerSession().HandleSyncRequest(thread, memory, core_timing);
}
diff --git a/src/core/hle/kernel/k_client_session.h b/src/core/hle/kernel/k_client_session.h
index ad6cc4ed1..0c750d756 100644
--- a/src/core/hle/kernel/k_client_session.h
+++ b/src/core/hle/kernel/k_client_session.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,7 +9,7 @@
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
-union ResultCode;
+union Result;
namespace Core::Memory {
class Memory;
@@ -47,8 +46,8 @@ public:
return parent;
}
- ResultCode SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
- Core::Timing::CoreTiming& core_timing);
+ Result SendSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing);
void OnServerClosed();
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
index 0b225e8e0..da57ceb21 100644
--- a/src/core/hle/kernel/k_code_memory.cpp
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -1,15 +1,13 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/common_types.h"
#include "core/device_memory.h"
-#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
@@ -21,7 +19,7 @@ namespace Kernel {
KCodeMemory::KCodeMemory(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
-ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
+Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
// Set members.
m_owner = kernel.CurrentProcess();
@@ -29,10 +27,10 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
auto& page_table = m_owner->PageTable();
// Construct the page group.
- m_page_group = KPageLinkedList(addr, Common::DivideUp(size, PageSize));
+ m_page_group = {};
// Lock the memory.
- R_TRY(page_table.LockForCodeMemory(addr, size))
+ R_TRY(page_table.LockForCodeMemory(&m_page_group, addr, size))
// Clear the memory.
for (const auto& block : m_page_group.Nodes()) {
@@ -40,6 +38,7 @@ ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr
}
// Set remaining tracking members.
+ m_owner->Open();
m_address = addr;
m_is_initialized = true;
m_is_owner_mapped = false;
@@ -53,11 +52,17 @@ void KCodeMemory::Finalize() {
// Unlock.
if (!m_is_mapped && !m_is_owner_mapped) {
const size_t size = m_page_group.GetNumPages() * PageSize;
- m_owner->PageTable().UnlockForCodeMemory(m_address, size);
+ m_owner->PageTable().UnlockForCodeMemory(m_address, size, m_page_group);
}
+
+ // Close the page group.
+ m_page_group = {};
+
+ // Close our reference to our owner.
+ m_owner->Close();
}
-ResultCode KCodeMemory::Map(VAddr address, size_t size) {
+Result KCodeMemory::Map(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
@@ -77,7 +82,7 @@ ResultCode KCodeMemory::Map(VAddr address, size_t size) {
return ResultSuccess;
}
-ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
+Result KCodeMemory::Unmap(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
@@ -94,7 +99,7 @@ ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
return ResultSuccess;
}
-ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
+Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
@@ -114,7 +119,8 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis
k_perm = KMemoryPermission::UserReadExecute;
break;
default:
- break;
+ // Already validated by ControlCodeMemory svc
+ UNREACHABLE();
}
// Map the memory.
@@ -127,7 +133,7 @@ ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermis
return ResultSuccess;
}
-ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
+Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
// Validate the size.
R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
index e0ba19a53..2e7e1436a 100644
--- a/src/core/hle/kernel/k_code_memory.h
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,7 +7,7 @@
#include "core/device_memory.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_light_lock.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
@@ -30,20 +29,20 @@ class KCodeMemory final
public:
explicit KCodeMemory(KernelCore& kernel_);
- ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
- void Finalize();
+ Result Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
+ void Finalize() override;
- ResultCode Map(VAddr address, size_t size);
- ResultCode Unmap(VAddr address, size_t size);
- ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
- ResultCode UnmapFromOwner(VAddr address, size_t size);
+ Result Map(VAddr address, size_t size);
+ Result Unmap(VAddr address, size_t size);
+ Result MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
+ Result UnmapFromOwner(VAddr address, size_t size);
- bool IsInitialized() const {
+ bool IsInitialized() const override {
return m_is_initialized;
}
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
- KProcess* GetOwner() const {
+ KProcess* GetOwner() const override {
return m_owner;
}
VAddr GetSourceAddress() const {
@@ -54,7 +53,7 @@ public:
}
private:
- KPageLinkedList m_page_group{};
+ KPageGroup m_page_group{};
KProcess* m_owner{};
VAddr m_address{};
KLightLock m_lock;
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp
index aadcc297a..124149697 100644
--- a/src/core/hle/kernel/k_condition_variable.cpp
+++ b/src/core/hle/kernel/k_condition_variable.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
@@ -9,7 +8,6 @@
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
-#include "core/hle/kernel/k_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
@@ -63,8 +61,7 @@ public:
explicit ThreadQueueImplForKConditionVariableWaitForAddress(KernelCore& kernel_)
: KThreadQueue(kernel_) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
waiting_thread->GetLockOwner()->RemoveWaiter(waiting_thread);
@@ -82,8 +79,7 @@ public:
KernelCore& kernel_, KConditionVariable::ThreadTree* t)
: KThreadQueue(kernel_), m_tree(t) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
@@ -107,8 +103,8 @@ KConditionVariable::KConditionVariable(Core::System& system_)
KConditionVariable::~KConditionVariable() = default;
-ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
- KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
+Result KConditionVariable::SignalToAddress(VAddr addr) {
+ KThread* owner_thread = GetCurrentThreadPointer(kernel);
// Signal the address.
{
@@ -128,7 +124,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
}
// Write the value to userspace.
- ResultCode result{ResultSuccess};
+ Result result{ResultSuccess};
if (WriteToUser(system, addr, std::addressof(next_value))) [[likely]] {
result = ResultSuccess;
} else {
@@ -148,8 +144,8 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
}
}
-ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
- KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
+Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) {
+ KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitForAddress wait_queue(kernel);
// Wait for the address.
@@ -244,7 +240,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
{
KScopedSchedulerLock sl(kernel);
- auto it = thread_tree.nfind_light({cv_key, -1});
+ auto it = thread_tree.nfind_key({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) {
KThread* target_thread = std::addressof(*it);
@@ -263,7 +259,7 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
}
}
-ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
+Result KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait.
KThread* cur_thread = GetCurrentThreadPointer(kernel);
ThreadQueueImplForKConditionVariableWaitConditionVariable wait_queue(
diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h
index 5e4815d08..fad4ed011 100644
--- a/src/core/hle/kernel/k_condition_variable.h
+++ b/src/core/hle/kernel/k_condition_variable.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -26,12 +25,12 @@ public:
~KConditionVariable();
// Arbitration
- [[nodiscard]] ResultCode SignalToAddress(VAddr addr);
- [[nodiscard]] ResultCode WaitForAddress(Handle handle, VAddr addr, u32 value);
+ [[nodiscard]] Result SignalToAddress(VAddr addr);
+ [[nodiscard]] Result WaitForAddress(Handle handle, VAddr addr, u32 value);
// Condition variable
void Signal(u64 cv_key, s32 count);
- [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
+ [[nodiscard]] Result Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private:
void SignalImpl(KThread* thread);
diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp
index 0720efece..e52fafbc7 100644
--- a/src/core/hle/kernel/k_event.cpp
+++ b/src/core/hle/kernel/k_event.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_process.h"
@@ -14,7 +13,7 @@ KEvent::KEvent(KernelCore& kernel_)
KEvent::~KEvent() = default;
-void KEvent::Initialize(std::string&& name_) {
+void KEvent::Initialize(std::string&& name_, KProcess* owner_) {
// Increment reference count.
// Because reference count is one on creation, this will result
// in a reference count of two. Thus, when both readable and
@@ -30,10 +29,8 @@ void KEvent::Initialize(std::string&& name_) {
writable_event.Initialize(this, name_ + ":Writable");
// Set our owner process.
- owner = kernel.CurrentProcess();
- if (owner) {
- owner->Open();
- }
+ owner = owner_;
+ owner->Open();
// Mark initialized.
name = std::move(name_);
@@ -47,10 +44,8 @@ void KEvent::Finalize() {
void KEvent::PostDestroy(uintptr_t arg) {
// Release the event count resource the owner process holds.
KProcess* owner = reinterpret_cast<KProcess*>(arg);
- if (owner) {
- owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
- owner->Close();
- }
+ owner->GetResourceLimit()->Release(LimitableResource::Events, 1);
+ owner->Close();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_event.h b/src/core/hle/kernel/k_event.h
index 3d3ec99e2..2ff828feb 100644
--- a/src/core/hle/kernel/k_event.h
+++ b/src/core/hle/kernel/k_event.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -22,7 +21,7 @@ public:
explicit KEvent(KernelCore& kernel_);
~KEvent() override;
- void Initialize(std::string&& name);
+ void Initialize(std::string&& name, KProcess* owner_);
void Finalize() override;
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp
index cf95f0852..e830ca46e 100644
--- a/src/core/hle/kernel/k_handle_table.cpp
+++ b/src/core/hle/kernel/k_handle_table.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_handle_table.h"
@@ -9,7 +8,7 @@ namespace Kernel {
KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
KHandleTable::~KHandleTable() = default;
-ResultCode KHandleTable::Finalize() {
+Result KHandleTable::Finalize() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
@@ -63,7 +62,7 @@ bool KHandleTable::Remove(Handle handle) {
return true;
}
-ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
+Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
@@ -75,7 +74,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
const auto linear_id = this->AllocateLinearId();
const auto index = this->AllocateEntry();
- m_entry_infos[index].info = {.linear_id = linear_id, .type = type};
+ m_entry_infos[index].linear_id = linear_id;
m_objects[index] = obj;
obj->Open();
@@ -86,7 +85,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
return ResultSuccess;
}
-ResultCode KHandleTable::Reserve(Handle* out_handle) {
+Result KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
@@ -116,7 +115,7 @@ void KHandleTable::Unreserve(Handle handle) {
}
}
-void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
+void KHandleTable::Register(Handle handle, KAutoObject* obj) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
@@ -132,7 +131,7 @@ void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
// Set the entry.
ASSERT(m_objects[index] == nullptr);
- m_entry_infos[index].info = {.linear_id = static_cast<u16>(linear_id), .type = type};
+ m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
m_objects[index] = obj;
obj->Open();
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h
index 87004a0f9..0864a737c 100644
--- a/src/core/hle/kernel/k_handle_table.h
+++ b/src/core/hle/kernel/k_handle_table.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -31,7 +30,7 @@ public:
explicit KHandleTable(KernelCore& kernel_);
~KHandleTable();
- ResultCode Initialize(s32 size) {
+ Result Initialize(s32 size) {
R_UNLESS(size <= static_cast<s32>(MaxTableSize), ResultOutOfMemory);
// Initialize all fields.
@@ -42,7 +41,7 @@ public:
m_free_head_index = -1;
// Free all entries.
- for (s32 i = 0; i < static_cast<s32>(m_table_size); ++i) {
+ for (s16 i = 0; i < static_cast<s16>(m_table_size); ++i) {
m_objects[i] = nullptr;
m_entry_infos[i].next_free_index = i - 1;
m_free_head_index = i;
@@ -61,7 +60,7 @@ public:
return m_max_count;
}
- ResultCode Finalize();
+ Result Finalize();
bool Remove(Handle handle);
template <typename T = KAutoObject>
@@ -101,20 +100,11 @@ public:
return this->template GetObjectWithoutPseudoHandle<T>(handle);
}
- ResultCode Reserve(Handle* out_handle);
+ Result Reserve(Handle* out_handle);
void Unreserve(Handle handle);
- template <typename T>
- ResultCode Add(Handle* out_handle, T* obj) {
- static_assert(std::is_base_of_v<KAutoObject, T>);
- return this->Add(out_handle, obj, obj->GetTypeObj().GetClassToken());
- }
-
- template <typename T>
- void Register(Handle handle, T* obj) {
- static_assert(std::is_base_of_v<KAutoObject, T>);
- return this->Register(handle, obj, obj->GetTypeObj().GetClassToken());
- }
+ Result Add(Handle* out_handle, KAutoObject* obj);
+ void Register(Handle handle, KAutoObject* obj);
template <typename T>
bool GetMultipleObjects(T** out, const Handle* handles, size_t num_handles) const {
@@ -160,9 +150,6 @@ public:
}
private:
- ResultCode Add(Handle* out_handle, KAutoObject* obj, u16 type);
- void Register(Handle handle, KAutoObject* obj, u16 type);
-
s32 AllocateEntry() {
ASSERT(m_count < m_table_size);
@@ -179,7 +166,7 @@ private:
ASSERT(m_count > 0);
m_objects[index] = nullptr;
- m_entry_infos[index].next_free_index = m_free_head_index;
+ m_entry_infos[index].next_free_index = static_cast<s16>(m_free_head_index);
m_free_head_index = index;
@@ -278,19 +265,13 @@ private:
}
union EntryInfo {
- struct {
- u16 linear_id;
- u16 type;
- } info;
- s32 next_free_index;
+ u16 linear_id;
+ s16 next_free_index;
constexpr u16 GetLinearId() const {
- return info.linear_id;
- }
- constexpr u16 GetType() const {
- return info.type;
+ return linear_id;
}
- constexpr s32 GetNextFreeIndex() const {
+ constexpr s16 GetNextFreeIndex() const {
return next_free_index;
}
};
diff --git a/src/core/hle/kernel/k_interrupt_manager.cpp b/src/core/hle/kernel/k_interrupt_manager.cpp
index e5dd39751..1b577a5b3 100644
--- a/src/core/hle/kernel/k_interrupt_manager.cpp
+++ b/src/core/hle/kernel/k_interrupt_manager.cpp
@@ -1,12 +1,12 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
namespace Kernel::KInterruptManager {
@@ -16,8 +16,10 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
return;
}
- auto& scheduler = kernel.Scheduler(core_id);
- auto& current_thread = *scheduler.GetCurrentThread();
+ // Acknowledge the interrupt.
+ kernel.PhysicalCore(core_id).ClearInterrupt();
+
+ auto& current_thread = GetCurrentThread(kernel);
// If the user disable count is set, we may need to pin the current thread.
if (current_thread.GetUserDisableCount() && !process->GetPinnedThread(core_id)) {
@@ -27,8 +29,11 @@ void HandleInterrupt(KernelCore& kernel, s32 core_id) {
process->PinCurrentThread(core_id);
// Set the interrupt flag for the thread.
- scheduler.GetCurrentThread()->SetInterruptFlag();
+ GetCurrentThread(kernel).SetInterruptFlag();
}
+
+ // Request interrupt scheduling.
+ kernel.CurrentScheduler()->RequestScheduleOnInterrupt();
}
} // namespace Kernel::KInterruptManager
diff --git a/src/core/hle/kernel/k_interrupt_manager.h b/src/core/hle/kernel/k_interrupt_manager.h
index 05924801e..f103dfe3f 100644
--- a/src/core/hle/kernel/k_interrupt_manager.h
+++ b/src/core/hle/kernel/k_interrupt_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_light_condition_variable.cpp b/src/core/hle/kernel/k_light_condition_variable.cpp
index a8001fffc..cade99cfd 100644
--- a/src/core/hle/kernel/k_light_condition_variable.cpp
+++ b/src/core/hle/kernel/k_light_condition_variable.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_scheduler.h"
@@ -18,8 +17,7 @@ public:
bool term)
: KThreadQueue(kernel_), m_wait_list(wl), m_allow_terminating_thread(term) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Only process waits if we're allowed to.
if (ResultTerminationRequested == wait_result && m_allow_terminating_thread) {
return;
diff --git a/src/core/hle/kernel/k_light_condition_variable.h b/src/core/hle/kernel/k_light_condition_variable.h
index 5d6d7f128..3cabd6b4f 100644
--- a/src/core/hle/kernel/k_light_condition_variable.h
+++ b/src/core/hle/kernel/k_light_condition_variable.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp
index 4620342eb..43185320d 100644
--- a/src/core/hle/kernel/k_light_lock.cpp
+++ b/src/core/hle/kernel/k_light_lock.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_scheduler.h"
@@ -16,8 +15,7 @@ class ThreadQueueImplForKLightLock final : public KThreadQueue {
public:
explicit ThreadQueueImplForKLightLock(KernelCore& kernel_) : KThreadQueue(kernel_) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread as a waiter from its owner.
if (KThread* owner = waiting_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(waiting_thread);
diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h
index 4163b8a85..7edd950c0 100644
--- a/src/core/hle/kernel/k_light_lock.h
+++ b/src/core/hle/kernel/k_light_lock.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_linked_list.h b/src/core/hle/kernel/k_linked_list.h
index 6adfe1e34..78859ced3 100644
--- a/src/core/hle/kernel/k_linked_list.h
+++ b/src/core/hle/kernel/k_linked_list.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index dcca47f1b..18df1f836 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_memory_block_manager.cpp b/src/core/hle/kernel/k_memory_block_manager.cpp
index fc7033564..3ddb9984f 100644
--- a/src/core/hle/kernel/k_memory_block_manager.cpp
+++ b/src/core/hle/kernel/k_memory_block_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_memory_block_manager.h"
#include "core/hle/kernel/memory_types.h"
diff --git a/src/core/hle/kernel/k_memory_block_manager.h b/src/core/hle/kernel/k_memory_block_manager.h
index d222da919..e14741b89 100644
--- a/src/core/hle/kernel/k_memory_block_manager.h
+++ b/src/core/hle/kernel/k_memory_block_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
index af652af58..098ba6eac 100644
--- a/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
+++ b/src/core/hle/kernel/k_memory_layout.board.nintendo_nx.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/literals.h"
diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp
index fb1e2435f..55dc296d0 100644
--- a/src/core/hle/kernel/k_memory_layout.cpp
+++ b/src/core/hle/kernel/k_memory_layout.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h
index 57ff538cc..884fc623a 100644
--- a/src/core/hle/kernel/k_memory_layout.h
+++ b/src/core/hle/kernel/k_memory_layout.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -57,11 +56,11 @@ constexpr std::size_t KernelPageTableHeapSize = GetMaximumOverheadSize(MainMemor
constexpr std::size_t KernelInitialPageHeapSize = 128_KiB;
constexpr std::size_t KernelSlabHeapDataSize = 5_MiB;
-constexpr std::size_t KernelSlabHeapGapsSize = 2_MiB - 64_KiB;
-constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize;
+constexpr std::size_t KernelSlabHeapGapsSizeMax = 2_MiB - 64_KiB;
+constexpr std::size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSizeMax;
// NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860.
-constexpr std::size_t KernelSlabHeapAdditionalSize = 416_KiB;
+constexpr std::size_t KernelSlabHeapAdditionalSize = 0x68000;
constexpr std::size_t KernelResourceSize =
KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize;
@@ -173,6 +172,10 @@ public:
return Dereference(FindVirtualLinear(address));
}
+ const KMemoryRegion& GetPhysicalLinearRegion(PAddr address) const {
+ return Dereference(FindPhysicalLinear(address));
+ }
+
const KMemoryRegion* GetPhysicalKernelTraceBufferRegion() const {
return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer);
}
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index 1b44541b1..5b0a9963a 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
@@ -10,189 +9,411 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/device_memory.h"
+#include "core/hle/kernel/initial_process.h"
#include "core/hle/kernel/k_memory_manager.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
namespace Kernel {
-KMemoryManager::KMemoryManager(Core::System& system_) : system{system_} {}
+namespace {
+
+constexpr KMemoryManager::Pool GetPoolFromMemoryRegionType(u32 type) {
+ if ((type | KMemoryRegionType_DramApplicationPool) == type) {
+ return KMemoryManager::Pool::Application;
+ } else if ((type | KMemoryRegionType_DramAppletPool) == type) {
+ return KMemoryManager::Pool::Applet;
+ } else if ((type | KMemoryRegionType_DramSystemPool) == type) {
+ return KMemoryManager::Pool::System;
+ } else if ((type | KMemoryRegionType_DramSystemNonSecurePool) == type) {
+ return KMemoryManager::Pool::SystemNonSecure;
+ } else {
+ ASSERT_MSG(false, "InvalidMemoryRegionType for conversion to Pool");
+ return {};
+ }
+}
-std::size_t KMemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) {
- const auto size{end_address - start_address};
+} // namespace
+
+KMemoryManager::KMemoryManager(Core::System& system_)
+ : system{system_}, pool_locks{
+ KLightLock{system_.Kernel()},
+ KLightLock{system_.Kernel()},
+ KLightLock{system_.Kernel()},
+ KLightLock{system_.Kernel()},
+ } {}
+
+void KMemoryManager::Initialize(VAddr management_region, size_t management_region_size) {
+
+ // Clear the management region to zero.
+ const VAddr management_region_end = management_region + management_region_size;
+
+ // Reset our manager count.
+ num_managers = 0;
+
+ // Traverse the virtual memory layout tree, initializing each manager as appropriate.
+ while (num_managers != MaxManagerCount) {
+ // Locate the region that should initialize the current manager.
+ PAddr region_address = 0;
+ size_t region_size = 0;
+ Pool region_pool = Pool::Count;
+ for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
+ // We only care about regions that we need to create managers for.
+ if (!it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
+ continue;
+ }
- // Calculate metadata sizes
- const auto ref_count_size{(size / PageSize) * sizeof(u16)};
- const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)};
- const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)};
- const auto page_heap_size{KPageHeap::CalculateManagementOverheadSize(size)};
- const auto total_metadata_size{manager_size + page_heap_size};
- ASSERT(manager_size <= total_metadata_size);
- ASSERT(Common::IsAligned(total_metadata_size, PageSize));
+ // We want to initialize the managers in order.
+ if (it.GetAttributes() != num_managers) {
+ continue;
+ }
- // Setup region
- pool = new_pool;
+ const PAddr cur_start = it.GetAddress();
+ const PAddr cur_end = it.GetEndAddress();
+
+ // Validate the region.
+ ASSERT(cur_end != 0);
+ ASSERT(cur_start != 0);
+ ASSERT(it.GetSize() > 0);
+
+ // Update the region's extents.
+ if (region_address == 0) {
+ region_address = cur_start;
+ region_size = it.GetSize();
+ region_pool = GetPoolFromMemoryRegionType(it.GetType());
+ } else {
+ ASSERT(cur_start == region_address + region_size);
+
+ // Update the size.
+ region_size = cur_end - region_address;
+ ASSERT(GetPoolFromMemoryRegionType(it.GetType()) == region_pool);
+ }
+ }
+
+ // If we didn't find a region, we're done.
+ if (region_size == 0) {
+ break;
+ }
- // Initialize the manager's KPageHeap
- heap.Initialize(start_address, size, page_heap_size);
+ // Initialize a new manager for the region.
+ Impl* manager = std::addressof(managers[num_managers++]);
+ ASSERT(num_managers <= managers.size());
+
+ const size_t cur_size = manager->Initialize(region_address, region_size, management_region,
+ management_region_end, region_pool);
+ management_region += cur_size;
+ ASSERT(management_region <= management_region_end);
+
+ // Insert the manager into the pool list.
+ const auto region_pool_index = static_cast<u32>(region_pool);
+ if (pool_managers_tail[region_pool_index] == nullptr) {
+ pool_managers_head[region_pool_index] = manager;
+ } else {
+ pool_managers_tail[region_pool_index]->SetNext(manager);
+ manager->SetPrev(pool_managers_tail[region_pool_index]);
+ }
+ pool_managers_tail[region_pool_index] = manager;
+ }
- // Free the memory to the heap
- heap.Free(start_address, size / PageSize);
+ // Free each region to its corresponding heap.
+ size_t reserved_sizes[MaxManagerCount] = {};
+ const PAddr ini_start = GetInitialProcessBinaryPhysicalAddress();
+ const PAddr ini_end = ini_start + InitialProcessBinarySizeMax;
+ const PAddr ini_last = ini_end - 1;
+ for (const auto& it : system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
+ if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
+ // Get the manager for the region.
+ auto index = it.GetAttributes();
+ auto& manager = managers[index];
+
+ const PAddr cur_start = it.GetAddress();
+ const PAddr cur_last = it.GetLastAddress();
+ const PAddr cur_end = it.GetEndAddress();
+
+ if (cur_start <= ini_start && ini_last <= cur_last) {
+ // Free memory before the ini to the heap.
+ if (cur_start != ini_start) {
+ manager.Free(cur_start, (ini_start - cur_start) / PageSize);
+ }
- // Update the heap's used size
- heap.UpdateUsedSize();
+ // Open/reserve the ini memory.
+ manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
+ reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
- return total_metadata_size;
-}
+ // Free memory after the ini to the heap.
+ if (ini_last != cur_last) {
+ ASSERT(cur_end != 0);
+ manager.Free(ini_end, cur_end - ini_end);
+ }
+ } else {
+ // Ensure there's no partial overlap with the ini image.
+ if (cur_start <= ini_last) {
+ ASSERT(cur_last < ini_start);
+ } else {
+ // Otherwise, check the region for general validity.
+ ASSERT(cur_end != 0);
+ }
-void KMemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) {
- ASSERT(pool < Pool::Count);
- managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address);
+ // Free the memory to the heap.
+ manager.Free(cur_start, it.GetSize() / PageSize);
+ }
+ }
+ }
+
+ // Update the used size for all managers.
+ for (size_t i = 0; i < num_managers; ++i) {
+ managers[i].SetInitialUsedHeapSize(reserved_sizes[i]);
+ }
}
-VAddr KMemoryManager::AllocateAndOpenContinuous(std::size_t num_pages, std::size_t align_pages,
- u32 option) {
- // Early return if we're allocating no pages
+PAddr KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option) {
+ // Early return if we're allocating no pages.
if (num_pages == 0) {
- return {};
+ return 0;
}
- // Lock the pool that we're allocating from
+ // Lock the pool that we're allocating from.
const auto [pool, dir] = DecodeOption(option);
- const auto pool_index{static_cast<std::size_t>(pool)};
- std::lock_guard lock{pool_locks[pool_index]};
-
- // Choose a heap based on our page size request
- const s32 heap_index{KPageHeap::GetAlignedBlockIndex(num_pages, align_pages)};
-
- // Loop, trying to iterate from each block
- // TODO (bunnei): Support multiple managers
- Impl& chosen_manager{managers[pool_index]};
- VAddr allocated_block{chosen_manager.AllocateBlock(heap_index, false)};
+ KScopedLightLock lk(pool_locks[static_cast<std::size_t>(pool)]);
+
+ // Choose a heap based on our page size request.
+ const s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, align_pages);
+
+ // Loop, trying to iterate from each block.
+ Impl* chosen_manager = nullptr;
+ PAddr allocated_block = 0;
+ for (chosen_manager = this->GetFirstManager(pool, dir); chosen_manager != nullptr;
+ chosen_manager = this->GetNextManager(chosen_manager, dir)) {
+ allocated_block = chosen_manager->AllocateBlock(heap_index, true);
+ if (allocated_block != 0) {
+ break;
+ }
+ }
- // If we failed to allocate, quit now
- if (!allocated_block) {
- return {};
+ // If we failed to allocate, quit now.
+ if (allocated_block == 0) {
+ return 0;
}
- // If we allocated more than we need, free some
- const auto allocated_pages{KPageHeap::GetBlockNumPages(heap_index)};
+ // If we allocated more than we need, free some.
+ const size_t allocated_pages = KPageHeap::GetBlockNumPages(heap_index);
if (allocated_pages > num_pages) {
- chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
+ chosen_manager->Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages);
}
+ // Open the first reference to the pages.
+ chosen_manager->OpenFirst(allocated_block, num_pages);
+
return allocated_block;
}
-ResultCode KMemoryManager::Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
- Direction dir, u32 heap_fill_value) {
- ASSERT(page_list.GetNumPages() == 0);
+Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool,
+ Direction dir, bool random) {
+ // Choose a heap based on our page size request.
+ const s32 heap_index = KPageHeap::GetBlockIndex(num_pages);
+ R_UNLESS(0 <= heap_index, ResultOutOfMemory);
+
+ // Ensure that we don't leave anything un-freed.
+ auto group_guard = SCOPE_GUARD({
+ for (const auto& it : out->Nodes()) {
+ auto& manager = this->GetManager(system.Kernel().MemoryLayout(), it.GetAddress());
+ const size_t num_pages_to_free =
+ std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
+ manager.Free(it.GetAddress(), num_pages_to_free);
+ }
+ });
- // Early return if we're allocating no pages
- if (num_pages == 0) {
- return ResultSuccess;
- }
+ // Keep allocating until we've allocated all our pages.
+ for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) {
+ const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index);
+ for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr;
+ cur_manager = this->GetNextManager(cur_manager, dir)) {
+ while (num_pages >= pages_per_alloc) {
+ // Allocate a block.
+ PAddr allocated_block = cur_manager->AllocateBlock(index, random);
+ if (allocated_block == 0) {
+ break;
+ }
- // Lock the pool that we're allocating from
- const auto pool_index{static_cast<std::size_t>(pool)};
- std::lock_guard lock{pool_locks[pool_index]};
+ // Safely add it to our group.
+ {
+ auto block_guard =
+ SCOPE_GUARD({ cur_manager->Free(allocated_block, pages_per_alloc); });
+ R_TRY(out->AddBlock(allocated_block, pages_per_alloc));
+ block_guard.Cancel();
+ }
- // Choose a heap based on our page size request
- const s32 heap_index{KPageHeap::GetBlockIndex(num_pages)};
- if (heap_index < 0) {
- return ResultOutOfMemory;
+ num_pages -= pages_per_alloc;
+ }
+ }
}
- // TODO (bunnei): Support multiple managers
- Impl& chosen_manager{managers[pool_index]};
+ // Only succeed if we allocated as many pages as we wanted.
+ R_UNLESS(num_pages == 0, ResultOutOfMemory);
- // Ensure that we don't leave anything un-freed
- auto group_guard = detail::ScopeExit([&] {
- for (const auto& it : page_list.Nodes()) {
- const auto min_num_pages{std::min<size_t>(
- it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
- chosen_manager.Free(it.GetAddress(), min_num_pages);
- }
- });
+ // We succeeded!
+ group_guard.Cancel();
+ return ResultSuccess;
+}
- // Keep allocating until we've allocated all our pages
- for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) {
- const auto pages_per_alloc{KPageHeap::GetBlockNumPages(index)};
+Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) {
+ ASSERT(out != nullptr);
+ ASSERT(out->GetNumPages() == 0);
- while (num_pages >= pages_per_alloc) {
- // Allocate a block
- VAddr allocated_block{chosen_manager.AllocateBlock(index, false)};
- if (!allocated_block) {
- break;
- }
+ // Early return if we're allocating no pages.
+ R_SUCCEED_IF(num_pages == 0);
- // Safely add it to our group
- {
- auto block_guard = detail::ScopeExit(
- [&] { chosen_manager.Free(allocated_block, pages_per_alloc); });
+ // Lock the pool that we're allocating from.
+ const auto [pool, dir] = DecodeOption(option);
+ KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
+
+ // Allocate the page group.
+ R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
+
+ // Open the first reference to the pages.
+ for (const auto& block : out->Nodes()) {
+ PAddr cur_address = block.GetAddress();
+ size_t remaining_pages = block.GetNumPages();
+ while (remaining_pages > 0) {
+ // Get the manager for the current address.
+ auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
+
+ // Process part or all of the block.
+ const size_t cur_pages =
+ std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
+ manager.OpenFirst(cur_address, cur_pages);
+
+ // Advance.
+ cur_address += cur_pages * PageSize;
+ remaining_pages -= cur_pages;
+ }
+ }
- if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)};
- result.IsError()) {
- return result;
- }
+ return ResultSuccess;
+}
- block_guard.Cancel();
- }
+Result KMemoryManager::AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option,
+ u64 process_id, u8 fill_pattern) {
+ ASSERT(out != nullptr);
+ ASSERT(out->GetNumPages() == 0);
- num_pages -= pages_per_alloc;
- }
- }
+ // Decode the option.
+ const auto [pool, dir] = DecodeOption(option);
- // Clear allocated memory.
- for (const auto& it : page_list.Nodes()) {
- std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
- it.GetSize());
+ // Allocate the memory.
+ {
+ // Lock the pool that we're allocating from.
+ KScopedLightLock lk(pool_locks[static_cast<size_t>(pool)]);
+
+ // Allocate the page group.
+ R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, false));
+
+ // Open the first reference to the pages.
+ for (const auto& block : out->Nodes()) {
+ PAddr cur_address = block.GetAddress();
+ size_t remaining_pages = block.GetNumPages();
+ while (remaining_pages > 0) {
+ // Get the manager for the current address.
+ auto& manager = this->GetManager(system.Kernel().MemoryLayout(), cur_address);
+
+ // Process part or all of the block.
+ const size_t cur_pages =
+ std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address));
+ manager.OpenFirst(cur_address, cur_pages);
+
+ // Advance.
+ cur_address += cur_pages * PageSize;
+ remaining_pages -= cur_pages;
+ }
+ }
}
- // Only succeed if we allocated as many pages as we wanted
- if (num_pages) {
- return ResultOutOfMemory;
+ // Set all the allocated memory.
+ for (const auto& block : out->Nodes()) {
+ std::memset(system.DeviceMemory().GetPointer(block.GetAddress()), fill_pattern,
+ block.GetSize());
}
- // We succeeded!
- group_guard.Cancel();
-
return ResultSuccess;
}
-ResultCode KMemoryManager::Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool,
- Direction dir, u32 heap_fill_value) {
- // Early return if we're freeing no pages
- if (!num_pages) {
- return ResultSuccess;
+void KMemoryManager::Open(PAddr address, size_t num_pages) {
+ // Repeatedly open references until we've done so for all pages.
+ while (num_pages) {
+ auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
+ const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
+
+ {
+ KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
+ manager.Open(address, cur_pages);
+ }
+
+ num_pages -= cur_pages;
+ address += cur_pages * PageSize;
}
+}
- // Lock the pool that we're freeing from
- const auto pool_index{static_cast<std::size_t>(pool)};
- std::lock_guard lock{pool_locks[pool_index]};
+void KMemoryManager::Close(PAddr address, size_t num_pages) {
+ // Repeatedly close references until we've done so for all pages.
+ while (num_pages) {
+ auto& manager = this->GetManager(system.Kernel().MemoryLayout(), address);
+ const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address));
- // TODO (bunnei): Support multiple managers
- Impl& chosen_manager{managers[pool_index]};
+ {
+ KScopedLightLock lk(pool_locks[static_cast<size_t>(manager.GetPool())]);
+ manager.Close(address, cur_pages);
+ }
- // Free all of the pages
- for (const auto& it : page_list.Nodes()) {
- const auto min_num_pages{std::min<size_t>(
- it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)};
- chosen_manager.Free(it.GetAddress(), min_num_pages);
+ num_pages -= cur_pages;
+ address += cur_pages * PageSize;
}
+}
- return ResultSuccess;
+void KMemoryManager::Close(const KPageGroup& pg) {
+ for (const auto& node : pg.Nodes()) {
+ Close(node.GetAddress(), node.GetNumPages());
+ }
+}
+void KMemoryManager::Open(const KPageGroup& pg) {
+ for (const auto& node : pg.Nodes()) {
+ Open(node.GetAddress(), node.GetNumPages());
+ }
+}
+
+size_t KMemoryManager::Impl::Initialize(PAddr address, size_t size, VAddr management,
+ VAddr management_end, Pool p) {
+ // Calculate management sizes.
+ const size_t ref_count_size = (size / PageSize) * sizeof(u16);
+ const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(size);
+ const size_t manager_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
+ const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(size);
+ const size_t total_management_size = manager_size + page_heap_size;
+ ASSERT(manager_size <= total_management_size);
+ ASSERT(management + total_management_size <= management_end);
+ ASSERT(Common::IsAligned(total_management_size, PageSize));
+
+ // Setup region.
+ pool = p;
+ management_region = management;
+ page_reference_counts.resize(
+ Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize() / PageSize);
+ ASSERT(Common::IsAligned(management_region, PageSize));
+
+ // Initialize the manager's KPageHeap.
+ heap.Initialize(address, size, management + manager_size, page_heap_size);
+
+ return total_management_size;
}
-std::size_t KMemoryManager::Impl::CalculateManagementOverheadSize(std::size_t region_size) {
- const std::size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
- const std::size_t optimize_map_size =
+size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) {
+ const size_t ref_count_size = (region_size / PageSize) * sizeof(u16);
+ const size_t optimize_map_size =
(Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
- const std::size_t manager_meta_size =
- Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
- const std::size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
+ const size_t manager_meta_size = Common::AlignUp(optimize_map_size + ref_count_size, PageSize);
+ const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size);
return manager_meta_size + page_heap_size;
}
diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h
index 17c7690f1..dcb9b6348 100644
--- a/src/core/hle/kernel/k_memory_manager.h
+++ b/src/core/hle/kernel/k_memory_manager.h
@@ -1,15 +1,15 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
-#include <mutex>
#include <tuple>
#include "common/common_funcs.h"
#include "common/common_types.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_page_heap.h"
#include "core/hle/result.h"
@@ -19,7 +19,7 @@ class System;
namespace Kernel {
-class KPageLinkedList;
+class KPageGroup;
class KMemoryManager final {
public:
@@ -52,22 +52,33 @@ public:
explicit KMemoryManager(Core::System& system_);
- constexpr std::size_t GetSize(Pool pool) const {
- return managers[static_cast<std::size_t>(pool)].GetSize();
+ void Initialize(VAddr management_region, size_t management_region_size);
+
+ constexpr size_t GetSize(Pool pool) const {
+ constexpr Direction GetSizeDirection = Direction::FromFront;
+ size_t total = 0;
+ for (auto* manager = this->GetFirstManager(pool, GetSizeDirection); manager != nullptr;
+ manager = this->GetNextManager(manager, GetSizeDirection)) {
+ total += manager->GetSize();
+ }
+ return total;
}
- void InitializeManager(Pool pool, u64 start_address, u64 end_address);
+ PAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
+ Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option);
+ Result AllocateAndOpenForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id,
+ u8 fill_pattern);
+
+ static constexpr size_t MaxManagerCount = 10;
- VAddr AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option);
- ResultCode Allocate(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
- u32 heap_fill_value = 0);
- ResultCode Free(KPageLinkedList& page_list, std::size_t num_pages, Pool pool, Direction dir,
- u32 heap_fill_value = 0);
+ void Close(PAddr address, size_t num_pages);
+ void Close(const KPageGroup& pg);
- static constexpr std::size_t MaxManagerCount = 10;
+ void Open(PAddr address, size_t num_pages);
+ void Open(const KPageGroup& pg);
public:
- static std::size_t CalculateManagementOverheadSize(std::size_t region_size) {
+ static size_t CalculateManagementOverheadSize(size_t region_size) {
return Impl::CalculateManagementOverheadSize(region_size);
}
@@ -100,17 +111,26 @@ private:
Impl() = default;
~Impl() = default;
- std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address);
+ size_t Initialize(PAddr address, size_t size, VAddr management, VAddr management_end,
+ Pool p);
VAddr AllocateBlock(s32 index, bool random) {
return heap.AllocateBlock(index, random);
}
- void Free(VAddr addr, std::size_t num_pages) {
+ void Free(VAddr addr, size_t num_pages) {
heap.Free(addr, num_pages);
}
- constexpr std::size_t GetSize() const {
+ void SetInitialUsedHeapSize(size_t reserved_size) {
+ heap.SetInitialUsedSize(reserved_size);
+ }
+
+ constexpr Pool GetPool() const {
+ return pool;
+ }
+
+ constexpr size_t GetSize() const {
return heap.GetSize();
}
@@ -122,10 +142,88 @@ private:
return heap.GetEndAddress();
}
- static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
+ constexpr size_t GetPageOffset(PAddr address) const {
+ return heap.GetPageOffset(address);
+ }
+
+ constexpr size_t GetPageOffsetToEnd(PAddr address) const {
+ return heap.GetPageOffsetToEnd(address);
+ }
+
+ constexpr void SetNext(Impl* n) {
+ next = n;
+ }
+
+ constexpr void SetPrev(Impl* n) {
+ prev = n;
+ }
+
+ constexpr Impl* GetNext() const {
+ return next;
+ }
+
+ constexpr Impl* GetPrev() const {
+ return prev;
+ }
+
+ void OpenFirst(PAddr address, size_t num_pages) {
+ size_t index = this->GetPageOffset(address);
+ const size_t end = index + num_pages;
+ while (index < end) {
+ const RefCount ref_count = (++page_reference_counts[index]);
+ ASSERT(ref_count == 1);
- static constexpr std::size_t CalculateOptimizedProcessOverheadSize(
- std::size_t region_size) {
+ index++;
+ }
+ }
+
+ void Open(PAddr address, size_t num_pages) {
+ size_t index = this->GetPageOffset(address);
+ const size_t end = index + num_pages;
+ while (index < end) {
+ const RefCount ref_count = (++page_reference_counts[index]);
+ ASSERT(ref_count > 1);
+
+ index++;
+ }
+ }
+
+ void Close(PAddr address, size_t num_pages) {
+ size_t index = this->GetPageOffset(address);
+ const size_t end = index + num_pages;
+
+ size_t free_start = 0;
+ size_t free_count = 0;
+ while (index < end) {
+ ASSERT(page_reference_counts[index] > 0);
+ const RefCount ref_count = (--page_reference_counts[index]);
+
+ // Keep track of how many zero refcounts we see in a row, to minimize calls to free.
+ if (ref_count == 0) {
+ if (free_count > 0) {
+ free_count++;
+ } else {
+ free_start = index;
+ free_count = 1;
+ }
+ } else {
+ if (free_count > 0) {
+ this->Free(heap.GetAddress() + free_start * PageSize, free_count);
+ free_count = 0;
+ }
+ }
+
+ index++;
+ }
+
+ if (free_count > 0) {
+ this->Free(heap.GetAddress() + free_start * PageSize, free_count);
+ }
+ }
+
+ static size_t CalculateManagementOverheadSize(size_t region_size);
+
+ static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) {
return (Common::AlignUp((region_size / PageSize), Common::BitSize<u64>()) /
Common::BitSize<u64>()) *
sizeof(u64);
@@ -135,13 +233,45 @@ private:
using RefCount = u16;
KPageHeap heap;
+ std::vector<RefCount> page_reference_counts;
+ VAddr management_region{};
Pool pool{};
+ Impl* next{};
+ Impl* prev{};
};
private:
+ Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) {
+ return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
+ }
+
+ const Impl& GetManager(const KMemoryLayout& memory_layout, PAddr address) const {
+ return managers[memory_layout.GetPhysicalLinearRegion(address).GetAttributes()];
+ }
+
+ constexpr Impl* GetFirstManager(Pool pool, Direction dir) const {
+ return dir == Direction::FromBack ? pool_managers_tail[static_cast<size_t>(pool)]
+ : pool_managers_head[static_cast<size_t>(pool)];
+ }
+
+ constexpr Impl* GetNextManager(Impl* cur, Direction dir) const {
+ if (dir == Direction::FromBack) {
+ return cur->GetPrev();
+ } else {
+ return cur->GetNext();
+ }
+ }
+
+ Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir,
+ bool random);
+
+private:
Core::System& system;
- std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks;
+ std::array<KLightLock, static_cast<size_t>(Pool::Count)> pool_locks;
+ std::array<Impl*, MaxManagerCount> pool_managers_head{};
+ std::array<Impl*, MaxManagerCount> pool_managers_tail{};
std::array<Impl, MaxManagerCount> managers;
+ size_t num_managers{};
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_region.h b/src/core/hle/kernel/k_memory_region.h
index e9bdf4e59..5037e657f 100644
--- a/src/core/hle/kernel/k_memory_region.h
+++ b/src/core/hle/kernel/k_memory_region.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_memory_region_type.h b/src/core/hle/kernel/k_memory_region_type.h
index a05e66677..7e2fcccdc 100644
--- a/src/core/hle/kernel/k_memory_region_type.h
+++ b/src/core/hle/kernel/k_memory_region_type.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -14,7 +13,8 @@
namespace Kernel {
enum KMemoryRegionType : u32 {
- KMemoryRegionAttr_CarveoutProtected = 0x04000000,
+ KMemoryRegionAttr_CarveoutProtected = 0x02000000,
+ KMemoryRegionAttr_Uncached = 0x04000000,
KMemoryRegionAttr_DidKernelMap = 0x08000000,
KMemoryRegionAttr_ShouldKernelMap = 0x10000000,
KMemoryRegionAttr_UserReadOnly = 0x20000000,
@@ -239,6 +239,11 @@ static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
+// UNUSED: .DeriveSparse(2, 2, 0);
+constexpr auto KMemoryRegionType_VirtualDramUnknownDebug =
+ KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
+static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
+
constexpr auto KMemoryRegionType_VirtualDramKernelInitPt =
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
constexpr auto KMemoryRegionType_VirtualDramPoolManagement =
@@ -330,6 +335,8 @@ constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
return KMemoryRegionType_VirtualDramKernelPtHeap;
+ } else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
+ return KMemoryRegionType_VirtualDramUnknownDebug;
} else {
return KMemoryRegionType_Dram;
}
diff --git a/src/core/hle/kernel/k_page_bitmap.h b/src/core/hle/kernel/k_page_bitmap.h
index c75d667c9..c97b3dc0b 100644
--- a/src/core/hle/kernel/k_page_bitmap.h
+++ b/src/core/hle/kernel/k_page_bitmap.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
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..1a0bf4439
--- /dev/null
+++ b/src/core/hle/kernel/k_page_buffer.cpp
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "core/core.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/k_page_buffer.h"
+#include "core/hle/kernel/memory_types.h"
+
+namespace Kernel {
+
+KPageBuffer* KPageBuffer::FromPhysicalAddress(Core::System& system, PAddr phys_addr) {
+ ASSERT(Common::IsAligned(phys_addr, PageSize));
+ return reinterpret_cast<KPageBuffer*>(system.DeviceMemory().GetPointer(phys_addr));
+}
+
+} // 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..7e50dc1d1
--- /dev/null
+++ b/src/core/hle/kernel/k_page_buffer.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+#include "core/hle/kernel/memory_types.h"
+#include "core/hle/kernel/slab_helpers.h"
+
+namespace Kernel {
+
+class KPageBuffer final : public KSlabAllocated<KPageBuffer> {
+public:
+ KPageBuffer() = default;
+
+ static KPageBuffer* FromPhysicalAddress(Core::System& system, PAddr phys_addr);
+
+private:
+ [[maybe_unused]] alignas(PageSize) std::array<u8, PageSize> m_buffer{};
+};
+
+static_assert(sizeof(KPageBuffer) == PageSize);
+static_assert(alignof(KPageBuffer) == PageSize);
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_group.h
index 0e2ae582a..968753992 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_group.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -13,7 +12,7 @@
namespace Kernel {
-class KPageLinkedList final {
+class KPageGroup final {
public:
class Node final {
public:
@@ -37,8 +36,8 @@ public:
};
public:
- KPageLinkedList() = default;
- KPageLinkedList(u64 address, u64 num_pages) {
+ KPageGroup() = default;
+ KPageGroup(u64 address, u64 num_pages) {
ASSERT(AddBlock(address, num_pages).IsSuccess());
}
@@ -58,7 +57,7 @@ public:
return num_pages;
}
- bool IsEqual(KPageLinkedList& other) const {
+ bool IsEqual(KPageGroup& other) const {
auto this_node = nodes.begin();
auto other_node = other.nodes.begin();
while (this_node != nodes.end() && other_node != other.nodes.end()) {
@@ -73,7 +72,7 @@ public:
return this_node == nodes.end() && other_node == other.nodes.end();
}
- ResultCode AddBlock(u64 address, u64 num_pages) {
+ Result AddBlock(u64 address, u64 num_pages) {
if (!num_pages) {
return ResultSuccess;
}
@@ -89,6 +88,10 @@ public:
return ResultSuccess;
}
+ bool Empty() const {
+ return nodes.empty();
+ }
+
private:
std::list<Node> nodes;
};
diff --git a/src/core/hle/kernel/k_page_heap.cpp b/src/core/hle/kernel/k_page_heap.cpp
index 29d996d62..5ede60168 100644
--- a/src/core/hle/kernel/k_page_heap.cpp
+++ b/src/core/hle/kernel/k_page_heap.cpp
@@ -1,41 +1,56 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/kernel/k_page_heap.h"
namespace Kernel {
-void KPageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) {
- // Check our assumptions
- ASSERT(Common::IsAligned((address), PageSize));
+void KPageHeap::Initialize(PAddr address, size_t size, VAddr management_address,
+ size_t management_size, const size_t* block_shifts,
+ size_t num_block_shifts) {
+ // Check our assumptions.
+ ASSERT(Common::IsAligned(address, PageSize));
ASSERT(Common::IsAligned(size, PageSize));
+ ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts);
+ const VAddr management_end = management_address + management_size;
- // Set our members
- heap_address = address;
- heap_size = size;
-
- // Setup bitmaps
- metadata.resize(metadata_size / sizeof(u64));
- u64* cur_bitmap_storage{metadata.data()};
- for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
- const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
- const std::size_t next_block_shift{
- (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
- cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift,
- next_block_shift, cur_bitmap_storage);
+ // Set our members.
+ m_heap_address = address;
+ m_heap_size = size;
+ m_num_blocks = num_block_shifts;
+
+ // Setup bitmaps.
+ m_management_data.resize(management_size / sizeof(u64));
+ u64* cur_bitmap_storage{m_management_data.data()};
+ for (size_t i = 0; i < num_block_shifts; i++) {
+ const size_t cur_block_shift = block_shifts[i];
+ const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
+ cur_bitmap_storage = m_blocks[i].Initialize(m_heap_address, m_heap_size, cur_block_shift,
+ next_block_shift, cur_bitmap_storage);
}
+
+ // Ensure we didn't overextend our bounds.
+ ASSERT(VAddr(cur_bitmap_storage) <= management_end);
+}
+
+size_t KPageHeap::GetNumFreePages() const {
+ size_t num_free = 0;
+
+ for (size_t i = 0; i < m_num_blocks; i++) {
+ num_free += m_blocks[i].GetNumFreePages();
+ }
+
+ return num_free;
}
-VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
- const std::size_t needed_size{blocks[index].GetSize()};
+PAddr KPageHeap::AllocateBlock(s32 index, bool random) {
+ const size_t needed_size = m_blocks[index].GetSize();
- for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) {
- if (const VAddr addr{blocks[i].PopBlock(random)}; addr) {
- if (const std::size_t allocated_size{blocks[i].GetSize()};
- allocated_size > needed_size) {
- Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
+ for (s32 i = index; i < static_cast<s32>(m_num_blocks); i++) {
+ if (const PAddr addr = m_blocks[i].PopBlock(random); addr != 0) {
+ if (const size_t allocated_size = m_blocks[i].GetSize(); allocated_size > needed_size) {
+ this->Free(addr + needed_size, (allocated_size - needed_size) / PageSize);
}
return addr;
}
@@ -44,34 +59,34 @@ VAddr KPageHeap::AllocateBlock(s32 index, bool random) {
return 0;
}
-void KPageHeap::FreeBlock(VAddr block, s32 index) {
+void KPageHeap::FreeBlock(PAddr block, s32 index) {
do {
- block = blocks[index++].PushBlock(block);
+ block = m_blocks[index++].PushBlock(block);
} while (block != 0);
}
-void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
- // Freeing no pages is a no-op
+void KPageHeap::Free(PAddr addr, size_t num_pages) {
+ // Freeing no pages is a no-op.
if (num_pages == 0) {
return;
}
- // Find the largest block size that we can free, and free as many as possible
- s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1};
- const VAddr start{addr};
- const VAddr end{(num_pages * PageSize) + addr};
- VAddr before_start{start};
- VAddr before_end{start};
- VAddr after_start{end};
- VAddr after_end{end};
+ // Find the largest block size that we can free, and free as many as possible.
+ s32 big_index = static_cast<s32>(m_num_blocks) - 1;
+ const PAddr start = addr;
+ const PAddr end = addr + num_pages * PageSize;
+ PAddr before_start = start;
+ PAddr before_end = start;
+ PAddr after_start = end;
+ PAddr after_end = end;
while (big_index >= 0) {
- const std::size_t block_size{blocks[big_index].GetSize()};
- const VAddr big_start{Common::AlignUp((start), block_size)};
- const VAddr big_end{Common::AlignDown((end), block_size)};
+ const size_t block_size = m_blocks[big_index].GetSize();
+ const PAddr big_start = Common::AlignUp(start, block_size);
+ const PAddr big_end = Common::AlignDown(end, block_size);
if (big_start < big_end) {
- // Free as many big blocks as we can
- for (auto block{big_start}; block < big_end; block += block_size) {
- FreeBlock(block, big_index);
+ // Free as many big blocks as we can.
+ for (auto block = big_start; block < big_end; block += block_size) {
+ this->FreeBlock(block, big_index);
}
before_end = big_start;
after_start = big_end;
@@ -81,31 +96,31 @@ void KPageHeap::Free(VAddr addr, std::size_t num_pages) {
}
ASSERT(big_index >= 0);
- // Free space before the big blocks
- for (s32 i{big_index - 1}; i >= 0; i--) {
- const std::size_t block_size{blocks[i].GetSize()};
+ // Free space before the big blocks.
+ for (s32 i = big_index - 1; i >= 0; i--) {
+ const size_t block_size = m_blocks[i].GetSize();
while (before_start + block_size <= before_end) {
before_end -= block_size;
- FreeBlock(before_end, i);
+ this->FreeBlock(before_end, i);
}
}
- // Free space after the big blocks
- for (s32 i{big_index - 1}; i >= 0; i--) {
- const std::size_t block_size{blocks[i].GetSize()};
+ // Free space after the big blocks.
+ for (s32 i = big_index - 1; i >= 0; i--) {
+ const size_t block_size = m_blocks[i].GetSize();
while (after_start + block_size <= after_end) {
- FreeBlock(after_start, i);
+ this->FreeBlock(after_start, i);
after_start += block_size;
}
}
}
-std::size_t KPageHeap::CalculateManagementOverheadSize(std::size_t region_size) {
- std::size_t overhead_size = 0;
- for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) {
- const std::size_t cur_block_shift{MemoryBlockPageShifts[i]};
- const std::size_t next_block_shift{
- (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0};
+size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
+ size_t num_block_shifts) {
+ size_t overhead_size = 0;
+ for (size_t i = 0; i < num_block_shifts; i++) {
+ const size_t cur_block_shift = block_shifts[i];
+ const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0;
overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(
region_size, cur_block_shift, next_block_shift);
}
diff --git a/src/core/hle/kernel/k_page_heap.h b/src/core/hle/kernel/k_page_heap.h
index a65aa28a0..0917a8bed 100644
--- a/src/core/hle/kernel/k_page_heap.h
+++ b/src/core/hle/kernel/k_page_heap.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -23,54 +22,73 @@ public:
KPageHeap() = default;
~KPageHeap() = default;
- constexpr VAddr GetAddress() const {
- return heap_address;
+ constexpr PAddr GetAddress() const {
+ return m_heap_address;
}
- constexpr std::size_t GetSize() const {
- return heap_size;
+ constexpr size_t GetSize() const {
+ return m_heap_size;
}
- constexpr VAddr GetEndAddress() const {
- return GetAddress() + GetSize();
+ constexpr PAddr GetEndAddress() const {
+ return this->GetAddress() + this->GetSize();
}
- constexpr std::size_t GetPageOffset(VAddr block) const {
- return (block - GetAddress()) / PageSize;
+ constexpr size_t GetPageOffset(PAddr block) const {
+ return (block - this->GetAddress()) / PageSize;
+ }
+ constexpr size_t GetPageOffsetToEnd(PAddr block) const {
+ return (this->GetEndAddress() - block) / PageSize;
+ }
+
+ void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
+ size_t management_size) {
+ return this->Initialize(heap_address, heap_size, management_address, management_size,
+ MemoryBlockPageShifts.data(), NumMemoryBlockPageShifts);
+ }
+
+ size_t GetFreeSize() const {
+ return this->GetNumFreePages() * PageSize;
}
- void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size);
- VAddr AllocateBlock(s32 index, bool random);
- void Free(VAddr addr, std::size_t num_pages);
+ void SetInitialUsedSize(size_t reserved_size) {
+ // Check that the reserved size is valid.
+ const size_t free_size = this->GetNumFreePages() * PageSize;
+ ASSERT(m_heap_size >= free_size + reserved_size);
- void UpdateUsedSize() {
- used_size = heap_size - (GetNumFreePages() * PageSize);
+ // Set the initial used size.
+ m_initial_used_size = m_heap_size - free_size - reserved_size;
}
- static std::size_t CalculateManagementOverheadSize(std::size_t region_size);
+ PAddr AllocateBlock(s32 index, bool random);
+ void Free(PAddr addr, size_t num_pages);
+
+ static size_t CalculateManagementOverheadSize(size_t region_size) {
+ return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts.data(),
+ NumMemoryBlockPageShifts);
+ }
- static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) {
- const auto target_pages{std::max(num_pages, align_pages)};
- for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
- if (target_pages <=
- (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
+ static constexpr s32 GetAlignedBlockIndex(size_t num_pages, size_t align_pages) {
+ const size_t target_pages = std::max(num_pages, align_pages);
+ for (size_t i = 0; i < NumMemoryBlockPageShifts; i++) {
+ if (target_pages <= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
return static_cast<s32>(i);
}
}
return -1;
}
- static constexpr s32 GetBlockIndex(std::size_t num_pages) {
- for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) {
- if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) {
+ static constexpr s32 GetBlockIndex(size_t num_pages) {
+ for (s32 i = static_cast<s32>(NumMemoryBlockPageShifts) - 1; i >= 0; i--) {
+ if (num_pages >= (size_t(1) << MemoryBlockPageShifts[i]) / PageSize) {
return i;
}
}
return -1;
}
- static constexpr std::size_t GetBlockSize(std::size_t index) {
- return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index];
+ static constexpr size_t GetBlockSize(size_t index) {
+ return size_t(1) << MemoryBlockPageShifts[index];
}
- static constexpr std::size_t GetBlockNumPages(std::size_t index) {
+ static constexpr size_t GetBlockNumPages(size_t index) {
return GetBlockSize(index) / PageSize;
}
@@ -83,114 +101,116 @@ private:
Block() = default;
~Block() = default;
- constexpr std::size_t GetShift() const {
- return block_shift;
+ constexpr size_t GetShift() const {
+ return m_block_shift;
}
- constexpr std::size_t GetNextShift() const {
- return next_block_shift;
+ constexpr size_t GetNextShift() const {
+ return m_next_block_shift;
}
- constexpr std::size_t GetSize() const {
- return static_cast<std::size_t>(1) << GetShift();
+ constexpr size_t GetSize() const {
+ return u64(1) << this->GetShift();
}
- constexpr std::size_t GetNumPages() const {
- return GetSize() / PageSize;
+ constexpr size_t GetNumPages() const {
+ return this->GetSize() / PageSize;
}
- constexpr std::size_t GetNumFreeBlocks() const {
- return bitmap.GetNumBits();
+ constexpr size_t GetNumFreeBlocks() const {
+ return m_bitmap.GetNumBits();
}
- constexpr std::size_t GetNumFreePages() const {
- return GetNumFreeBlocks() * GetNumPages();
+ constexpr size_t GetNumFreePages() const {
+ return this->GetNumFreeBlocks() * this->GetNumPages();
}
- u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs,
- u64* bit_storage) {
- // Set shifts
- block_shift = bs;
- next_block_shift = nbs;
-
- // Align up the address
- VAddr end{addr + size};
- const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift)
- : (1ULL << block_shift)};
- addr = Common::AlignDown((addr), align);
- end = Common::AlignUp((end), align);
-
- heap_address = addr;
- end_offset = (end - addr) / (1ULL << block_shift);
- return bitmap.Initialize(bit_storage, end_offset);
+ u64* Initialize(PAddr addr, size_t size, size_t bs, size_t nbs, u64* bit_storage) {
+ // Set shifts.
+ m_block_shift = bs;
+ m_next_block_shift = nbs;
+
+ // Align up the address.
+ PAddr end = addr + size;
+ const size_t align = (m_next_block_shift != 0) ? (u64(1) << m_next_block_shift)
+ : (u64(1) << m_block_shift);
+ addr = Common::AlignDown(addr, align);
+ end = Common::AlignUp(end, align);
+
+ m_heap_address = addr;
+ m_end_offset = (end - addr) / (u64(1) << m_block_shift);
+ return m_bitmap.Initialize(bit_storage, m_end_offset);
}
- VAddr PushBlock(VAddr address) {
- // Set the bit for the free block
- std::size_t offset{(address - heap_address) >> GetShift()};
- bitmap.SetBit(offset);
+ PAddr PushBlock(PAddr address) {
+ // Set the bit for the free block.
+ size_t offset = (address - m_heap_address) >> this->GetShift();
+ m_bitmap.SetBit(offset);
- // If we have a next shift, try to clear the blocks below and return the address
- if (GetNextShift()) {
- const auto diff{1ULL << (GetNextShift() - GetShift())};
+ // If we have a next shift, try to clear the blocks below this one and return the new
+ // address.
+ if (this->GetNextShift()) {
+ const size_t diff = u64(1) << (this->GetNextShift() - this->GetShift());
offset = Common::AlignDown(offset, diff);
- if (bitmap.ClearRange(offset, diff)) {
- return heap_address + (offset << GetShift());
+ if (m_bitmap.ClearRange(offset, diff)) {
+ return m_heap_address + (offset << this->GetShift());
}
}
- // We couldn't coalesce, or we're already as big as possible
- return 0;
+ // We couldn't coalesce, or we're already as big as possible.
+ return {};
}
- VAddr PopBlock(bool random) {
- // Find a free block
- const s64 soffset{bitmap.FindFreeBlock(random)};
+ PAddr PopBlock(bool random) {
+ // Find a free block.
+ s64 soffset = m_bitmap.FindFreeBlock(random);
if (soffset < 0) {
- return 0;
+ return {};
}
- const auto offset{static_cast<std::size_t>(soffset)};
+ const size_t offset = static_cast<size_t>(soffset);
- // Update our tracking and return it
- bitmap.ClearBit(offset);
- return heap_address + (offset << GetShift());
+ // Update our tracking and return it.
+ m_bitmap.ClearBit(offset);
+ return m_heap_address + (offset << this->GetShift());
}
- static constexpr std::size_t CalculateManagementOverheadSize(std::size_t region_size,
- std::size_t cur_block_shift,
- std::size_t next_block_shift) {
- const auto cur_block_size{(1ULL << cur_block_shift)};
- const auto next_block_size{(1ULL << next_block_shift)};
- const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size};
+ public:
+ static constexpr size_t CalculateManagementOverheadSize(size_t region_size,
+ size_t cur_block_shift,
+ size_t next_block_shift) {
+ const size_t cur_block_size = (u64(1) << cur_block_shift);
+ const size_t next_block_size = (u64(1) << next_block_shift);
+ const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size;
return KPageBitmap::CalculateManagementOverheadSize(
(align * 2 + Common::AlignUp(region_size, align)) / cur_block_size);
}
private:
- KPageBitmap bitmap;
- VAddr heap_address{};
- uintptr_t end_offset{};
- std::size_t block_shift{};
- std::size_t next_block_shift{};
+ KPageBitmap m_bitmap;
+ PAddr m_heap_address{};
+ uintptr_t m_end_offset{};
+ size_t m_block_shift{};
+ size_t m_next_block_shift{};
};
- constexpr std::size_t GetNumFreePages() const {
- std::size_t num_free{};
-
- for (const auto& block : blocks) {
- num_free += block.GetNumFreePages();
- }
-
- return num_free;
- }
+private:
+ void Initialize(PAddr heap_address, size_t heap_size, VAddr management_address,
+ size_t management_size, const size_t* block_shifts, size_t num_block_shifts);
+ size_t GetNumFreePages() const;
- void FreeBlock(VAddr block, s32 index);
+ void FreeBlock(PAddr block, s32 index);
- static constexpr std::size_t NumMemoryBlockPageShifts{7};
- static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
+ static constexpr size_t NumMemoryBlockPageShifts{7};
+ static constexpr std::array<size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{
0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E,
};
- VAddr heap_address{};
- std::size_t heap_size{};
- std::size_t used_size{};
- std::array<Block, NumMemoryBlockPageShifts> blocks{};
- std::vector<u64> metadata;
+private:
+ static size_t CalculateManagementOverheadSize(size_t region_size, const size_t* block_shifts,
+ size_t num_block_shifts);
+
+private:
+ PAddr m_heap_address{};
+ size_t m_heap_size{};
+ size_t m_initial_used_size{};
+ size_t m_num_blocks{};
+ std::array<Block, NumMemoryBlockPageShifts> m_blocks{};
+ std::vector<u64> m_management_data;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 912853e5c..d975de844 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/alignment.h"
#include "common/assert.h"
@@ -10,7 +9,7 @@
#include "core/hle/kernel/k_address_space_info.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_memory_block_manager.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -36,29 +35,11 @@ constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceT
case FileSys::ProgramAddressSpaceType::Is39Bit:
return 39;
default:
- UNREACHABLE();
+ ASSERT(false);
return {};
}
}
-constexpr u64 GetAddressInRange(const KMemoryInfo& info, VAddr addr) {
- if (info.GetAddress() < addr) {
- return addr;
- }
- return info.GetAddress();
-}
-
-constexpr std::size_t GetSizeInRange(const KMemoryInfo& info, VAddr start, VAddr end) {
- std::size_t size{info.GetSize()};
- if (info.GetAddress() < start) {
- size -= start - info.GetAddress();
- }
- if (info.GetEndAddress() > end) {
- size -= info.GetEndAddress() - end;
- }
- return size;
-}
-
} // namespace
KPageTable::KPageTable(Core::System& system_)
@@ -66,9 +47,9 @@ KPageTable::KPageTable(Core::System& system_)
KPageTable::~KPageTable() = default;
-ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type,
- bool enable_aslr, VAddr code_addr,
- std::size_t code_size, KMemoryManager::Pool pool) {
+Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
+ VAddr code_addr, std::size_t code_size,
+ KMemoryManager::Pool pool) {
const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) {
return KAddressSpaceInfo::GetAddressSpaceStart(address_space_width, type);
@@ -84,7 +65,6 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
std::size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)};
std::size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)};
- ASSERT(start <= code_addr);
ASSERT(code_addr < code_addr + code_size);
ASSERT(code_addr + code_size - 1 <= end - 1);
@@ -147,7 +127,7 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
const std::size_t needed_size{
(alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)};
if (alloc_size < needed_size) {
- UNREACHABLE();
+ ASSERT(false);
return ResultOutOfMemory;
}
@@ -277,8 +257,8 @@ ResultCode KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_
return InitializeMemoryLayout(start, end);
}
-ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state,
- KMemoryPermission perm) {
+Result KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemoryState state,
+ KMemoryPermission perm) {
const u64 size{num_pages * PageSize};
// Validate the mapping request.
@@ -291,89 +271,367 @@ ResultCode KPageTable::MapProcessCode(VAddr addr, std::size_t num_pages, KMemory
R_TRY(this->CheckMemoryState(addr, size, KMemoryState::All, KMemoryState::Free,
KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::None, KMemoryAttribute::None));
+ KPageGroup pg;
+ R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
+ &pg, num_pages,
+ KMemoryManager::EncodeOption(KMemoryManager::Pool::Application, allocation_option)));
- KPageLinkedList page_linked_list;
- R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool,
- allocation_option));
- R_TRY(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup));
+ R_TRY(Operate(addr, num_pages, pg, OperationType::MapGroup));
block_manager->Update(addr, num_pages, state, perm);
return ResultSuccess;
}
-ResultCode KPageTable::MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size) {
+ // Validate the mapping request.
+ R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
+ ResultInvalidMemoryRegion);
+
+ // Lock the table.
KScopedLightLock lk(general_lock);
- const std::size_t num_pages{size / PageSize};
+ // Verify that the source memory is normal heap.
+ KMemoryState src_state{};
+ KMemoryPermission src_perm{};
+ std::size_t num_src_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(&src_state, &src_perm, nullptr, &num_src_allocator_blocks,
+ src_address, size, KMemoryState::All, KMemoryState::Normal,
+ KMemoryPermission::All, KMemoryPermission::UserReadWrite,
+ KMemoryAttribute::All, KMemoryAttribute::None));
- KMemoryState state{};
- KMemoryPermission perm{};
- CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, nullptr, src_addr, size,
- KMemoryState::All, KMemoryState::Normal, KMemoryPermission::All,
- KMemoryPermission::UserReadWrite, KMemoryAttribute::Mask,
- KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
+ // Verify that the destination memory is unmapped.
+ std::size_t num_dst_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(&num_dst_allocator_blocks, dst_address, size, KMemoryState::All,
+ KMemoryState::Free, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::None,
+ KMemoryAttribute::None));
- if (IsRegionMapped(dst_addr, size)) {
- return ResultInvalidCurrentMemory;
+ // Map the code memory.
+ {
+ // Determine the number of pages being operated on.
+ const std::size_t num_pages = size / PageSize;
+
+ // Create page groups for the memory being mapped.
+ KPageGroup pg;
+ AddRegionToPages(src_address, num_pages, pg);
+
+ // Reprotect the source as kernel-read/not mapped.
+ const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |
+ KMemoryPermission::NotMapped);
+ R_TRY(Operate(src_address, num_pages, new_perm, OperationType::ChangePermissions));
+
+ // Ensure that we unprotect the source pages on failure.
+ auto unprot_guard = SCOPE_GUARD({
+ ASSERT(this->Operate(src_address, num_pages, src_perm, OperationType::ChangePermissions)
+ .IsSuccess());
+ });
+
+ // Map the alias pages.
+ R_TRY(MapPages(dst_address, pg, new_perm));
+
+ // We successfully mapped the alias pages, so we don't need to unprotect the src pages on
+ // failure.
+ unprot_guard.Cancel();
+
+ // Apply the memory block updates.
+ block_manager->Update(src_address, num_pages, src_state, new_perm,
+ KMemoryAttribute::Locked);
+ block_manager->Update(dst_address, num_pages, KMemoryState::AliasCode, new_perm,
+ KMemoryAttribute::None);
}
- KPageLinkedList page_linked_list;
- AddRegionToPages(src_addr, num_pages, page_linked_list);
+ return ResultSuccess;
+}
+
+Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
+ ICacheInvalidationStrategy icache_invalidation_strategy) {
+ // Validate the mapping request.
+ R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode),
+ ResultInvalidMemoryRegion);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Verify that the source memory is locked normal heap.
+ std::size_t num_src_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(std::addressof(num_src_allocator_blocks), src_address, size,
+ KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::Locked));
+
+ // Verify that the destination memory is aliasable code.
+ std::size_t num_dst_allocator_blocks{};
+ R_TRY(this->CheckMemoryStateContiguous(
+ std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
+ KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::All, KMemoryAttribute::None));
+ // Determine whether any pages being unmapped are code.
+ bool any_code_pages = false;
{
- auto block_guard = detail::ScopeExit(
- [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); });
+ KMemoryBlockManager::const_iterator it = block_manager->FindIterator(dst_address);
+ while (true) {
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
+
+ // Check if the memory has code flag.
+ if ((info.GetState() & KMemoryState::FlagCode) != KMemoryState::None) {
+ any_code_pages = true;
+ break;
+ }
- CASCADE_CODE(Operate(src_addr, num_pages, KMemoryPermission::None,
- OperationType::ChangePermissions));
- CASCADE_CODE(MapPages(dst_addr, page_linked_list, KMemoryPermission::None));
+ // Check if we're done.
+ if (dst_address + size - 1 <= info.GetLastAddress()) {
+ break;
+ }
- block_guard.Cancel();
+ // Advance.
+ ++it;
+ }
}
- block_manager->Update(src_addr, num_pages, state, KMemoryPermission::None,
- KMemoryAttribute::Locked);
- block_manager->Update(dst_addr, num_pages, KMemoryState::AliasCode);
+ // Ensure that we maintain the instruction cache.
+ bool reprotected_pages = false;
+ SCOPE_EXIT({
+ if (reprotected_pages && any_code_pages) {
+ if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) {
+ system.InvalidateCpuInstructionCacheRange(dst_address, size);
+ } else {
+ system.InvalidateCpuInstructionCaches();
+ }
+ }
+ });
+
+ // Unmap.
+ {
+ // Determine the number of pages being operated on.
+ const std::size_t num_pages = size / PageSize;
+
+ // Unmap the aliased copy of the pages.
+ R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap));
+
+ // Try to set the permissions for the source pages back to what they should be.
+ R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite,
+ OperationType::ChangePermissions));
+
+ // Apply the memory block updates.
+ block_manager->Update(dst_address, num_pages, KMemoryState::None);
+ block_manager->Update(src_address, num_pages, KMemoryState::Normal,
+ KMemoryPermission::UserReadWrite);
+
+ // Note that we reprotected pages.
+ reprotected_pages = true;
+ }
return ResultSuccess;
}
-ResultCode KPageTable::UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
- KScopedLightLock lk(general_lock);
+VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages,
+ std::size_t num_pages, std::size_t alignment, std::size_t offset,
+ std::size_t guard_pages) {
+ VAddr address = 0;
+
+ if (num_pages <= region_num_pages) {
+ if (this->IsAslrEnabled()) {
+ // Try to directly find a free area up to 8 times.
+ for (std::size_t i = 0; i < 8; i++) {
+ const std::size_t random_offset =
+ KSystemControl::GenerateRandomRange(
+ 0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) *
+ alignment;
+ const VAddr candidate =
+ Common::AlignDown((region_start + random_offset), alignment) + offset;
+
+ KMemoryInfo info = this->QueryInfoImpl(candidate);
+
+ if (info.state != KMemoryState::Free) {
+ continue;
+ }
+ if (region_start > candidate) {
+ continue;
+ }
+ if (info.GetAddress() + guard_pages * PageSize > candidate) {
+ continue;
+ }
+
+ const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1;
+ if (candidate_end > info.GetLastAddress()) {
+ continue;
+ }
+ if (candidate_end > region_start + region_num_pages * PageSize - 1) {
+ continue;
+ }
+
+ address = candidate;
+ break;
+ }
+ // Fall back to finding the first free area with a random offset.
+ if (address == 0) {
+ // NOTE: Nintendo does not account for guard pages here.
+ // This may theoretically cause an offset to be chosen that cannot be mapped. We
+ // will account for guard pages.
+ const std::size_t offset_pages = KSystemControl::GenerateRandomRange(
+ 0, region_num_pages - num_pages - guard_pages);
+ address = block_manager->FindFreeArea(region_start + offset_pages * PageSize,
+ region_num_pages - offset_pages, num_pages,
+ alignment, offset, guard_pages);
+ }
+ }
- if (!size) {
- return ResultSuccess;
+ // Find the first free area.
+ if (address == 0) {
+ address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages,
+ alignment, offset, guard_pages);
+ }
}
- const std::size_t num_pages{size / PageSize};
+ return address;
+}
- CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, nullptr, src_addr, size,
- KMemoryState::All, KMemoryState::Normal, KMemoryPermission::None,
- KMemoryPermission::None, KMemoryAttribute::Mask,
- KMemoryAttribute::Locked, KMemoryAttribute::IpcAndDeviceMapped));
+Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) {
+ ASSERT(this->IsLockedByCurrentThread());
- KMemoryState state{};
- CASCADE_CODE(CheckMemoryState(
- &state, nullptr, nullptr, nullptr, dst_addr, PageSize, KMemoryState::FlagCanCodeAlias,
- KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
- KMemoryAttribute::Mask, KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
- CASCADE_CODE(CheckMemoryState(dst_addr, size, KMemoryState::All, state, KMemoryPermission::None,
- KMemoryPermission::None, KMemoryAttribute::Mask,
- KMemoryAttribute::None));
- CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
+ const size_t size = num_pages * PageSize;
- block_manager->Update(dst_addr, num_pages, KMemoryState::Free);
- block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
- KMemoryPermission::UserReadWrite);
+ // We're making a new group, not adding to an existing one.
+ R_UNLESS(pg.Empty(), ResultInvalidCurrentMemory);
+
+ // Begin traversal.
+ Common::PageTable::TraversalContext context;
+ Common::PageTable::TraversalEntry next_entry;
+ R_UNLESS(page_table_impl.BeginTraversal(next_entry, context, addr), ResultInvalidCurrentMemory);
- system.InvalidateCpuInstructionCacheRange(dst_addr, size);
+ // Prepare tracking variables.
+ PAddr cur_addr = next_entry.phys_addr;
+ size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
+ size_t tot_size = cur_size;
+
+ // Iterate, adding to group as we go.
+ const auto& memory_layout = system.Kernel().MemoryLayout();
+ while (tot_size < size) {
+ R_UNLESS(page_table_impl.ContinueTraversal(next_entry, context),
+ ResultInvalidCurrentMemory);
+
+ if (next_entry.phys_addr != (cur_addr + cur_size)) {
+ const size_t cur_pages = cur_size / PageSize;
+
+ R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
+ R_TRY(pg.AddBlock(cur_addr, cur_pages));
+
+ cur_addr = next_entry.phys_addr;
+ cur_size = next_entry.block_size;
+ } else {
+ cur_size += next_entry.block_size;
+ }
+
+ tot_size += next_entry.block_size;
+ }
+
+ // Ensure we add the right amount for the last block.
+ if (tot_size > size) {
+ cur_size -= (tot_size - size);
+ }
+
+ // Add the last block.
+ const size_t cur_pages = cur_size / PageSize;
+ R_UNLESS(IsHeapPhysicalAddress(memory_layout, cur_addr), ResultInvalidCurrentMemory);
+ R_TRY(pg.AddBlock(cur_addr, cur_pages));
return ResultSuccess;
}
-ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
- KPageTable& src_page_table, VAddr src_addr) {
+bool KPageTable::IsValidPageGroup(const KPageGroup& pg_ll, VAddr addr, size_t num_pages) {
+ ASSERT(this->IsLockedByCurrentThread());
+
+ const size_t size = num_pages * PageSize;
+ const auto& pg = pg_ll.Nodes();
+ const auto& memory_layout = system.Kernel().MemoryLayout();
+
+ // Empty groups are necessarily invalid.
+ if (pg.empty()) {
+ return false;
+ }
+
+ // We're going to validate that the group we'd expect is the group we see.
+ auto cur_it = pg.begin();
+ PAddr cur_block_address = cur_it->GetAddress();
+ size_t cur_block_pages = cur_it->GetNumPages();
+
+ auto UpdateCurrentIterator = [&]() {
+ if (cur_block_pages == 0) {
+ if ((++cur_it) == pg.end()) {
+ return false;
+ }
+
+ cur_block_address = cur_it->GetAddress();
+ cur_block_pages = cur_it->GetNumPages();
+ }
+ return true;
+ };
+
+ // Begin traversal.
+ Common::PageTable::TraversalContext context;
+ Common::PageTable::TraversalEntry next_entry;
+ if (!page_table_impl.BeginTraversal(next_entry, context, addr)) {
+ return false;
+ }
+
+ // Prepare tracking variables.
+ PAddr cur_addr = next_entry.phys_addr;
+ size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1));
+ size_t tot_size = cur_size;
+
+ // Iterate, comparing expected to actual.
+ while (tot_size < size) {
+ if (!page_table_impl.ContinueTraversal(next_entry, context)) {
+ return false;
+ }
+
+ if (next_entry.phys_addr != (cur_addr + cur_size)) {
+ const size_t cur_pages = cur_size / PageSize;
+
+ if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
+ return false;
+ }
+
+ if (!UpdateCurrentIterator()) {
+ return false;
+ }
+
+ if (cur_block_address != cur_addr || cur_block_pages < cur_pages) {
+ return false;
+ }
+
+ cur_block_address += cur_size;
+ cur_block_pages -= cur_pages;
+ cur_addr = next_entry.phys_addr;
+ cur_size = next_entry.block_size;
+ } else {
+ cur_size += next_entry.block_size;
+ }
+
+ tot_size += next_entry.block_size;
+ }
+
+ // Ensure we compare the right amount for the last block.
+ if (tot_size > size) {
+ cur_size -= (tot_size - size);
+ }
+
+ if (!IsHeapPhysicalAddress(memory_layout, cur_addr)) {
+ return false;
+ }
+
+ if (!UpdateCurrentIterator()) {
+ return false;
+ }
+
+ return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize);
+}
+
+Result KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
+ VAddr src_addr) {
KScopedLightLock lk(general_lock);
const std::size_t num_pages{size / PageSize};
@@ -397,156 +655,486 @@ ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
KMemoryAttribute::None);
+ system.InvalidateCpuInstructionCaches();
+
return ResultSuccess;
}
-ResultCode KPageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
+Result KPageTable::MapPhysicalMemory(VAddr address, std::size_t size) {
// Lock the physical memory lock.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
- // Lock the table.
- KScopedLightLock lk(general_lock);
-
- std::size_t mapped_size{};
- const VAddr end_addr{addr + size};
+ // Calculate the last address for convenience.
+ const VAddr last_address = address + size - 1;
- block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
- if (info.state != KMemoryState::Free) {
- mapped_size += GetSizeInRange(info, addr, end_addr);
- }
- });
+ // Define iteration variables.
+ VAddr cur_address;
+ std::size_t mapped_size;
- if (mapped_size == size) {
- return ResultSuccess;
- }
+ // The entire mapping process can be retried.
+ while (true) {
+ // Check if the memory is already mapped.
+ {
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Iterate over the memory.
+ cur_address = address;
+ mapped_size = 0;
+
+ auto it = block_manager->FindIterator(cur_address);
+ while (true) {
+ // Check that the iterator is valid.
+ ASSERT(it != block_manager->end());
+
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
+
+ // Check if we're done.
+ if (last_address <= info.GetLastAddress()) {
+ if (info.GetState() != KMemoryState::Free) {
+ mapped_size += (last_address + 1 - cur_address);
+ }
+ break;
+ }
+
+ // Track the memory if it's mapped.
+ if (info.GetState() != KMemoryState::Free) {
+ mapped_size += VAddr(info.GetEndAddress()) - cur_address;
+ }
+
+ // Advance.
+ cur_address = info.GetEndAddress();
+ ++it;
+ }
- const std::size_t remaining_size{size - mapped_size};
- const std::size_t remaining_pages{remaining_size / PageSize};
+ // If the size mapped is the size requested, we've nothing to do.
+ R_SUCCEED_IF(size == mapped_size);
+ }
- // Reserve the memory from the process resource limit.
- KScopedResourceReservation memory_reservation(
- system.Kernel().CurrentProcess()->GetResourceLimit(), LimitableResource::PhysicalMemory,
- remaining_size);
- if (!memory_reservation.Succeeded()) {
- LOG_ERROR(Kernel, "Could not reserve remaining {:X} bytes", remaining_size);
- return ResultLimitReached;
+ // Allocate and map the memory.
+ {
+ // Reserve the memory from the process resource limit.
+ KScopedResourceReservation memory_reservation(
+ system.Kernel().CurrentProcess()->GetResourceLimit(),
+ LimitableResource::PhysicalMemory, size - mapped_size);
+ R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
+
+ // Allocate pages for the new memory.
+ KPageGroup pg;
+ R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
+ &pg, (size - mapped_size) / PageSize,
+ KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
+
+ // Map the memory.
+ {
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ size_t num_allocator_blocks = 0;
+
+ // Verify that nobody has mapped memory since we first checked.
+ {
+ // Iterate over the memory.
+ size_t checked_mapped_size = 0;
+ cur_address = address;
+
+ auto it = block_manager->FindIterator(cur_address);
+ while (true) {
+ // Check that the iterator is valid.
+ ASSERT(it != block_manager->end());
+
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
+
+ const bool is_free = info.GetState() == KMemoryState::Free;
+ if (is_free) {
+ if (info.GetAddress() < address) {
+ ++num_allocator_blocks;
+ }
+ if (last_address < info.GetLastAddress()) {
+ ++num_allocator_blocks;
+ }
+ }
+
+ // Check if we're done.
+ if (last_address <= info.GetLastAddress()) {
+ if (!is_free) {
+ checked_mapped_size += (last_address + 1 - cur_address);
+ }
+ break;
+ }
+
+ // Track the memory if it's mapped.
+ if (!is_free) {
+ checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address;
+ }
+
+ // Advance.
+ cur_address = info.GetEndAddress();
+ ++it;
+ }
+
+ // If the size now isn't what it was before, somebody mapped or unmapped
+ // concurrently. If this happened, retry.
+ if (mapped_size != checked_mapped_size) {
+ continue;
+ }
+ }
+
+ // Reset the current tracking address, and make sure we clean up on failure.
+ cur_address = address;
+ auto unmap_guard = detail::ScopeExit([&] {
+ if (cur_address > address) {
+ const VAddr last_unmap_address = cur_address - 1;
+
+ // Iterate, unmapping the pages.
+ cur_address = address;
+
+ auto it = block_manager->FindIterator(cur_address);
+ while (true) {
+ // Check that the iterator is valid.
+ ASSERT(it != block_manager->end());
+
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
+
+ // If the memory state is free, we mapped it and need to unmap it.
+ if (info.GetState() == KMemoryState::Free) {
+ // Determine the range to unmap.
+ const size_t cur_pages =
+ std::min(VAddr(info.GetEndAddress()) - cur_address,
+ last_unmap_address + 1 - cur_address) /
+ PageSize;
+
+ // Unmap.
+ ASSERT(Operate(cur_address, cur_pages, KMemoryPermission::None,
+ OperationType::Unmap)
+ .IsSuccess());
+ }
+
+ // Check if we're done.
+ if (last_unmap_address <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance.
+ cur_address = info.GetEndAddress();
+ ++it;
+ }
+ }
+ });
+
+ // Iterate over the memory.
+ auto pg_it = pg.Nodes().begin();
+ PAddr pg_phys_addr = pg_it->GetAddress();
+ size_t pg_pages = pg_it->GetNumPages();
+
+ auto it = block_manager->FindIterator(cur_address);
+ while (true) {
+ // Check that the iterator is valid.
+ ASSERT(it != block_manager->end());
+
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
+
+ // If it's unmapped, we need to map it.
+ if (info.GetState() == KMemoryState::Free) {
+ // Determine the range to map.
+ size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
+ last_address + 1 - cur_address) /
+ PageSize;
+
+ // While we have pages to map, map them.
+ while (map_pages > 0) {
+ // Check if we're at the end of the physical block.
+ if (pg_pages == 0) {
+ // Ensure there are more pages to map.
+ ASSERT(pg_it != pg.Nodes().end());
+
+ // Advance our physical block.
+ ++pg_it;
+ pg_phys_addr = pg_it->GetAddress();
+ pg_pages = pg_it->GetNumPages();
+ }
+
+ // Map whatever we can.
+ const size_t cur_pages = std::min(pg_pages, map_pages);
+ R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
+ OperationType::Map, pg_phys_addr));
+
+ // Advance.
+ cur_address += cur_pages * PageSize;
+ map_pages -= cur_pages;
+
+ pg_phys_addr += cur_pages * PageSize;
+ pg_pages -= cur_pages;
+ }
+ }
+
+ // Check if we're done.
+ if (last_address <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance.
+ cur_address = info.GetEndAddress();
+ ++it;
+ }
+
+ // We succeeded, so commit the memory reservation.
+ memory_reservation.Commit();
+
+ // Increase our tracked mapped size.
+ mapped_physical_memory_size += (size - mapped_size);
+
+ // Update the relevant memory blocks.
+ block_manager->Update(address, size / PageSize, KMemoryState::Free,
+ KMemoryPermission::None, KMemoryAttribute::None,
+ KMemoryState::Normal, KMemoryPermission::UserReadWrite,
+ KMemoryAttribute::None);
+
+ // Cancel our guard.
+ unmap_guard.Cancel();
+
+ return ResultSuccess;
+ }
+ }
}
+}
- KPageLinkedList page_linked_list;
+Result KPageTable::UnmapPhysicalMemory(VAddr address, std::size_t size) {
+ // Lock the physical memory lock.
+ KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
- CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
- memory_pool, allocation_option));
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
- // We succeeded, so commit the memory reservation.
- memory_reservation.Commit();
+ // Calculate the last address for convenience.
+ const VAddr last_address = address + size - 1;
- // Map the memory.
- auto node{page_linked_list.Nodes().begin()};
- PAddr map_addr{node->GetAddress()};
- std::size_t src_num_pages{node->GetNumPages()};
- block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
- if (info.state != KMemoryState::Free) {
- return;
- }
+ // Define iteration variables.
+ VAddr cur_address = 0;
+ std::size_t mapped_size = 0;
+ std::size_t num_allocator_blocks = 0;
- std::size_t dst_num_pages{GetSizeInRange(info, addr, end_addr) / PageSize};
- VAddr dst_addr{GetAddressInRange(info, addr)};
+ // Check if the memory is mapped.
+ {
+ // Iterate over the memory.
+ cur_address = address;
+ mapped_size = 0;
+
+ auto it = block_manager->FindIterator(cur_address);
+ while (true) {
+ // Check that the iterator is valid.
+ ASSERT(it != block_manager->end());
+
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
+
+ // Verify the memory's state.
+ const bool is_normal = info.GetState() == KMemoryState::Normal &&
+ info.GetAttribute() == KMemoryAttribute::None;
+ const bool is_free = info.GetState() == KMemoryState::Free;
+ R_UNLESS(is_normal || is_free, ResultInvalidCurrentMemory);
+
+ if (is_normal) {
+ R_UNLESS(info.GetAttribute() == KMemoryAttribute::None, ResultInvalidCurrentMemory);
+
+ if (info.GetAddress() < address) {
+ ++num_allocator_blocks;
+ }
+ if (last_address < info.GetLastAddress()) {
+ ++num_allocator_blocks;
+ }
+ }
- while (dst_num_pages) {
- if (!src_num_pages) {
- node = std::next(node);
- map_addr = node->GetAddress();
- src_num_pages = node->GetNumPages();
+ // Check if we're done.
+ if (last_address <= info.GetLastAddress()) {
+ if (is_normal) {
+ mapped_size += (last_address + 1 - cur_address);
+ }
+ break;
}
- const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)};
- Operate(dst_addr, num_pages, KMemoryPermission::UserReadWrite, OperationType::Map,
- map_addr);
+ // Track the memory if it's mapped.
+ if (is_normal) {
+ mapped_size += VAddr(info.GetEndAddress()) - cur_address;
+ }
- dst_addr += num_pages * PageSize;
- map_addr += num_pages * PageSize;
- src_num_pages -= num_pages;
- dst_num_pages -= num_pages;
+ // Advance.
+ cur_address = info.GetEndAddress();
+ ++it;
}
- });
- mapped_physical_memory_size += remaining_size;
-
- const std::size_t num_pages{size / PageSize};
- block_manager->Update(addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
- KMemoryAttribute::None, KMemoryState::Normal,
- KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
-
- return ResultSuccess;
-}
+ // If there's nothing mapped, we've nothing to do.
+ R_SUCCEED_IF(mapped_size == 0);
+ }
-ResultCode KPageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) {
- // Lock the physical memory lock.
- KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
+ // Make a page group for the unmap region.
+ KPageGroup pg;
+ {
+ auto& impl = this->PageTableImpl();
+
+ // Begin traversal.
+ Common::PageTable::TraversalContext context;
+ Common::PageTable::TraversalEntry cur_entry = {.phys_addr = 0, .block_size = 0};
+ bool cur_valid = false;
+ Common::PageTable::TraversalEntry next_entry;
+ bool next_valid = false;
+ size_t tot_size = 0;
+
+ cur_address = address;
+ next_valid = impl.BeginTraversal(next_entry, context, cur_address);
+ next_entry.block_size =
+ (next_entry.block_size - (next_entry.phys_addr & (next_entry.block_size - 1)));
+
+ // Iterate, building the group.
+ while (true) {
+ if ((!next_valid && !cur_valid) ||
+ (next_valid && cur_valid &&
+ next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) {
+ cur_entry.block_size += next_entry.block_size;
+ } else {
+ if (cur_valid) {
+ // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
+ R_TRY(pg.AddBlock(cur_entry.phys_addr, cur_entry.block_size / PageSize));
+ }
+
+ // Update tracking variables.
+ tot_size += cur_entry.block_size;
+ cur_entry = next_entry;
+ cur_valid = next_valid;
+ }
- // Lock the table.
- KScopedLightLock lk(general_lock);
+ if (cur_entry.block_size + tot_size >= size) {
+ break;
+ }
- const VAddr end_addr{addr + size};
- ResultCode result{ResultSuccess};
- std::size_t mapped_size{};
+ next_valid = impl.ContinueTraversal(next_entry, context);
+ }
- // Verify that the region can be unmapped
- block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
- if (info.state == KMemoryState::Normal) {
- if (info.attribute != KMemoryAttribute::None) {
- result = ResultInvalidCurrentMemory;
- return;
+ // Add the last block.
+ if (cur_valid) {
+ // ASSERT(IsHeapPhysicalAddress(cur_entry.phys_addr));
+ R_TRY(pg.AddBlock(cur_entry.phys_addr, (size - tot_size) / PageSize));
+ }
+ }
+ ASSERT(pg.GetNumPages() == mapped_size / PageSize);
+
+ // Reset the current tracking address, and make sure we clean up on failure.
+ cur_address = address;
+ auto remap_guard = detail::ScopeExit([&] {
+ if (cur_address > address) {
+ const VAddr last_map_address = cur_address - 1;
+ cur_address = address;
+
+ // Iterate over the memory we unmapped.
+ auto it = block_manager->FindIterator(cur_address);
+ auto pg_it = pg.Nodes().begin();
+ PAddr pg_phys_addr = pg_it->GetAddress();
+ size_t pg_pages = pg_it->GetNumPages();
+
+ while (true) {
+ // Get the memory info for the pages we unmapped, convert to property.
+ const KMemoryInfo info = it->GetMemoryInfo();
+
+ // If the memory is normal, we unmapped it and need to re-map it.
+ if (info.GetState() == KMemoryState::Normal) {
+ // Determine the range to map.
+ size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
+ last_map_address + 1 - cur_address) /
+ PageSize;
+
+ // While we have pages to map, map them.
+ while (map_pages > 0) {
+ // Check if we're at the end of the physical block.
+ if (pg_pages == 0) {
+ // Ensure there are more pages to map.
+ ASSERT(pg_it != pg.Nodes().end());
+
+ // Advance our physical block.
+ ++pg_it;
+ pg_phys_addr = pg_it->GetAddress();
+ pg_pages = pg_it->GetNumPages();
+ }
+
+ // Map whatever we can.
+ const size_t cur_pages = std::min(pg_pages, map_pages);
+ ASSERT(this->Operate(cur_address, cur_pages, info.GetPermission(),
+ OperationType::Map, pg_phys_addr) == ResultSuccess);
+
+ // Advance.
+ cur_address += cur_pages * PageSize;
+ map_pages -= cur_pages;
+
+ pg_phys_addr += cur_pages * PageSize;
+ pg_pages -= cur_pages;
+ }
+ }
+
+ // Check if we're done.
+ if (last_map_address <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance.
+ ++it;
}
- mapped_size += GetSizeInRange(info, addr, end_addr);
- } else if (info.state != KMemoryState::Free) {
- result = ResultInvalidCurrentMemory;
}
});
- if (result.IsError()) {
- return result;
- }
-
- if (!mapped_size) {
- return ResultSuccess;
- }
+ // Iterate over the memory, unmapping as we go.
+ auto it = block_manager->FindIterator(cur_address);
+ while (true) {
+ // Check that the iterator is valid.
+ ASSERT(it != block_manager->end());
- // Unmap each region within the range
- KPageLinkedList page_linked_list;
- block_manager->IterateForRange(addr, end_addr, [&](const KMemoryInfo& info) {
- if (info.state == KMemoryState::Normal) {
- const std::size_t block_size{GetSizeInRange(info, addr, end_addr)};
- const std::size_t block_num_pages{block_size / PageSize};
- const VAddr block_addr{GetAddressInRange(info, addr)};
+ // Get the memory info.
+ const KMemoryInfo info = it->GetMemoryInfo();
- AddRegionToPages(block_addr, block_size / PageSize, page_linked_list);
+ // If the memory state is normal, we need to unmap it.
+ if (info.GetState() == KMemoryState::Normal) {
+ // Determine the range to unmap.
+ const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address,
+ last_address + 1 - cur_address) /
+ PageSize;
- if (result = Operate(block_addr, block_num_pages, KMemoryPermission::None,
- OperationType::Unmap);
- result.IsError()) {
- return;
- }
+ // Unmap.
+ R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::None, OperationType::Unmap));
}
- });
- if (result.IsError()) {
- return result;
- }
- const std::size_t num_pages{size / PageSize};
- system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool,
- allocation_option);
+ // Check if we're done.
+ if (last_address <= info.GetLastAddress()) {
+ break;
+ }
- block_manager->Update(addr, num_pages, KMemoryState::Free);
+ // Advance.
+ cur_address = info.GetEndAddress();
+ ++it;
+ }
+ // Release the memory resource.
+ mapped_physical_memory_size -= mapped_size;
auto process{system.Kernel().CurrentProcess()};
process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size);
- mapped_physical_memory_size -= mapped_size;
+
+ // Update memory blocks.
+ block_manager->Update(address, size / PageSize, KMemoryState::Free, KMemoryPermission::None,
+ KMemoryAttribute::None);
+
+ // TODO(bunnei): This is a workaround until the next set of changes, where we add reference
+ // counting for mapped pages. Until then, we must manually close the reference to the page
+ // group.
+ system.Kernel().MemoryManager().Close(pg);
+
+ // We succeeded.
+ remap_guard.Cancel();
return ResultSuccess;
}
-ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+Result KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryState src_state{};
@@ -559,7 +1147,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz
return ResultInvalidCurrentMemory;
}
- KPageLinkedList page_linked_list;
+ KPageGroup page_linked_list;
const std::size_t num_pages{size / PageSize};
AddRegionToPages(src_addr, num_pages, page_linked_list);
@@ -585,7 +1173,7 @@ ResultCode KPageTable::MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t siz
return ResultSuccess;
}
-ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
+Result KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryState src_state{};
@@ -600,8 +1188,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s
KMemoryPermission::None, KMemoryAttribute::Mask,
KMemoryAttribute::None, KMemoryAttribute::IpcAndDeviceMapped));
- KPageLinkedList src_pages;
- KPageLinkedList dst_pages;
+ KPageGroup src_pages;
+ KPageGroup dst_pages;
const std::size_t num_pages{size / PageSize};
AddRegionToPages(src_addr, num_pages, src_pages);
@@ -627,8 +1215,8 @@ ResultCode KPageTable::UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t s
return ResultSuccess;
}
-ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
- KMemoryPermission perm) {
+Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list,
+ KMemoryPermission perm) {
ASSERT(this->IsLockedByCurrentThread());
VAddr cur_addr{addr};
@@ -651,8 +1239,8 @@ ResultCode KPageTable::MapPages(VAddr addr, const KPageLinkedList& page_linked_l
return ResultSuccess;
}
-ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list,
- KMemoryState state, KMemoryPermission perm) {
+Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state,
+ KMemoryPermission perm) {
// Check that the map is in range.
const std::size_t num_pages{page_linked_list.GetNumPages()};
const std::size_t size{num_pages * PageSize};
@@ -675,15 +1263,54 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list
return ResultSuccess;
}
-ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) {
+Result KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment,
+ PAddr phys_addr, bool is_pa_valid, VAddr region_start,
+ std::size_t region_num_pages, KMemoryState state,
+ KMemoryPermission perm) {
+ ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize);
+
+ // Ensure this is a valid map request.
+ R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state),
+ ResultInvalidCurrentMemory);
+ R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Find a random address to map at.
+ VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0,
+ this->GetNumGuardPages());
+ R_UNLESS(addr != 0, ResultOutOfMemory);
+ ASSERT(Common::IsAligned(addr, alignment));
+ ASSERT(this->CanContain(addr, num_pages * PageSize, state));
+ ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,
+ KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::None, KMemoryAttribute::None)
+ .IsSuccess());
+
+ // Perform mapping operation.
+ if (is_pa_valid) {
+ R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr));
+ } else {
+ UNIMPLEMENTED();
+ }
+
+ // Update the blocks.
+ block_manager->Update(addr, num_pages, state, perm);
+
+ // We successfully mapped the pages.
+ *out_addr = addr;
+ return ResultSuccess;
+}
+
+Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) {
ASSERT(this->IsLockedByCurrentThread());
VAddr cur_addr{addr};
for (const auto& node : page_linked_list.Nodes()) {
- const std::size_t num_pages{(addr - cur_addr) / PageSize};
- if (const auto result{
- Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap)};
+ if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None,
+ OperationType::Unmap)};
result.IsError()) {
return result;
}
@@ -694,8 +1321,7 @@ ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked
return ResultSuccess;
}
-ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
- KMemoryState state) {
+Result KPageTable::UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state) {
// Check that the unmap is in range.
const std::size_t num_pages{page_linked_list.GetNumPages()};
const std::size_t size{num_pages * PageSize};
@@ -718,8 +1344,57 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list,
return ResultSuccess;
}
-ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
- Svc::MemoryPermission svc_perm) {
+Result KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) {
+ // Check that the unmap is in range.
+ const std::size_t size = num_pages * PageSize;
+ R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Check the memory state.
+ std::size_t num_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,
+ KMemoryState::All, state, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::None));
+
+ // Perform the unmap.
+ R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap));
+
+ // Update the blocks.
+ block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None);
+
+ return ResultSuccess;
+}
+
+Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) {
+ // Ensure that the page group isn't null.
+ ASSERT(out != nullptr);
+
+ // Make sure that the region we're mapping is valid for the table.
+ const size_t size = num_pages * PageSize;
+ R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Check if state allows us to create the group.
+ R_TRY(this->CheckMemoryState(address, size, state_mask | KMemoryState::FlagReferenceCounted,
+ state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
+ attr_mask, attr));
+
+ // Create a new page group for the region.
+ R_TRY(this->MakePageGroup(*out, address, num_pages));
+
+ return ResultSuccess;
+}
+
+Result KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
+ Svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
// Lock the table.
@@ -753,7 +1428,7 @@ ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size,
new_state = KMemoryState::AliasCodeData;
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
}
@@ -791,7 +1466,7 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) {
return QueryInfoImpl(addr);
}
-ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
+Result KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm) {
KScopedLightLock lk(general_lock);
KMemoryState state{};
@@ -809,7 +1484,7 @@ ResultCode KPageTable::ReserveTransferMemory(VAddr addr, std::size_t size, KMemo
return ResultSuccess;
}
-ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
+Result KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryState state{};
@@ -824,8 +1499,8 @@ ResultCode KPageTable::ResetTransferMemory(VAddr addr, std::size_t size) {
return ResultSuccess;
}
-ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
- Svc::MemoryPermission svc_perm) {
+Result KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
+ Svc::MemoryPermission svc_perm) {
const size_t num_pages = size / PageSize;
// Lock the table.
@@ -852,7 +1527,7 @@ ResultCode KPageTable::SetMemoryPermission(VAddr addr, std::size_t size,
return ResultSuccess;
}
-ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) {
+Result KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr) {
const size_t num_pages = size / PageSize;
ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) ==
KMemoryAttribute::SetMask);
@@ -887,7 +1562,7 @@ ResultCode KPageTable::SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask
return ResultSuccess;
}
-ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
+Result KPageTable::SetMaxHeapSize(std::size_t size) {
// Lock the table.
KScopedLightLock lk(general_lock);
@@ -899,7 +1574,7 @@ ResultCode KPageTable::SetMaxHeapSize(std::size_t size) {
return ResultSuccess;
}
-ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
+Result KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Lock the physical memory mutex.
KScopedLightLock map_phys_mem_lk(map_physical_memory_lock);
@@ -966,9 +1641,16 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached);
// Allocate pages for the heap extension.
- KPageLinkedList page_linked_list;
- R_TRY(system.Kernel().MemoryManager().Allocate(page_linked_list, allocation_size / PageSize,
- memory_pool, allocation_option));
+ KPageGroup pg;
+ R_TRY(system.Kernel().MemoryManager().AllocateAndOpen(
+ &pg, allocation_size / PageSize,
+ KMemoryManager::EncodeOption(memory_pool, allocation_option)));
+
+ // Clear all the newly allocated pages.
+ for (const auto& it : pg.Nodes()) {
+ std::memset(system.DeviceMemory().GetPointer(it.GetAddress()), heap_fill_value,
+ it.GetSize());
+ }
// Map the pages.
{
@@ -987,7 +1669,7 @@ ResultCode KPageTable::SetHeapSize(VAddr* out, std::size_t size) {
// Map the pages.
const auto num_pages = allocation_size / PageSize;
- R_TRY(Operate(current_heap_end, num_pages, page_linked_list, OperationType::MapGroup));
+ R_TRY(Operate(current_heap_end, num_pages, pg, OperationType::MapGroup));
// Clear all the newly allocated pages.
for (std::size_t cur_page = 0; cur_page < num_pages; ++cur_page) {
@@ -1034,9 +1716,10 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
if (is_map_only) {
R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr));
} else {
- KPageLinkedList page_group;
- R_TRY(system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool,
- allocation_option));
+ KPageGroup page_group;
+ R_TRY(system.Kernel().MemoryManager().AllocateAndOpenForProcess(
+ &page_group, needed_num_pages,
+ KMemoryManager::EncodeOption(memory_pool, allocation_option), 0, 0));
R_TRY(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup));
}
@@ -1045,11 +1728,11 @@ ResultVal<VAddr> KPageTable::AllocateAndMapMemory(std::size_t needed_num_pages,
return addr;
}
-ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
+Result KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryPermission perm{};
- if (const ResultCode result{CheckMemoryState(
+ if (const Result result{CheckMemoryState(
nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
@@ -1068,11 +1751,11 @@ ResultCode KPageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) {
return ResultSuccess;
}
-ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
+Result KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) {
KScopedLightLock lk(general_lock);
KMemoryPermission perm{};
- if (const ResultCode result{CheckMemoryState(
+ if (const Result result{CheckMemoryState(
nullptr, &perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanChangeAttribute,
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
KMemoryAttribute::LockedAndIpcLocked, KMemoryAttribute::None,
@@ -1091,61 +1774,24 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return ResultSuccess;
}
-ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
- KScopedLightLock lk(general_lock);
-
- KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
-
- KMemoryPermission old_perm{};
-
- if (const ResultCode result{CheckMemoryState(
- nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
- KMemoryState::FlagCanCodeMemory, KMemoryPermission::All,
- KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
- result.IsError()) {
- return result;
- }
-
- new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
-
- block_manager->UpdateLock(
- addr, size / PageSize,
- [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
- block->ShareToDevice(permission);
- },
- new_perm);
-
- return ResultSuccess;
+Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size) {
+ return this->LockMemoryAndOpen(
+ out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
+ KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
+ KMemoryAttribute::None,
+ static_cast<KMemoryPermission>(KMemoryPermission::NotMapped |
+ KMemoryPermission::KernelReadWrite),
+ KMemoryAttribute::Locked);
}
-ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
- KScopedLightLock lk(general_lock);
-
- KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
-
- KMemoryPermission old_perm{};
-
- if (const ResultCode result{CheckMemoryState(
- nullptr, &old_perm, nullptr, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
- KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
- KMemoryAttribute::All, KMemoryAttribute::Locked)};
- result.IsError()) {
- return result;
- }
-
- new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
-
- block_manager->UpdateLock(
- addr, size / PageSize,
- [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
- block->UnshareToDevice(permission);
- },
- new_perm);
-
- return ResultSuccess;
+Result KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg) {
+ return this->UnlockMemory(
+ addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
+ KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg);
}
-ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
+Result KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
block_manager = std::make_unique<KMemoryBlockManager>(start, end);
return ResultSuccess;
@@ -1170,13 +1816,11 @@ bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const {
}
void KPageTable::AddRegionToPages(VAddr start, std::size_t num_pages,
- KPageLinkedList& page_linked_list) {
+ KPageGroup& page_linked_list) {
VAddr addr{start};
while (addr < start + (num_pages * PageSize)) {
const PAddr paddr{GetPhysicalAddr(addr)};
- if (!paddr) {
- UNREACHABLE();
- }
+ ASSERT(paddr != 0);
page_linked_list.AddBlock(paddr, 1);
addr += PageSize;
}
@@ -1191,8 +1835,8 @@ VAddr KPageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_page
IsKernel() ? 1 : 4);
}
-ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
- OperationType operation) {
+Result KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group,
+ OperationType operation) {
ASSERT(this->IsLockedByCurrentThread());
ASSERT(Common::IsAligned(addr, PageSize));
@@ -1207,7 +1851,7 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress());
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
addr += size;
@@ -1216,8 +1860,8 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, const KPageLin
return ResultSuccess;
}
-ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
- OperationType operation, PAddr map_addr) {
+Result KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
+ OperationType operation, PAddr map_addr) {
ASSERT(this->IsLockedByCurrentThread());
ASSERT(num_pages > 0);
@@ -1238,12 +1882,12 @@ ResultCode KPageTable::Operate(VAddr addr, std::size_t num_pages, KMemoryPermiss
case OperationType::ChangePermissionsAndRefresh:
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
return ResultSuccess;
}
-constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
+VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
switch (state) {
case KMemoryState::Free:
case KMemoryState::Kernel:
@@ -1275,11 +1919,10 @@ constexpr VAddr KPageTable::GetRegionAddress(KMemoryState state) const {
return code_region_start;
default:
UNREACHABLE();
- return {};
}
}
-constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
+std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
switch (state) {
case KMemoryState::Free:
case KMemoryState::Kernel:
@@ -1311,7 +1954,6 @@ constexpr std::size_t KPageTable::GetRegionSize(KMemoryState state) const {
return code_region_end - code_region_start;
default:
UNREACHABLE();
- return {};
}
}
@@ -1361,10 +2003,10 @@ bool KPageTable::CanContain(VAddr addr, std::size_t size, KMemoryState state) co
}
}
-ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const {
+Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr) const {
// Validate the states match expectation.
R_UNLESS((info.state & state_mask) == state, ResultInvalidCurrentMemory);
R_UNLESS((info.perm & perm_mask) == perm, ResultInvalidCurrentMemory);
@@ -1373,12 +2015,11 @@ ResultCode KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState st
return ResultSuccess;
}
-ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
- std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm,
- KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const {
+Result KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
+ std::size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr) const {
ASSERT(this->IsLockedByCurrentThread());
// Get information about the first block.
@@ -1416,12 +2057,12 @@ ResultCode KPageTable::CheckMemoryStateContiguous(std::size_t* out_blocks_needed
return ResultSuccess;
}
-ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
- KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
- VAddr addr, std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
+Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
+ KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
+ VAddr addr, std::size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
ASSERT(this->IsLockedByCurrentThread());
// Get information about the first block.
@@ -1478,4 +2119,109 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
return ResultSuccess;
}
+Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr) {
+ // Validate basic preconditions.
+ ASSERT((lock_attr & attr) == KMemoryAttribute::None);
+ ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) ==
+ KMemoryAttribute::None);
+
+ // Validate the lock request.
+ const size_t num_pages = size / PageSize;
+ R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Check that the output page group is empty, if it exists.
+ if (out_pg) {
+ ASSERT(out_pg->GetNumPages() == 0);
+ }
+
+ // Check the state.
+ KMemoryState old_state{};
+ KMemoryPermission old_perm{};
+ KMemoryAttribute old_attr{};
+ size_t num_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
+ std::addressof(old_attr), std::addressof(num_allocator_blocks),
+ addr, size, state_mask | KMemoryState::FlagReferenceCounted,
+ state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
+ attr_mask, attr));
+
+ // Get the physical address, if we're supposed to.
+ if (out_paddr != nullptr) {
+ ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr));
+ }
+
+ // Make the page group, if we're supposed to.
+ if (out_pg != nullptr) {
+ R_TRY(this->MakePageGroup(*out_pg, addr, num_pages));
+ }
+
+ // Decide on new perm and attr.
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+ KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr | lock_attr);
+
+ // Update permission, if we need to.
+ if (new_perm != old_perm) {
+ R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
+ }
+
+ // Apply the memory block updates.
+ block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
+
+ return ResultSuccess;
+}
+
+Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr, KMemoryPermission new_perm,
+ KMemoryAttribute lock_attr, const KPageGroup* pg) {
+ // Validate basic preconditions.
+ ASSERT((attr_mask & lock_attr) == lock_attr);
+ ASSERT((attr & lock_attr) == lock_attr);
+
+ // Validate the unlock request.
+ const size_t num_pages = size / PageSize;
+ R_UNLESS(this->Contains(addr, size), ResultInvalidCurrentMemory);
+
+ // Lock the table.
+ KScopedLightLock lk(general_lock);
+
+ // Check the state.
+ KMemoryState old_state{};
+ KMemoryPermission old_perm{};
+ KMemoryAttribute old_attr{};
+ size_t num_allocator_blocks{};
+ R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
+ std::addressof(old_attr), std::addressof(num_allocator_blocks),
+ addr, size, state_mask | KMemoryState::FlagReferenceCounted,
+ state | KMemoryState::FlagReferenceCounted, perm_mask, perm,
+ attr_mask, attr));
+
+ // Check the page group.
+ if (pg != nullptr) {
+ R_UNLESS(this->IsValidPageGroup(*pg, addr, num_pages), ResultInvalidMemoryRegion);
+ }
+
+ // Decide on new perm and attr.
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+ KMemoryAttribute new_attr = static_cast<KMemoryAttribute>(old_attr & ~lock_attr);
+
+ // Update permission, if we need to.
+ if (new_perm != old_perm) {
+ R_TRY(Operate(addr, num_pages, new_perm, OperationType::ChangePermissions));
+ }
+
+ // Apply the memory block updates.
+ block_manager->Update(addr, num_pages, old_state, new_perm, new_attr);
+
+ return ResultSuccess;
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index c98887d34..25774f232 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -12,6 +11,7 @@
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/k_light_lock.h"
#include "core/hle/kernel/k_memory_block.h"
+#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/result.h"
@@ -25,45 +25,57 @@ class KMemoryBlockManager;
class KPageTable final {
public:
+ enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll };
+
YUZU_NON_COPYABLE(KPageTable);
YUZU_NON_MOVEABLE(KPageTable);
explicit KPageTable(Core::System& system_);
~KPageTable();
- ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
- VAddr code_addr, std::size_t code_size,
- KMemoryManager::Pool pool);
- ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
- KMemoryPermission perm);
- ResultCode MapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
- ResultCode UnmapCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
- ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
- VAddr src_addr);
- ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
- ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
- ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
- ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
- ResultCode MapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state,
- KMemoryPermission perm);
- ResultCode UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, KMemoryState state);
- ResultCode SetProcessMemoryPermission(VAddr addr, std::size_t size,
- Svc::MemoryPermission svc_perm);
+ Result InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr,
+ VAddr code_addr, std::size_t code_size, KMemoryManager::Pool pool);
+ Result MapProcessCode(VAddr addr, std::size_t pages_count, KMemoryState state,
+ KMemoryPermission perm);
+ Result MapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size);
+ Result UnmapCodeMemory(VAddr dst_address, VAddr src_address, std::size_t size,
+ ICacheInvalidationStrategy icache_invalidation_strategy);
+ Result UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
+ VAddr src_addr);
+ Result MapPhysicalMemory(VAddr addr, std::size_t size);
+ Result UnmapPhysicalMemory(VAddr addr, std::size_t size);
+ Result MapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ Result UnmapMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state,
+ KMemoryPermission perm);
+ Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr,
+ KMemoryState state, KMemoryPermission perm) {
+ return this->MapPages(out_addr, num_pages, alignment, phys_addr, true,
+ this->GetRegionAddress(state), this->GetRegionSize(state) / PageSize,
+ state, perm);
+ }
+ Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state);
+ Result UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state);
+ Result SetProcessMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission svc_perm);
KMemoryInfo QueryInfo(VAddr addr);
- ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
- ResultCode ResetTransferMemory(VAddr addr, std::size_t size);
- ResultCode SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
- ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
- ResultCode SetMaxHeapSize(std::size_t size);
- ResultCode SetHeapSize(VAddr* out, std::size_t size);
+ Result ReserveTransferMemory(VAddr addr, std::size_t size, KMemoryPermission perm);
+ Result ResetTransferMemory(VAddr addr, std::size_t size);
+ Result SetMemoryPermission(VAddr addr, std::size_t size, Svc::MemoryPermission perm);
+ Result SetMemoryAttribute(VAddr addr, std::size_t size, u32 mask, u32 attr);
+ Result SetMaxHeapSize(std::size_t size);
+ Result SetHeapSize(VAddr* out, std::size_t size);
ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align,
bool is_map_only, VAddr region_start,
std::size_t region_num_pages, KMemoryState state,
KMemoryPermission perm, PAddr map_addr = 0);
- ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
- ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
- ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
- ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
+ Result LockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ Result UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ Result LockForCodeMemory(KPageGroup* out, VAddr addr, std::size_t size);
+ Result UnlockForCodeMemory(VAddr addr, std::size_t size, const KPageGroup& pg);
+ Result MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr);
Common::PageTable& PageTableImpl() {
return page_table_impl;
@@ -88,68 +100,97 @@ private:
KMemoryAttribute::IpcLocked |
KMemoryAttribute::DeviceShared;
- ResultCode InitializeMemoryLayout(VAddr start, VAddr end);
- ResultCode MapPages(VAddr addr, const KPageLinkedList& page_linked_list,
- KMemoryPermission perm);
- ResultCode UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list);
+ Result InitializeMemoryLayout(VAddr start, VAddr end);
+ Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm);
+ Result MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, PAddr phys_addr,
+ bool is_pa_valid, VAddr region_start, std::size_t region_num_pages,
+ KMemoryState state, KMemoryPermission perm);
+ Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list);
bool IsRegionMapped(VAddr address, u64 size);
bool IsRegionContiguous(VAddr addr, u64 size) const;
- void AddRegionToPages(VAddr start, std::size_t num_pages, KPageLinkedList& page_linked_list);
+ void AddRegionToPages(VAddr start, std::size_t num_pages, KPageGroup& page_linked_list);
KMemoryInfo QueryInfoImpl(VAddr addr);
VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages,
std::size_t align);
- ResultCode Operate(VAddr addr, std::size_t num_pages, const KPageLinkedList& page_group,
- OperationType operation);
- ResultCode Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
- OperationType operation, PAddr map_addr = 0);
- constexpr VAddr GetRegionAddress(KMemoryState state) const;
- constexpr std::size_t GetRegionSize(KMemoryState state) const;
-
- ResultCode CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr,
- std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const;
- ResultCode CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const {
+ Result Operate(VAddr addr, std::size_t num_pages, const KPageGroup& page_group,
+ OperationType operation);
+ Result Operate(VAddr addr, std::size_t num_pages, KMemoryPermission perm,
+ OperationType operation, PAddr map_addr = 0);
+ VAddr GetRegionAddress(KMemoryState state) const;
+ std::size_t GetRegionSize(KMemoryState state) const;
+
+ VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages,
+ std::size_t alignment, std::size_t offset, std::size_t guard_pages);
+
+ Result CheckMemoryStateContiguous(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
+ Result CheckMemoryStateContiguous(VAddr addr, std::size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask,
+ KMemoryPermission perm, KMemoryAttribute attr_mask,
+ KMemoryAttribute attr) const {
return this->CheckMemoryStateContiguous(nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr);
}
- ResultCode CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr) const;
- ResultCode CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
- KMemoryAttribute* out_attr, std::size_t* out_blocks_needed,
- VAddr addr, std::size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr,
- KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
- ResultCode CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
- KMemoryState state_mask, KMemoryState state,
- KMemoryPermission perm_mask, KMemoryPermission perm,
- KMemoryAttribute attr_mask, KMemoryAttribute attr,
- KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
+ Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
+ Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
+ KMemoryAttribute* out_attr, std::size_t* out_blocks_needed, VAddr addr,
+ std::size_t size, KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
+ Result CheckMemoryState(std::size_t* out_blocks_needed, VAddr addr, std::size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
return CheckMemoryState(nullptr, nullptr, nullptr, out_blocks_needed, addr, size,
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr);
}
- ResultCode CheckMemoryState(VAddr addr, size_t size, KMemoryState state_mask,
- KMemoryState state, KMemoryPermission perm_mask,
- KMemoryPermission perm, KMemoryAttribute attr_mask,
- KMemoryAttribute attr,
- KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
+ Result CheckMemoryState(VAddr addr, std::size_t size, KMemoryState state_mask,
+ KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const {
return this->CheckMemoryState(nullptr, addr, size, state_mask, state, perm_mask, perm,
attr_mask, attr, ignore_attr);
}
+ Result LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr);
+ Result UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr,
+ KMemoryPermission new_perm, KMemoryAttribute lock_attr,
+ const KPageGroup* pg);
+
+ Result MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages);
+ bool IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages);
+
bool IsLockedByCurrentThread() const {
return general_lock.IsLockedByCurrentThread();
}
+ bool IsHeapPhysicalAddress(const KMemoryLayout& layout, PAddr phys_addr) {
+ ASSERT(this->IsLockedByCurrentThread());
+
+ return layout.IsHeapPhysicalAddress(cached_physical_heap_region, phys_addr);
+ }
+
+ bool GetPhysicalAddressLocked(PAddr* out, VAddr virt_addr) const {
+ ASSERT(this->IsLockedByCurrentThread());
+
+ *out = GetPhysicalAddr(virt_addr);
+
+ return *out != 0;
+ }
+
mutable KLightLock general_lock;
mutable KLightLock map_physical_memory_lock;
@@ -210,7 +251,7 @@ public:
constexpr VAddr GetAliasCodeRegionSize() const {
return alias_code_region_end - alias_code_region_start;
}
- size_t GetNormalMemorySize() {
+ std::size_t GetNormalMemorySize() {
KScopedLightLock lk(general_lock);
return GetHeapSize() + mapped_physical_memory_size;
}
@@ -253,9 +294,10 @@ public:
constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const {
return !IsOutsideASLRRegion(address, size);
}
-
- PAddr GetPhysicalAddr(VAddr addr) {
- ASSERT(IsLockedByCurrentThread());
+ constexpr std::size_t GetNumGuardPages() const {
+ return IsKernel() ? 1 : 4;
+ }
+ PAddr GetPhysicalAddr(VAddr addr) const {
const auto backing_addr = page_table_impl.backing_addr[addr >> PageBits];
ASSERT(backing_addr);
return backing_addr + addr;
@@ -276,10 +318,6 @@ private:
return is_aslr_enabled;
}
- constexpr std::size_t GetNumGuardPages() const {
- return IsKernel() ? 1 : 4;
- }
-
constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const {
return (address_space_start <= addr) &&
(num_pages <= (address_space_end - address_space_start) / PageSize) &&
@@ -311,6 +349,9 @@ private:
bool is_kernel{};
bool is_aslr_enabled{};
+ u32 heap_fill_value{};
+ const KMemoryRegion* cached_physical_heap_region{};
+
KMemoryManager::Pool memory_pool{KMemoryManager::Pool::Application};
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..7a5a9dc2a 100644
--- a/src/core/hle/kernel/k_port.cpp
+++ b/src/core/hle/kernel/k_port.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_port.h"
@@ -51,13 +50,18 @@ bool KPort::IsServerClosed() const {
return state == State::ServerClosed;
}
-ResultCode KPort::EnqueueSession(KServerSession* session) {
+Result KPort::EnqueueSession(KServerSession* session) {
KScopedSchedulerLock sl{kernel};
R_UNLESS(state == State::Normal, ResultPortClosed);
server.EnqueueSession(session);
- server.GetSessionRequestHandler()->ClientConnected(server.AcceptSession());
+
+ if (auto session_ptr = server.GetSessionRequestHandler().lock()) {
+ session_ptr->ClientConnected(server.AcceptSession());
+ } else {
+ ASSERT(false);
+ }
return ResultSuccess;
}
diff --git a/src/core/hle/kernel/k_port.h b/src/core/hle/kernel/k_port.h
index b6e4a1fcd..0cfc16dab 100644
--- a/src/core/hle/kernel/k_port.h
+++ b/src/core/hle/kernel/k_port.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -35,7 +34,7 @@ public:
bool IsServerClosed() const;
- ResultCode EnqueueSession(KServerSession* session);
+ Result EnqueueSession(KServerSession* session);
KClientPort& GetClientPort() {
return client;
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h
index bd779739d..cb2512b0b 100644
--- a/src/core/hle/kernel/k_priority_queue.h
+++ b/src/core/hle/kernel/k_priority_queue.h
@@ -1,9 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// This file references various implementation details from Atmosphere, an open-source firmware for
-// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index 85c506979..d3e99665f 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <bitset>
@@ -13,7 +12,6 @@
#include "common/scope_exit.h"
#include "common/settings.h"
#include "core/core.h"
-#include "core/device_memory.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/k_memory_block_manager.h"
@@ -24,7 +22,6 @@
#include "core/hle/kernel/k_scoped_resource_reservation.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "core/hle/kernel/k_shared_memory_info.h"
-#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
@@ -59,76 +56,22 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
thread->GetContext64().cpu_registers[0] = 0;
thread->GetContext32().cpu_registers[1] = thread_handle;
thread->GetContext64().cpu_registers[1] = thread_handle;
- thread->DisableDispatch();
- auto& kernel = system.Kernel();
- // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
- {
- KScopedSchedulerLock lock{kernel};
- thread->SetState(ThreadState::Runnable);
+ if (system.DebuggerEnabled()) {
+ thread->RequestSuspend(SuspendType::Debug);
}
+
+ // Run our thread.
+ void(thread->Run());
}
} // Anonymous namespace
-// Represents a page used for thread-local storage.
-//
-// Each TLS page contains slots that may be used by processes and threads.
-// Every process and thread is created with a slot in some arbitrary page
-// (whichever page happens to have an available slot).
-class TLSPage {
-public:
- static constexpr std::size_t num_slot_entries =
- Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE;
-
- explicit TLSPage(VAddr address) : base_address{address} {}
-
- bool HasAvailableSlots() const {
- return !is_slot_used.all();
- }
-
- VAddr GetBaseAddress() const {
- return base_address;
- }
-
- std::optional<VAddr> ReserveSlot() {
- for (std::size_t i = 0; i < is_slot_used.size(); i++) {
- if (is_slot_used[i]) {
- continue;
- }
-
- is_slot_used[i] = true;
- return base_address + (i * Core::Memory::TLS_ENTRY_SIZE);
- }
-
- return std::nullopt;
- }
-
- void ReleaseSlot(VAddr address) {
- // Ensure that all given addresses are consistent with how TLS pages
- // are intended to be used when releasing slots.
- ASSERT(IsWithinPage(address));
- ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0);
-
- const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE;
- is_slot_used[index] = false;
- }
-
-private:
- bool IsWithinPage(VAddr address) const {
- return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE;
- }
-
- VAddr base_address;
- std::bitset<num_slot_entries> is_slot_used;
-};
-
-ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
- ProcessType type) {
+Result KProcess::Initialize(KProcess* process, Core::System& system, std::string process_name,
+ ProcessType type, KResourceLimit* res_limit) {
auto& kernel = system.Kernel();
process->name = std::move(process_name);
-
- process->resource_limit = kernel.GetSystemResourceLimit();
+ process->resource_limit = res_limit;
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
@@ -143,9 +86,6 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
kernel.AppendNewProcess(process);
- // Open a reference to the resource limit.
- process->resource_limit->Open();
-
// Clear remaining fields.
process->num_running_threads = 0;
process->is_signaled = false;
@@ -153,6 +93,9 @@ ResultCode KProcess::Initialize(KProcess* process, Core::System& system, std::st
process->is_suspended = false;
process->schedule_count = 0;
+ // Open a reference to the resource limit.
+ process->resource_limit->Open();
+
return ResultSuccess;
}
@@ -217,7 +160,7 @@ bool KProcess::ReleaseUserException(KThread* thread) {
std::addressof(num_waiters),
reinterpret_cast<uintptr_t>(std::addressof(exception_thread)));
next != nullptr) {
- next->SetState(ThreadState::Runnable);
+ next->EndWait(ResultSuccess);
}
KScheduler::SetSchedulerUpdateNeeded(kernel);
@@ -232,7 +175,8 @@ void KProcess::PinCurrentThread(s32 core_id) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
- KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
+ KThread* cur_thread =
+ kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread();
// If the thread isn't terminated, pin it.
if (!cur_thread->IsTerminationRequested()) {
@@ -249,7 +193,8 @@ void KProcess::UnpinCurrentThread(s32 core_id) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Get the current thread.
- KThread* cur_thread = kernel.Scheduler(static_cast<std::size_t>(core_id)).GetCurrentThread();
+ KThread* cur_thread =
+ kernel.Scheduler(static_cast<std::size_t>(core_id)).GetSchedulerCurrentThread();
// Unpin it.
cur_thread->Unpin();
@@ -273,8 +218,8 @@ void KProcess::UnpinThread(KThread* thread) {
KScheduler::SetSchedulerUpdateNeeded(kernel);
}
-ResultCode KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
- [[maybe_unused]] size_t size) {
+Result KProcess::AddSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr address,
+ [[maybe_unused]] size_t size) {
// Lock ourselves, to prevent concurrent access.
KScopedLightLock lk(state_lock);
@@ -326,15 +271,19 @@ void KProcess::RemoveSharedMemory(KSharedMemory* shmem, [[maybe_unused]] VAddr a
shmem->Close();
}
-void KProcess::RegisterThread(const KThread* thread) {
+void KProcess::RegisterThread(KThread* thread) {
+ KScopedLightLock lk{list_lock};
+
thread_list.push_back(thread);
}
-void KProcess::UnregisterThread(const KThread* thread) {
+void KProcess::UnregisterThread(KThread* thread) {
+ KScopedLightLock lk{list_lock};
+
thread_list.remove(thread);
}
-ResultCode KProcess::Reset() {
+Result KProcess::Reset() {
// Lock the process and the scheduler.
KScopedLightLock lk(state_lock);
KScopedSchedulerLock sl{kernel};
@@ -348,8 +297,51 @@ ResultCode KProcess::Reset() {
return ResultSuccess;
}
-ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
- std::size_t code_size) {
+Result KProcess::SetActivity(ProcessActivity activity) {
+ // Lock ourselves and the scheduler.
+ KScopedLightLock lk{state_lock};
+ KScopedLightLock list_lk{list_lock};
+ KScopedSchedulerLock sl{kernel};
+
+ // Validate our state.
+ R_UNLESS(status != ProcessStatus::Exiting, ResultInvalidState);
+ R_UNLESS(status != ProcessStatus::Exited, ResultInvalidState);
+
+ // Either pause or resume.
+ if (activity == ProcessActivity::Paused) {
+ // Verify that we're not suspended.
+ if (is_suspended) {
+ return ResultInvalidState;
+ }
+
+ // Suspend all threads.
+ for (auto* thread : GetThreadList()) {
+ thread->RequestSuspend(SuspendType::Process);
+ }
+
+ // Set ourselves as suspended.
+ SetSuspended(true);
+ } else {
+ ASSERT(activity == ProcessActivity::Runnable);
+
+ // Verify that we're suspended.
+ if (!is_suspended) {
+ return ResultInvalidState;
+ }
+
+ // Resume all threads.
+ for (auto* thread : GetThreadList()) {
+ thread->Resume(SuspendType::Process);
+ }
+
+ // Set ourselves as resumed.
+ SetSuspended(false);
+ }
+
+ return ResultSuccess;
+}
+
+Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size) {
program_id = metadata.GetTitleID();
ideal_core = metadata.GetMainThreadCore();
is_64bit_process = metadata.Is64BitProgram();
@@ -364,24 +356,24 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
return ResultLimitReached;
}
// Initialize proces address space
- if (const ResultCode result{
- page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000,
- code_size, KMemoryManager::Pool::Application)};
+ if (const Result result{page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false,
+ 0x8000000, code_size,
+ KMemoryManager::Pool::Application)};
result.IsError()) {
return result;
}
// Map process code region
- if (const ResultCode result{page_table->MapProcessCode(page_table->GetCodeRegionStart(),
- code_size / PageSize, KMemoryState::Code,
- KMemoryPermission::None)};
+ if (const Result result{page_table->MapProcessCode(page_table->GetCodeRegionStart(),
+ code_size / PageSize, KMemoryState::Code,
+ KMemoryPermission::None)};
result.IsError()) {
return result;
}
// Initialize process capabilities
const auto& caps{metadata.GetKernelCapabilities()};
- if (const ResultCode result{
+ if (const Result result{
capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)};
result.IsError()) {
return result;
@@ -401,11 +393,11 @@ ResultCode KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
break;
default:
- UNREACHABLE();
+ ASSERT(false);
}
// Create TLS region
- tls_region_address = CreateTLSRegion();
+ R_TRY(this->CreateThreadLocalRegion(std::addressof(tls_region_address)));
memory_reservation.Commit();
return handle_table.Initialize(capabilities.GetHandleTableSize());
@@ -428,11 +420,11 @@ void KProcess::PrepareForTermination() {
ChangeStatus(ProcessStatus::Exiting);
const auto stop_threads = [this](const std::vector<KThread*>& in_thread_list) {
- for (auto& thread : in_thread_list) {
+ for (auto* thread : in_thread_list) {
if (thread->GetOwnerProcess() != this)
continue;
- if (thread == kernel.CurrentScheduler()->GetCurrentThread())
+ if (thread == GetCurrentThreadPointer(kernel))
continue;
// TODO(Subv): When are the other running/ready threads terminated?
@@ -445,7 +437,7 @@ void KProcess::PrepareForTermination() {
stop_threads(kernel.System().GlobalSchedulerContext().GetThreadList());
- FreeTLSRegion(tls_region_address);
+ this->DeleteThreadLocalRegion(tls_region_address);
tls_region_address = 0;
if (resource_limit) {
@@ -457,9 +449,6 @@ void KProcess::PrepareForTermination() {
}
void KProcess::Finalize() {
- // Finalize the handle table and close any open handles.
- handle_table.Finalize();
-
// Free all shared memory infos.
{
auto it = shared_memory_list.begin();
@@ -484,67 +473,156 @@ void KProcess::Finalize() {
resource_limit = nullptr;
}
+ // Finalize the page table.
+ page_table.reset();
+
// Perform inherited finalization.
KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask>::Finalize();
}
-/**
- * Attempts to find a TLS page that contains a free slot for
- * use by a thread.
- *
- * @returns If a page with an available slot is found, then an iterator
- * pointing to the page is returned. Otherwise the end iterator
- * is returned instead.
- */
-static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
- return std::find_if(tls_pages.begin(), tls_pages.end(),
- [](const auto& page) { return page.HasAvailableSlots(); });
+Result KProcess::CreateThreadLocalRegion(VAddr* out) {
+ KThreadLocalPage* tlp = nullptr;
+ VAddr tlr = 0;
+
+ // See if we can get a region from a partially used TLP.
+ {
+ KScopedSchedulerLock sl{kernel};
+
+ if (auto it = partially_used_tlp_tree.begin(); it != partially_used_tlp_tree.end()) {
+ tlr = it->Reserve();
+ ASSERT(tlr != 0);
+
+ if (it->IsAllUsed()) {
+ tlp = std::addressof(*it);
+ partially_used_tlp_tree.erase(it);
+ fully_used_tlp_tree.insert(*tlp);
+ }
+
+ *out = tlr;
+ return ResultSuccess;
+ }
+ }
+
+ // Allocate a new page.
+ tlp = KThreadLocalPage::Allocate(kernel);
+ R_UNLESS(tlp != nullptr, ResultOutOfMemory);
+ auto tlp_guard = SCOPE_GUARD({ KThreadLocalPage::Free(kernel, tlp); });
+
+ // Initialize the new page.
+ R_TRY(tlp->Initialize(kernel, this));
+
+ // Reserve a TLR.
+ tlr = tlp->Reserve();
+ ASSERT(tlr != 0);
+
+ // Insert into our tree.
+ {
+ KScopedSchedulerLock sl{kernel};
+ if (tlp->IsAllUsed()) {
+ fully_used_tlp_tree.insert(*tlp);
+ } else {
+ partially_used_tlp_tree.insert(*tlp);
+ }
+ }
+
+ // We succeeded!
+ tlp_guard.Cancel();
+ *out = tlr;
+ return ResultSuccess;
}
-VAddr KProcess::CreateTLSRegion() {
- KScopedSchedulerLock lock(kernel);
- if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)};
- tls_page_iter != tls_pages.cend()) {
- return *tls_page_iter->ReserveSlot();
+Result KProcess::DeleteThreadLocalRegion(VAddr addr) {
+ KThreadLocalPage* page_to_free = nullptr;
+
+ // Release the region.
+ {
+ KScopedSchedulerLock sl{kernel};
+
+ // Try to find the page in the partially used list.
+ auto it = partially_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
+ if (it == partially_used_tlp_tree.end()) {
+ // If we don't find it, it has to be in the fully used list.
+ it = fully_used_tlp_tree.find_key(Common::AlignDown(addr, PageSize));
+ R_UNLESS(it != fully_used_tlp_tree.end(), ResultInvalidAddress);
+
+ // Release the region.
+ it->Release(addr);
+
+ // Move the page out of the fully used list.
+ KThreadLocalPage* tlp = std::addressof(*it);
+ fully_used_tlp_tree.erase(it);
+ if (tlp->IsAllFree()) {
+ page_to_free = tlp;
+ } else {
+ partially_used_tlp_tree.insert(*tlp);
+ }
+ } else {
+ // Release the region.
+ it->Release(addr);
+
+ // Handle the all-free case.
+ KThreadLocalPage* tlp = std::addressof(*it);
+ if (tlp->IsAllFree()) {
+ partially_used_tlp_tree.erase(it);
+ page_to_free = tlp;
+ }
+ }
+ }
+
+ // If we should free the page it was in, do so.
+ if (page_to_free != nullptr) {
+ page_to_free->Finalize();
+
+ KThreadLocalPage::Free(kernel, page_to_free);
}
- Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()};
- ASSERT(tls_page_ptr);
+ return ResultSuccess;
+}
- const VAddr start{page_table->GetKernelMapRegionStart()};
- const VAddr size{page_table->GetKernelMapRegionEnd() - start};
- const PAddr tls_map_addr{kernel.System().DeviceMemory().GetPhysicalAddr(tls_page_ptr)};
- const VAddr tls_page_addr{page_table
- ->AllocateAndMapMemory(1, PageSize, true, start, size / PageSize,
- KMemoryState::ThreadLocal,
- KMemoryPermission::UserReadWrite,
- tls_map_addr)
- .ValueOr(0)};
+bool KProcess::InsertWatchpoint(Core::System& system, VAddr addr, u64 size,
+ DebugWatchpointType type) {
+ const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) {
+ return wp.type == DebugWatchpointType::None;
+ })};
- ASSERT(tls_page_addr);
+ if (watch == watchpoints.end()) {
+ return false;
+ }
- std::memset(tls_page_ptr, 0, PageSize);
- tls_pages.emplace_back(tls_page_addr);
+ watch->start_address = addr;
+ watch->end_address = addr + size;
+ watch->type = type;
- const auto reserve_result{tls_pages.back().ReserveSlot()};
- ASSERT(reserve_result.has_value());
+ for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) {
+ debug_page_refcounts[page]++;
+ system.Memory().MarkRegionDebug(page, PageSize, true);
+ }
- return *reserve_result;
+ return true;
}
-void KProcess::FreeTLSRegion(VAddr tls_address) {
- KScopedSchedulerLock lock(kernel);
- const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);
- auto iter =
- std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
- return page.GetBaseAddress() == aligned_address;
- });
+bool KProcess::RemoveWatchpoint(Core::System& system, VAddr addr, u64 size,
+ DebugWatchpointType type) {
+ const auto watch{std::find_if(watchpoints.begin(), watchpoints.end(), [&](const auto& wp) {
+ return wp.start_address == addr && wp.end_address == addr + size && wp.type == type;
+ })};
- // Something has gone very wrong if we're freeing a region
- // with no actual page available.
- ASSERT(iter != tls_pages.cend());
+ if (watch == watchpoints.end()) {
+ return false;
+ }
+
+ watch->start_address = 0;
+ watch->end_address = 0;
+ watch->type = DebugWatchpointType::None;
+
+ for (VAddr page = Common::AlignDown(addr, PageSize); page < addr + size; page += PageSize) {
+ debug_page_refcounts[page]--;
+ if (!debug_page_refcounts[page]) {
+ system.Memory().MarkRegionDebug(page, PageSize, false);
+ }
+ }
- iter->ReleaseSlot(tls_address);
+ return true;
}
void KProcess::LoadModule(CodeSet code_set, VAddr base_addr) {
@@ -567,9 +645,10 @@ bool KProcess::IsSignaled() const {
}
KProcess::KProcess(KernelCore& kernel_)
- : KAutoObjectWithSlabHeapAndContainer{kernel_},
- page_table{std::make_unique<KPageTable>(kernel_.System())}, handle_table{kernel_},
- address_arbiter{kernel_.System()}, condition_var{kernel_.System()}, state_lock{kernel_} {}
+ : KAutoObjectWithSlabHeapAndContainer{kernel_}, page_table{std::make_unique<KPageTable>(
+ kernel_.System())},
+ handle_table{kernel_}, address_arbiter{kernel_.System()}, condition_var{kernel_.System()},
+ state_lock{kernel_}, list_lock{kernel_} {}
KProcess::~KProcess() = default;
@@ -583,7 +662,7 @@ void KProcess::ChangeStatus(ProcessStatus new_status) {
NotifyAvailable();
}
-ResultCode KProcess::AllocateMainThreadStack(std::size_t stack_size) {
+Result KProcess::AllocateMainThreadStack(std::size_t stack_size) {
ASSERT(stack_size);
// The kernel always ensures that the given stack size is page aligned.
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 38b446350..d56d73bab 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -1,20 +1,20 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include <list>
+#include <map>
#include <string>
-#include <vector>
#include "common/common_types.h"
#include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_condition_variable.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_thread_local_page.h"
#include "core/hle/kernel/k_worker_task.h"
#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/slab_helpers.h"
@@ -63,6 +63,25 @@ enum class ProcessStatus {
DebugBreak,
};
+enum class ProcessActivity : u32 {
+ Runnable,
+ Paused,
+};
+
+enum class DebugWatchpointType : u8 {
+ None = 0,
+ Read = 1 << 0,
+ Write = 1 << 1,
+ ReadOrWrite = Read | Write,
+};
+DECLARE_ENUM_FLAG_OPERATORS(DebugWatchpointType);
+
+struct DebugWatchpoint {
+ VAddr start_address;
+ VAddr end_address;
+ DebugWatchpointType type;
+};
+
class KProcess final : public KAutoObjectWithSlabHeapAndContainer<KProcess, KWorkerTask> {
KERNEL_AUTOOBJECT_TRAITS(KProcess, KSynchronizationObject);
@@ -90,8 +109,8 @@ public:
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
- static ResultCode Initialize(KProcess* process, Core::System& system, std::string process_name,
- ProcessType type);
+ static Result Initialize(KProcess* process, Core::System& system, std::string process_name,
+ ProcessType type, KResourceLimit* res_limit);
/// Gets a reference to the process' page table.
KPageTable& PageTable() {
@@ -113,11 +132,11 @@ public:
return handle_table;
}
- ResultCode SignalToAddress(VAddr address) {
+ Result SignalToAddress(VAddr address) {
return condition_var.SignalToAddress(address);
}
- ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) {
+ Result WaitForAddress(Handle handle, VAddr address, u32 tag) {
return condition_var.WaitForAddress(handle, address, tag);
}
@@ -125,17 +144,16 @@ public:
return condition_var.Signal(cv_key, count);
}
- ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
+ Result WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) {
return condition_var.Wait(address, cv_key, tag, ns);
}
- ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value,
- s32 count) {
+ Result SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, s32 count) {
return address_arbiter.SignalToAddress(address, signal_type, value, count);
}
- ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
- s64 timeout) {
+ Result WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value,
+ s64 timeout) {
return address_arbiter.WaitForAddress(address, arb_type, value, timeout);
}
@@ -282,17 +300,17 @@ public:
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
/// Gets the list of all threads created with this process as their owner.
- const std::list<const KThread*>& GetThreadList() const {
+ std::list<KThread*>& GetThreadList() {
return thread_list;
}
/// Registers a thread as being created under this process,
/// adding it to this process' thread list.
- void RegisterThread(const KThread* thread);
+ void RegisterThread(KThread* thread);
/// Unregisters a thread from this process, removing it
/// from this process' thread list.
- void UnregisterThread(const KThread* thread);
+ void UnregisterThread(KThread* thread);
/// Clears the signaled state of the process if and only if it's signaled.
///
@@ -302,7 +320,7 @@ public:
/// @pre The process must be in a signaled state. If this is called on a
/// process instance that is not signaled, ERR_INVALID_STATE will be
/// returned.
- ResultCode Reset();
+ Result Reset();
/**
* Loads process-specifics configuration info with metadata provided
@@ -313,7 +331,7 @@ public:
* @returns ResultSuccess if all relevant metadata was able to be
* loaded and parsed. Otherwise, an error code is returned.
*/
- ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);
+ Result LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);
/**
* Starts the main application thread for this process.
@@ -347,6 +365,8 @@ public:
void DoWorkerTaskImpl();
+ Result SetActivity(ProcessActivity activity);
+
void PinCurrentThread(s32 core_id);
void UnpinCurrentThread(s32 core_id);
void UnpinThread(KThread* thread);
@@ -355,17 +375,30 @@ public:
return state_lock;
}
- ResultCode AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
+ Result AddSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
void RemoveSharedMemory(KSharedMemory* shmem, VAddr address, size_t size);
///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management
// Marks the next available region as used and returns the address of the slot.
- [[nodiscard]] VAddr CreateTLSRegion();
+ [[nodiscard]] Result CreateThreadLocalRegion(VAddr* out);
// Frees a used TLS slot identified by the given address
- void FreeTLSRegion(VAddr tls_address);
+ Result DeleteThreadLocalRegion(VAddr addr);
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Debug watchpoint management
+
+ // Attempts to insert a watchpoint into a free slot. Returns false if none are available.
+ bool InsertWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type);
+
+ // Attempts to remove the watchpoint specified by the given parameters.
+ bool RemoveWatchpoint(Core::System& system, VAddr addr, u64 size, DebugWatchpointType type);
+
+ const std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>& GetWatchpoints() const {
+ return watchpoints;
+ }
private:
void PinThread(s32 core_id, KThread* thread) {
@@ -388,7 +421,7 @@ private:
void ChangeStatus(ProcessStatus new_status);
/// Allocates the main thread stack for the process, given the stack size in bytes.
- ResultCode AllocateMainThreadStack(std::size_t stack_size);
+ Result AllocateMainThreadStack(std::size_t stack_size);
/// Memory manager for this process
std::unique_ptr<KPageTable> page_table;
@@ -413,13 +446,6 @@ private:
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
- /// The Thread Local Storage area is allocated as processes create threads,
- /// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
- /// holds the TLS for a specific thread. This vector contains which parts are in use for each
- /// page as a bitmask.
- /// This vector will grow as more pages are allocated for new threads.
- std::vector<TLSPage> tls_pages;
-
/// Contains the parsed process capability descriptors.
ProcessCapabilities capabilities;
@@ -429,7 +455,7 @@ private:
bool is_64bit_process = true;
/// Total running time for the process in ticks.
- u64 total_process_running_time_ticks = 0;
+ std::atomic<u64> total_process_running_time_ticks = 0;
/// Per-process handle table for storing created object handles in.
KHandleTable handle_table;
@@ -449,7 +475,7 @@ private:
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
/// List of threads that are running with this process as their owner.
- std::list<const KThread*> thread_list;
+ std::list<KThread*> thread_list;
/// List of shared memory that are running with this process as their owner.
std::list<KSharedMemoryInfo*> shared_memory_list;
@@ -478,10 +504,19 @@ private:
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{};
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{};
+ std::array<DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS> watchpoints{};
+ std::map<VAddr, u64> debug_page_refcounts;
KThread* exception_thread{};
KLightLock state_lock;
+ KLightLock list_lock;
+
+ using TLPTree =
+ Common::IntrusiveRedBlackTreeBaseTraits<KThreadLocalPage>::TreeType<KThreadLocalPage>;
+ using TLPIterator = TLPTree::iterator;
+ TLPTree fully_used_tlp_tree;
+ TLPTree partially_used_tlp_tree;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_readable_event.cpp b/src/core/hle/kernel/k_readable_event.cpp
index bf1db10d4..94c5464fe 100644
--- a/src/core/hle/kernel/k_readable_event.cpp
+++ b/src/core/hle/kernel/k_readable_event.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/hle/kernel/k_event.h"
@@ -28,7 +27,7 @@ void KReadableEvent::Destroy() {
}
}
-ResultCode KReadableEvent::Signal() {
+Result KReadableEvent::Signal() {
KScopedSchedulerLock lk{kernel};
if (!is_signaled) {
@@ -39,13 +38,13 @@ ResultCode KReadableEvent::Signal() {
return ResultSuccess;
}
-ResultCode KReadableEvent::Clear() {
+Result KReadableEvent::Clear() {
Reset();
return ResultSuccess;
}
-ResultCode KReadableEvent::Reset() {
+Result KReadableEvent::Reset() {
KScopedSchedulerLock lk{kernel};
if (!is_signaled) {
diff --git a/src/core/hle/kernel/k_readable_event.h b/src/core/hle/kernel/k_readable_event.h
index 149fa78dd..18dcad289 100644
--- a/src/core/hle/kernel/k_readable_event.h
+++ b/src/core/hle/kernel/k_readable_event.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -34,9 +33,9 @@ public:
bool IsSignaled() const override;
void Destroy() override;
- ResultCode Signal();
- ResultCode Clear();
- ResultCode Reset();
+ Result Signal();
+ Result Clear();
+ Result Reset();
private:
bool is_signaled{};
diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp
index 0c4bba66b..010dcf99e 100644
--- a/src/core/hle/kernel/k_resource_limit.cpp
+++ b/src/core/hle/kernel/k_resource_limit.cpp
@@ -1,8 +1,8 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
+#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/svc_results.h"
@@ -73,7 +73,7 @@ s64 KResourceLimit::GetFreeValue(LimitableResource which) const {
return value;
}
-ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
+Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
const auto index = static_cast<std::size_t>(which);
KScopedLightLock lk(lock);
R_UNLESS(current_values[index] <= value, ResultInvalidState);
@@ -151,4 +151,22 @@ void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) {
}
}
+KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
+ auto* resource_limit = KResourceLimit::Create(system.Kernel());
+ resource_limit->Initialize(&system.CoreTiming());
+
+ // Initialize default resource limit values.
+ // TODO(bunnei): These values are the system defaults, the limits for service processes are
+ // lower. These should use the correct limit values.
+
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size)
+ .IsSuccess());
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess());
+ ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
+
+ return resource_limit;
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h
index fab6005ff..65c98c979 100644
--- a/src/core/hle/kernel/k_resource_limit.h
+++ b/src/core/hle/kernel/k_resource_limit.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -9,7 +8,7 @@
#include "core/hle/kernel/k_light_condition_variable.h"
#include "core/hle/kernel/k_light_lock.h"
-union ResultCode;
+union Result;
namespace Core::Timing {
class CoreTiming;
@@ -47,7 +46,7 @@ public:
s64 GetPeakValue(LimitableResource which) const;
s64 GetFreeValue(LimitableResource which) const;
- ResultCode SetLimitValue(LimitableResource which, s64 value);
+ Result SetLimitValue(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value);
bool Reserve(LimitableResource which, s64 value, s64 timeout);
@@ -67,4 +66,7 @@ private:
KLightConditionVariable cond_var;
const Core::Timing::CoreTiming* core_timing{};
};
+
+KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size);
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp
index c96520828..c34ce7a17 100644
--- a/src/core/hle/kernel/k_scheduler.cpp
+++ b/src/core/hle/kernel/k_scheduler.cpp
@@ -1,9 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// This file references various implementation details from Atmosphere, an open-source firmware for
-// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <bit>
@@ -22,7 +18,6 @@
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_core.h"
-#include "core/hle/kernel/time_manager.h"
namespace Kernel {
@@ -32,69 +27,185 @@ static void IncrementScheduledCount(Kernel::KThread* thread) {
}
}
-void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
- auto scheduler = kernel.CurrentScheduler();
+KScheduler::KScheduler(KernelCore& kernel_) : kernel{kernel_} {
+ m_switch_fiber = std::make_shared<Common::Fiber>([this] {
+ while (true) {
+ ScheduleImplFiber();
+ }
+ });
+
+ m_state.needs_scheduling = true;
+}
+
+KScheduler::~KScheduler() = default;
- u32 current_core{0xF};
- bool must_context_switch{};
- if (scheduler) {
- current_core = scheduler->core_id;
- // TODO(bunnei): Should be set to true when we deprecate single core
- must_context_switch = !kernel.IsPhantomModeForSingleCore();
+void KScheduler::SetInterruptTaskRunnable() {
+ m_state.interrupt_task_runnable = true;
+ m_state.needs_scheduling = true;
+}
+
+void KScheduler::RequestScheduleOnInterrupt() {
+ m_state.needs_scheduling = true;
+
+ if (CanSchedule(kernel)) {
+ ScheduleOnInterrupt();
}
+}
- while (cores_pending_reschedule != 0) {
- const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
- ASSERT(core < Core::Hardware::NUM_CPU_CORES);
- if (!must_context_switch || core != current_core) {
- auto& phys_core = kernel.PhysicalCore(core);
- phys_core.Interrupt();
- }
- cores_pending_reschedule &= ~(1ULL << core);
+void KScheduler::DisableScheduling(KernelCore& kernel) {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
+ GetCurrentThread(kernel).DisableDispatch();
+}
+
+void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 1);
+
+ auto* scheduler{kernel.CurrentScheduler()};
+
+ if (!scheduler || kernel.IsPhantomModeForSingleCore()) {
+ KScheduler::RescheduleCores(kernel, cores_needing_scheduling);
+ KScheduler::RescheduleCurrentHLEThread(kernel);
+ return;
}
- for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
- if (kernel.PhysicalCore(core_id).IsInterrupted()) {
- KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
- }
+ scheduler->RescheduleOtherCores(cores_needing_scheduling);
+
+ if (GetCurrentThread(kernel).GetDisableDispatchCount() > 1) {
+ GetCurrentThread(kernel).EnableDispatch();
+ } else {
+ scheduler->RescheduleCurrentCore();
}
+}
+
+void KScheduler::RescheduleCurrentHLEThread(KernelCore& kernel) {
+ // HACK: we cannot schedule from this thread, it is not a core thread
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ // Special case to ensure dummy threads that are waiting block
+ GetCurrentThread(kernel).IfDummyThreadTryWait();
- if (must_context_switch) {
- auto core_scheduler = kernel.CurrentScheduler();
- kernel.ExitSVCProfile();
- core_scheduler->RescheduleCurrentCore();
- kernel.EnterSVCProfile();
+ ASSERT(GetCurrentThread(kernel).GetState() != ThreadState::Waiting);
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
+ if (IsSchedulerUpdateNeeded(kernel)) {
+ return UpdateHighestPriorityThreadsImpl(kernel);
+ } else {
+ return 0;
}
}
+void KScheduler::Schedule() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+ ASSERT(m_core_id == GetCurrentCoreId(kernel));
+
+ ScheduleImpl();
+}
+
+void KScheduler::ScheduleOnInterrupt() {
+ GetCurrentThread(kernel).DisableDispatch();
+ Schedule();
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+void KScheduler::PreemptSingleCore() {
+ GetCurrentThread(kernel).DisableDispatch();
+
+ auto* thread = GetCurrentThreadPointer(kernel);
+ auto& previous_scheduler = kernel.Scheduler(thread->GetCurrentCore());
+ previous_scheduler.Unload(thread);
+
+ Common::Fiber::YieldTo(thread->GetHostContext(), *m_switch_fiber);
+
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
+void KScheduler::RescheduleCurrentCore() {
+ ASSERT(!kernel.IsPhantomModeForSingleCore());
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ GetCurrentThread(kernel).EnableDispatch();
+
+ if (m_state.needs_scheduling.load()) {
+ // Disable interrupts, and then check again if rescheduling is needed.
+ // KScopedInterruptDisable intr_disable;
+
+ kernel.CurrentScheduler()->RescheduleCurrentCoreImpl();
+ }
+}
+
+void KScheduler::RescheduleCurrentCoreImpl() {
+ // Check that scheduling is needed.
+ if (m_state.needs_scheduling.load()) [[likely]] {
+ GetCurrentThread(kernel).DisableDispatch();
+ Schedule();
+ GetCurrentThread(kernel).EnableDispatch();
+ }
+}
+
+void KScheduler::Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id) {
+ // Set core ID/idle thread/interrupt task manager.
+ m_core_id = core_id;
+ m_idle_thread = idle_thread;
+ // m_state.idle_thread_stack = m_idle_thread->GetStackTop();
+ // m_state.interrupt_task_manager = &kernel.GetInterruptTaskManager();
+
+ // Insert the main thread into the priority queue.
+ // {
+ // KScopedSchedulerLock lk{kernel};
+ // GetPriorityQueue(kernel).PushBack(GetCurrentThreadPointer(kernel));
+ // SetSchedulerUpdateNeeded(kernel);
+ // }
+
+ // Bind interrupt handler.
+ // kernel.GetInterruptManager().BindHandler(
+ // GetSchedulerInterruptHandler(kernel), KInterruptName::Scheduler, m_core_id,
+ // KInterruptController::PriorityLevel::Scheduler, false, false);
+
+ // Set the current thread.
+ m_current_thread = main_thread;
+}
+
+void KScheduler::Activate() {
+ ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() == 1);
+
+ // m_state.should_count_idle = KTargetSystem::IsDebugMode();
+ m_is_active = true;
+ RescheduleCurrentCore();
+}
+
+void KScheduler::OnThreadStart() {
+ GetCurrentThread(kernel).EnableDispatch();
+}
+
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
- KScopedSpinLock lk{guard};
- if (KThread* prev_highest_thread = state.highest_priority_thread;
- prev_highest_thread != highest_thread) {
- if (prev_highest_thread != nullptr) {
+ if (KThread* prev_highest_thread = m_state.highest_priority_thread;
+ prev_highest_thread != highest_thread) [[likely]] {
+ if (prev_highest_thread != nullptr) [[likely]] {
IncrementScheduledCount(prev_highest_thread);
- prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
+ prev_highest_thread->SetLastScheduledTick(kernel.System().CoreTiming().GetCPUTicks());
}
- if (state.should_count_idle) {
- if (highest_thread != nullptr) {
+ if (m_state.should_count_idle) {
+ if (highest_thread != nullptr) [[likely]] {
if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
- process->SetRunningThread(core_id, highest_thread, state.idle_count);
+ process->SetRunningThread(m_core_id, highest_thread, m_state.idle_count);
}
} else {
- state.idle_count++;
+ m_state.idle_count++;
}
}
- state.highest_priority_thread = highest_thread;
- state.needs_scheduling.store(true);
- return (1ULL << core_id);
+ m_state.highest_priority_thread = highest_thread;
+ m_state.needs_scheduling = true;
+ return (1ULL << m_core_id);
} else {
return 0;
}
}
u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Clear that we need to update.
ClearSchedulerUpdateNeeded(kernel);
@@ -103,18 +214,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
auto& priority_queue = GetPriorityQueue(kernel);
- /// We want to go over all cores, finding the highest priority thread and determining if
- /// scheduling is needed for that core.
+ // We want to go over all cores, finding the highest priority thread and determining if
+ // scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
if (top_thread != nullptr) {
- // If the thread has no waiters, we need to check if the process has a thread pinned.
- if (top_thread->GetNumKernelWaiters() == 0) {
- if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
- if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
- pinned != nullptr && pinned != top_thread) {
- // We prefer our parent's pinned thread if possible. However, we also don't
- // want to schedule un-runnable threads.
+ // We need to check if the thread's process has a pinned thread.
+ if (KProcess* parent = top_thread->GetOwnerProcess()) {
+ // Check that there's a pinned thread other than the current top thread.
+ if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
+ pinned != nullptr && pinned != top_thread) {
+ // We need to prefer threads with kernel waiters to the pinned thread.
+ if (top_thread->GetNumKernelWaiters() ==
+ 0 /* && top_thread != parent->GetExceptionThread() */) {
+ // If the pinned thread is runnable, use it.
if (pinned->GetRawState() == ThreadState::Runnable) {
top_thread = pinned;
} else {
@@ -134,7 +247,8 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) {
- const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
+ const s32 core_id = static_cast<s32>(std::countr_zero(idle_cores));
+
if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0;
@@ -155,7 +269,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// The suggested thread isn't bound to its core, so we can migrate it!
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested);
-
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]);
@@ -188,7 +301,6 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
// Perform the migration.
suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(candidate_core, suggested);
-
top_threads[core_id] = suggested;
cores_needing_scheduling |=
kernel.Scheduler(core_id).UpdateHighestPriorityThread(
@@ -205,24 +317,210 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling;
}
+void KScheduler::SwitchThread(KThread* next_thread) {
+ KProcess* const cur_process = kernel.CurrentProcess();
+ KThread* const cur_thread = GetCurrentThreadPointer(kernel);
+
+ // We never want to schedule a null thread, so use the idle thread if we don't have a next.
+ if (next_thread == nullptr) {
+ next_thread = m_idle_thread;
+ }
+
+ if (next_thread->GetCurrentCore() != m_core_id) {
+ next_thread->SetCurrentCore(m_core_id);
+ }
+
+ // If we're not actually switching thread, there's nothing to do.
+ if (next_thread == cur_thread) {
+ return;
+ }
+
+ // Next thread is now known not to be nullptr, and must not be dispatchable.
+ ASSERT(next_thread->GetDisableDispatchCount() == 1);
+ ASSERT(!next_thread->IsDummyThread());
+
+ // Update the CPU time tracking variables.
+ const s64 prev_tick = m_last_context_switch_time;
+ const s64 cur_tick = kernel.System().CoreTiming().GetCPUTicks();
+ const s64 tick_diff = cur_tick - prev_tick;
+ cur_thread->AddCpuTime(m_core_id, tick_diff);
+ if (cur_process != nullptr) {
+ cur_process->UpdateCPUTimeTicks(tick_diff);
+ }
+ m_last_context_switch_time = cur_tick;
+
+ // Update our previous thread.
+ if (cur_process != nullptr) {
+ if (!cur_thread->IsTerminationRequested() && cur_thread->GetActiveCore() == m_core_id)
+ [[likely]] {
+ m_state.prev_thread = cur_thread;
+ } else {
+ m_state.prev_thread = nullptr;
+ }
+ }
+
+ // Switch the current process, if we're switching processes.
+ // if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) {
+ // KProcess::Switch(cur_process, next_process);
+ // }
+
+ // Set the new thread.
+ SetCurrentThread(kernel, next_thread);
+ m_current_thread = next_thread;
+
+ // Set the new Thread Local region.
+ // cpu::SwitchThreadLocalRegion(GetInteger(next_thread->GetThreadLocalRegionAddress()));
+}
+
+void KScheduler::ScheduleImpl() {
+ // First, clear the needs scheduling bool.
+ m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+
+ // Load the appropriate thread pointers for scheduling.
+ KThread* const cur_thread{GetCurrentThreadPointer(kernel)};
+ KThread* highest_priority_thread{m_state.highest_priority_thread};
+
+ // Check whether there are runnable interrupt tasks.
+ if (m_state.interrupt_task_runnable) {
+ // The interrupt task is runnable.
+ // We want to switch to the interrupt task/idle thread.
+ highest_priority_thread = nullptr;
+ }
+
+ // If there aren't, we want to check if the highest priority thread is the same as the current
+ // thread.
+ if (highest_priority_thread == cur_thread) {
+ // If they're the same, then we can just return.
+ return;
+ }
+
+ // The highest priority thread is not the same as the current thread.
+ // Jump to the switcher and continue executing from there.
+ m_switch_cur_thread = cur_thread;
+ m_switch_highest_priority_thread = highest_priority_thread;
+ m_switch_from_schedule = true;
+ Common::Fiber::YieldTo(cur_thread->host_context, *m_switch_fiber);
+
+ // Returning from ScheduleImpl occurs after this thread has been scheduled again.
+}
+
+void KScheduler::ScheduleImplFiber() {
+ KThread* const cur_thread{m_switch_cur_thread};
+ KThread* highest_priority_thread{m_switch_highest_priority_thread};
+
+ // If we're not coming from scheduling (i.e., we came from SC preemption),
+ // we should restart the scheduling loop directly. Not accurate to HOS.
+ if (!m_switch_from_schedule) {
+ goto retry;
+ }
+
+ // Mark that we are not coming from scheduling anymore.
+ m_switch_from_schedule = false;
+
+ // Save the original thread context.
+ Unload(cur_thread);
+
+ // The current thread's context has been entirely taken care of.
+ // Now we want to loop until we successfully switch the thread context.
+ while (true) {
+ // We're starting to try to do the context switch.
+ // Check if the highest priority thread is null.
+ if (!highest_priority_thread) {
+ // The next thread is nullptr!
+
+ // Switch to the idle thread. Note: HOS treats idling as a special case for
+ // performance. This is not *required* for yuzu's purposes, and for singlecore
+ // compatibility, we can just move the logic that would go here into the execution
+ // of the idle thread. If we ever remove singlecore, we should implement this
+ // accurately to HOS.
+ highest_priority_thread = m_idle_thread;
+ }
+
+ // We want to try to lock the highest priority thread's context.
+ // Try to take it.
+ while (!highest_priority_thread->context_guard.try_lock()) {
+ // The highest priority thread's context is already locked.
+ // Check if we need scheduling. If we don't, we can retry directly.
+ if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
+ // If we do, another core is interfering, and we must start again.
+ goto retry;
+ }
+ }
+
+ // It's time to switch the thread.
+ // Switch to the highest priority thread.
+ SwitchThread(highest_priority_thread);
+
+ // Check if we need scheduling. If we do, then we can't complete the switch and should
+ // retry.
+ if (m_state.needs_scheduling.load(std::memory_order_seq_cst)) {
+ // Our switch failed.
+ // We should unlock the thread context, and then retry.
+ highest_priority_thread->context_guard.unlock();
+ goto retry;
+ } else {
+ break;
+ }
+
+ retry:
+
+ // We failed to successfully do the context switch, and need to retry.
+ // Clear needs_scheduling.
+ m_state.needs_scheduling.store(false, std::memory_order_seq_cst);
+
+ // Refresh the highest priority thread.
+ highest_priority_thread = m_state.highest_priority_thread;
+ }
+
+ // Reload the guest thread context.
+ Reload(highest_priority_thread);
+
+ // Reload the host thread.
+ Common::Fiber::YieldTo(m_switch_fiber, *highest_priority_thread->host_context);
+}
+
+void KScheduler::Unload(KThread* thread) {
+ auto& cpu_core = kernel.System().ArmInterface(m_core_id);
+ cpu_core.SaveContext(thread->GetContext32());
+ cpu_core.SaveContext(thread->GetContext64());
+ // Save the TPIDR_EL0 system register in case it was modified.
+ thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
+ cpu_core.ClearExclusiveState();
+
+ // Check if the thread is terminated by checking the DPC flags.
+ if ((thread->GetStackParameters().dpc_flags & static_cast<u32>(DpcFlag::Terminated)) == 0) {
+ // The thread isn't terminated, so we want to unlock it.
+ thread->context_guard.unlock();
+ }
+}
+
+void KScheduler::Reload(KThread* thread) {
+ auto& cpu_core = kernel.System().ArmInterface(m_core_id);
+ cpu_core.LoadContext(thread->GetContext32());
+ cpu_core.LoadContext(thread->GetContext64());
+ cpu_core.SetTlsAddress(thread->GetTLSAddress());
+ cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
+ cpu_core.LoadWatchpointArray(thread->GetOwnerProcess()->GetWatchpoints());
+ cpu_core.ClearExclusiveState();
+}
+
void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
// Get an atomic reference to the core scheduler's previous thread.
- std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
- static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
+ auto& prev_thread{kernel.Scheduler(i).m_state.prev_thread};
// Atomically clear the previous thread if it's our target.
KThread* compare = thread;
- prev_thread.compare_exchange_strong(compare, nullptr);
+ prev_thread.compare_exchange_strong(compare, nullptr, std::memory_order_seq_cst);
}
}
void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Check if the state has changed, because if it hasn't there's nothing to do.
- const auto cur_state = thread->GetRawState();
+ const ThreadState cur_state = thread->GetRawState();
if (cur_state == old_state) {
return;
}
@@ -242,12 +540,12 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, Threa
}
void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// If the thread is runnable, we want to change its priority in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
GetPriorityQueue(kernel).ChangePriority(old_priority,
- thread == kernel.GetCurrentEmuThread(), thread);
+ thread == GetCurrentThreadPointer(kernel), thread);
IncrementScheduledCount(thread);
SetSchedulerUpdateNeeded(kernel);
}
@@ -255,7 +553,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s3
void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core) {
- ASSERT(kernel.GlobalSchedulerContext().IsLocked());
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// If the thread is runnable, we want to change its affinity in the queue.
if (thread->GetRawState() == ThreadState::Runnable) {
@@ -265,15 +563,14 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread
}
}
-void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
- ASSERT(system.GlobalSchedulerContext().IsLocked());
+void KScheduler::RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority) {
+ ASSERT(IsSchedulerLockedByCurrentThread(kernel));
// Get a reference to the priority queue.
- auto& kernel = system.Kernel();
auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end.
- KThread* top_thread = priority_queue.GetScheduledFront(cpu_core_id, priority);
+ KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
KThread* next_thread = nullptr;
if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread);
@@ -285,7 +582,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
// While we have a suggested thread, try to migrate it!
{
- KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id, priority);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority);
while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore();
@@ -306,7 +603,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
// to the front of the queue.
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) {
- suggested->SetActiveCore(cpu_core_id);
+ suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -314,22 +611,21 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
// Get the next suggestion.
- suggested = priority_queue.GetSamePriorityNext(cpu_core_id, suggested);
+ suggested = priority_queue.GetSamePriorityNext(core_id, suggested);
}
}
// Now that we might have migrated a thread with the same priority, check if we can do better.
-
{
- KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
- if (best_thread == GetCurrentThread()) {
- best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
+ KThread* best_thread = priority_queue.GetScheduledFront(core_id);
+ if (best_thread == GetCurrentThreadPointer(kernel)) {
+ best_thread = priority_queue.GetScheduledNext(core_id, best_thread);
}
// If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread.
if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
- KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id);
+ KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) {
@@ -348,7 +644,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
if (top_on_suggested_core == nullptr ||
top_on_suggested_core->GetPriority() >=
HighestCoreMigrationAllowedPriority) {
- suggested->SetActiveCore(cpu_core_id);
+ suggested->SetActiveCore(core_id);
priority_queue.ChangeCore(suggested_core, suggested, true);
IncrementScheduledCount(suggested);
break;
@@ -356,7 +652,7 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
}
// Get the next suggestion.
- suggested = priority_queue.GetSuggestedNext(cpu_core_id, suggested);
+ suggested = priority_queue.GetSuggestedNext(core_id, suggested);
}
}
}
@@ -365,71 +661,13 @@ void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
SetSchedulerUpdateNeeded(kernel);
}
-bool KScheduler::CanSchedule(KernelCore& kernel) {
- return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
-}
-
-bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
- return kernel.GlobalSchedulerContext().scheduler_update_needed.load(std::memory_order_acquire);
-}
-
-void KScheduler::SetSchedulerUpdateNeeded(KernelCore& kernel) {
- kernel.GlobalSchedulerContext().scheduler_update_needed.store(true, std::memory_order_release);
-}
-
-void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
- kernel.GlobalSchedulerContext().scheduler_update_needed.store(false, std::memory_order_release);
-}
-
-void KScheduler::DisableScheduling(KernelCore& kernel) {
- // If we are shutting down the kernel, none of this is relevant anymore.
- if (kernel.IsShuttingDown()) {
- return;
- }
-
- ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
- GetCurrentThreadPointer(kernel)->DisableDispatch();
-}
-
-void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
- // If we are shutting down the kernel, none of this is relevant anymore.
- if (kernel.IsShuttingDown()) {
- return;
- }
-
- auto* current_thread = GetCurrentThreadPointer(kernel);
-
- ASSERT(current_thread->GetDisableDispatchCount() >= 1);
-
- if (current_thread->GetDisableDispatchCount() > 1) {
- current_thread->EnableDispatch();
- } else {
- RescheduleCores(kernel, cores_needing_scheduling);
- }
-
- // Special case to ensure dummy threads that are waiting block.
- current_thread->IfDummyThreadTryWait();
-}
-
-u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
- if (IsSchedulerUpdateNeeded(kernel)) {
- return UpdateHighestPriorityThreadsImpl(kernel);
- } else {
- return 0;
- }
-}
-
-KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
- return kernel.GlobalSchedulerContext().priority_queue;
-}
-
void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Validate preconditions.
ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- KThread& cur_thread = Kernel::GetCurrentThread(kernel);
+ KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -442,7 +680,7 @@ void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -468,7 +706,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- KThread& cur_thread = Kernel::GetCurrentThread(kernel);
+ KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -481,7 +719,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -501,7 +739,7 @@ void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
if (KThread* running_on_suggested_core =
(suggested_core >= 0)
- ? kernel.Scheduler(suggested_core).state.highest_priority_thread
+ ? kernel.Scheduler(suggested_core).m_state.highest_priority_thread
: nullptr;
running_on_suggested_core != suggested) {
// If the current thread's priority is higher than our suggestion's we prefer
@@ -556,7 +794,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process.
- KThread& cur_thread = Kernel::GetCurrentThread(kernel);
+ KThread& cur_thread = GetCurrentThread(kernel);
KProcess& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do.
@@ -569,7 +807,7 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
// Perform the yield.
{
- KScopedSchedulerLock lock(kernel);
+ KScopedSchedulerLock sl{kernel};
const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) {
@@ -626,219 +864,19 @@ void KScheduler::YieldToAnyThread(KernelCore& kernel) {
}
}
-KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
- switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
- state.needs_scheduling.store(true);
- state.interrupt_task_thread_runnable = false;
- state.should_count_idle = false;
- state.idle_count = 0;
- state.idle_thread_stack = nullptr;
- state.highest_priority_thread = nullptr;
-}
-
-void KScheduler::Finalize() {
- if (idle_thread) {
- idle_thread->Close();
- idle_thread = nullptr;
- }
-}
-
-KScheduler::~KScheduler() {
- ASSERT(!idle_thread);
-}
-
-KThread* KScheduler::GetCurrentThread() const {
- if (auto result = current_thread.load(); result) {
- return result;
- }
- return idle_thread;
-}
-
-u64 KScheduler::GetLastContextSwitchTicks() const {
- return last_context_switch_time;
-}
-
-void KScheduler::RescheduleCurrentCore() {
- ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
-
- auto& phys_core = system.Kernel().PhysicalCore(core_id);
- if (phys_core.IsInterrupted()) {
- phys_core.ClearInterrupt();
+void KScheduler::RescheduleOtherCores(u64 cores_needing_scheduling) {
+ if (const u64 core_mask = cores_needing_scheduling & ~(1ULL << m_core_id); core_mask != 0) {
+ RescheduleCores(kernel, core_mask);
}
-
- guard.Lock();
- if (state.needs_scheduling.load()) {
- Schedule();
- } else {
- GetCurrentThread()->EnableDispatch();
- guard.Unlock();
- }
-}
-
-void KScheduler::OnThreadStart() {
- SwitchContextStep2();
}
-void KScheduler::Unload(KThread* thread) {
- ASSERT(thread);
-
- LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
-
- if (thread->IsCallingSvc()) {
- thread->ClearIsCallingSvc();
- }
-
- auto& physical_core = system.Kernel().PhysicalCore(core_id);
- if (!physical_core.IsInitialized()) {
- return;
- }
-
- Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
- cpu_core.SaveContext(thread->GetContext32());
- cpu_core.SaveContext(thread->GetContext64());
- // Save the TPIDR_EL0 system register in case it was modified.
- thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
-
- if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
- prev_thread = thread;
- } else {
- prev_thread = nullptr;
- }
-
- thread->context_guard.Unlock();
-}
-
-void KScheduler::Reload(KThread* thread) {
- LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread->GetName());
-
- Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
- cpu_core.LoadContext(thread->GetContext32());
- cpu_core.LoadContext(thread->GetContext64());
- cpu_core.SetTlsAddress(thread->GetTLSAddress());
- cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
-}
-
-void KScheduler::SwitchContextStep2() {
- // Load context of new thread
- Reload(GetCurrentThread());
-
- RescheduleCurrentCore();
-}
-
-void KScheduler::ScheduleImpl() {
- KThread* previous_thread = GetCurrentThread();
- KThread* next_thread = state.highest_priority_thread;
-
- state.needs_scheduling.store(false);
-
- // We never want to schedule a null thread, so use the idle thread if we don't have a next.
- if (next_thread == nullptr) {
- next_thread = idle_thread;
- }
-
- if (next_thread->GetCurrentCore() != core_id) {
- next_thread->SetCurrentCore(core_id);
- }
-
- // We never want to schedule a dummy thread, as these are only used by host threads for locking.
- if (next_thread->GetThreadType() == ThreadType::Dummy) {
- ASSERT_MSG(false, "Dummy threads should never be scheduled!");
- next_thread = idle_thread;
- }
-
- // If we're not actually switching thread, there's nothing to do.
- if (next_thread == current_thread.load()) {
- previous_thread->EnableDispatch();
- guard.Unlock();
- return;
- }
-
- // Update the CPU time tracking variables.
- KProcess* const previous_process = system.Kernel().CurrentProcess();
- UpdateLastContextSwitchTime(previous_thread, previous_process);
-
- // Save context for previous thread
- Unload(previous_thread);
-
- std::shared_ptr<Common::Fiber>* old_context;
- old_context = &previous_thread->GetHostContext();
-
- // Set the new thread.
- current_thread.store(next_thread);
-
- guard.Unlock();
-
- Common::Fiber::YieldTo(*old_context, *switch_fiber);
- /// When a thread wakes up, the scheduler may have changed to other in another core.
- auto& next_scheduler = *system.Kernel().CurrentScheduler();
- next_scheduler.SwitchContextStep2();
-}
-
-void KScheduler::OnSwitch(void* this_scheduler) {
- KScheduler* sched = static_cast<KScheduler*>(this_scheduler);
- sched->SwitchToCurrent();
-}
-
-void KScheduler::SwitchToCurrent() {
- while (true) {
- {
- KScopedSpinLock lk{guard};
- current_thread.store(state.highest_priority_thread);
- state.needs_scheduling.store(false);
+void KScheduler::RescheduleCores(KernelCore& kernel, u64 core_mask) {
+ // Send IPI
+ for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
+ if (core_mask & (1ULL << i)) {
+ kernel.PhysicalCore(i).Interrupt();
}
- const auto is_switch_pending = [this] {
- KScopedSpinLock lk{guard};
- return state.needs_scheduling.load();
- };
- do {
- auto next_thread = current_thread.load();
- if (next_thread != nullptr) {
- const auto locked = next_thread->context_guard.TryLock();
- if (state.needs_scheduling.load()) {
- next_thread->context_guard.Unlock();
- break;
- }
- if (next_thread->GetActiveCore() != core_id) {
- next_thread->context_guard.Unlock();
- break;
- }
- if (!locked) {
- continue;
- }
- }
- auto thread = next_thread ? next_thread : idle_thread;
- Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
- } while (!is_switch_pending());
}
}
-void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) {
- const u64 prev_switch_ticks = last_context_switch_time;
- const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
- const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
-
- if (thread != nullptr) {
- thread->AddCpuTime(core_id, update_ticks);
- }
-
- if (process != nullptr) {
- process->UpdateCPUTimeTicks(update_ticks);
- }
-
- last_context_switch_time = most_recent_switch_ticks;
-}
-
-void KScheduler::Initialize() {
- idle_thread = KThread::Create(system.Kernel());
- ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess());
- idle_thread->SetName(fmt::format("IdleThread:{}", core_id));
-}
-
-KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
- : KScopedLock(kernel.GlobalSchedulerContext().SchedulerLock()) {}
-
-KScopedSchedulerLock::~KScopedSchedulerLock() = default;
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h
index 82fcd99e7..534321d8d 100644
--- a/src/core/hle/kernel/k_scheduler.h
+++ b/src/core/hle/kernel/k_scheduler.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -12,6 +11,7 @@
#include "core/hle/kernel/k_scheduler_lock.h"
#include "core/hle/kernel/k_scoped_lock.h"
#include "core/hle/kernel/k_spin_lock.h"
+#include "core/hle/kernel/k_thread.h"
namespace Common {
class Fiber;
@@ -24,188 +24,150 @@ class System;
namespace Kernel {
class KernelCore;
+class KInterruptTaskManager;
class KProcess;
-class SchedulerLock;
class KThread;
+class KScopedDisableDispatch;
+class KScopedSchedulerLock;
+class KScopedSchedulerLockAndSleep;
class KScheduler final {
public:
- explicit KScheduler(Core::System& system_, s32 core_id_);
- ~KScheduler();
-
- void Finalize();
+ YUZU_NON_COPYABLE(KScheduler);
+ YUZU_NON_MOVEABLE(KScheduler);
- /// Reschedules to the next available thread (call after current thread is suspended)
- void RescheduleCurrentCore();
+ using LockType = KAbstractSchedulerLock<KScheduler>;
- /// Reschedules cores pending reschedule, to be called on EnableScheduling.
- static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
+ explicit KScheduler(KernelCore& kernel);
+ ~KScheduler();
- /// The next two are for SingleCore Only.
- /// Unload current thread before preempting core.
+ void Initialize(KThread* main_thread, KThread* idle_thread, s32 core_id);
+ void Activate();
+ void OnThreadStart();
void Unload(KThread* thread);
-
- /// Reload current thread after core preemption.
void Reload(KThread* thread);
- /// Gets the current running thread
- [[nodiscard]] KThread* GetCurrentThread() const;
+ void SetInterruptTaskRunnable();
+ void RequestScheduleOnInterrupt();
+ void PreemptSingleCore();
- /// Gets the idle thread
- [[nodiscard]] KThread* GetIdleThread() const {
- return idle_thread;
+ u64 GetIdleCount() {
+ return m_state.idle_count;
}
- /// Returns true if the scheduler is idle
- [[nodiscard]] bool IsIdle() const {
- return GetCurrentThread() == idle_thread;
+ KThread* GetIdleThread() const {
+ return m_idle_thread;
}
- /// Gets the timestamp for the last context switch in ticks.
- [[nodiscard]] u64 GetLastContextSwitchTicks() const;
-
- [[nodiscard]] bool ContextSwitchPending() const {
- return state.needs_scheduling.load(std::memory_order_relaxed);
+ bool IsIdle() const {
+ return m_current_thread.load() == m_idle_thread;
}
- void Initialize();
-
- void OnThreadStart();
+ KThread* GetPreviousThread() const {
+ return m_state.prev_thread;
+ }
- [[nodiscard]] std::shared_ptr<Common::Fiber>& ControlContext() {
- return switch_fiber;
+ KThread* GetSchedulerCurrentThread() const {
+ return m_current_thread.load();
}
- [[nodiscard]] const std::shared_ptr<Common::Fiber>& ControlContext() const {
- return switch_fiber;
+ s64 GetLastContextSwitchTime() const {
+ return m_last_context_switch_time;
}
- [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread);
+ // Static public API.
+ static bool CanSchedule(KernelCore& kernel) {
+ return GetCurrentThread(kernel).GetDisableDispatchCount() == 0;
+ }
+ static bool IsSchedulerLockedByCurrentThread(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().scheduler_lock.IsLockedByCurrentThread();
+ }
- /**
- * Takes a thread and moves it to the back of the it's priority list.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldWithoutCoreMigration(KernelCore& kernel);
+ static bool IsSchedulerUpdateNeeded(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().scheduler_update_needed;
+ }
+ static void SetSchedulerUpdateNeeded(KernelCore& kernel) {
+ kernel.GlobalSchedulerContext().scheduler_update_needed = true;
+ }
+ static void ClearSchedulerUpdateNeeded(KernelCore& kernel) {
+ kernel.GlobalSchedulerContext().scheduler_update_needed = false;
+ }
- /**
- * Takes a thread and moves it to the back of the it's priority list.
- * Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
- * a better priority than the next thread in the core.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldWithCoreMigration(KernelCore& kernel);
+ static void DisableScheduling(KernelCore& kernel);
+ static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
- /**
- * Takes a thread and moves it out of the scheduling queue.
- * and into the suggested queue. If no thread can be scheduled afterwards in that core,
- * a suggested thread is obtained instead.
- *
- * @note This operation can be redundant and no scheduling is changed if marked as so.
- */
- static void YieldToAnyThread(KernelCore& kernel);
+ static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
static void ClearPreviousThread(KernelCore& kernel, KThread* thread);
- /// Notify the scheduler a thread's status has changed.
static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
-
- /// Notify the scheduler a thread's priority has changed.
static void OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority);
-
- /// Notify the scheduler a thread's core and/or affinity mask has changed.
static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
const KAffinityMask& old_affinity, s32 old_core);
- static bool CanSchedule(KernelCore& kernel);
- static bool IsSchedulerUpdateNeeded(const KernelCore& kernel);
- static void SetSchedulerUpdateNeeded(KernelCore& kernel);
- static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
- static void DisableScheduling(KernelCore& kernel);
- static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
- [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
+ static void RotateScheduledQueue(KernelCore& kernel, s32 core_id, s32 priority);
+ static void RescheduleCores(KernelCore& kernel, u64 cores_needing_scheduling);
+
+ static void YieldWithoutCoreMigration(KernelCore& kernel);
+ static void YieldWithCoreMigration(KernelCore& kernel);
+ static void YieldToAnyThread(KernelCore& kernel);
private:
- friend class GlobalSchedulerContext;
-
- /**
- * Takes care of selecting the new scheduled threads in three steps:
- *
- * 1. First a thread is selected from the top of the priority queue. If no thread
- * is obtained then we move to step two, else we are done.
- *
- * 2. Second we try to get a suggested thread that's not assigned to any core or
- * that is not the top thread in that core.
- *
- * 3. Third is no suggested thread is found, we do a second pass and pick a running
- * thread in another core and swap it with its current thread.
- *
- * returns the cores needing scheduling.
- */
- [[nodiscard]] static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
-
- [[nodiscard]] static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel);
-
- void RotateScheduledQueue(s32 cpu_core_id, s32 priority);
-
- void Schedule() {
- ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
- this->ScheduleImpl();
+ // Static private API.
+ static KSchedulerPriorityQueue& GetPriorityQueue(KernelCore& kernel) {
+ return kernel.GlobalSchedulerContext().priority_queue;
}
+ static u64 UpdateHighestPriorityThreadsImpl(KernelCore& kernel);
- /// Switches the CPU's active thread context to that of the specified thread
- void ScheduleImpl();
-
- /// When a thread wakes up, it must run this through it's new scheduler
- void SwitchContextStep2();
+ static void RescheduleCurrentHLEThread(KernelCore& kernel);
- /**
- * Called on every context switch to update the internal timestamp
- * This also updates the running time ticks for the given thread and
- * process using the following difference:
- *
- * ticks += most_recent_ticks - last_context_switch_ticks
- *
- * The internal tick timestamp for the scheduler is simply the
- * most recent tick count retrieved. No special arithmetic is
- * applied to it.
- */
- void UpdateLastContextSwitchTime(KThread* thread, KProcess* process);
+ // Instanced private API.
+ void ScheduleImpl();
+ void ScheduleImplFiber();
+ void SwitchThread(KThread* next_thread);
- static void OnSwitch(void* this_scheduler);
- void SwitchToCurrent();
+ void Schedule();
+ void ScheduleOnInterrupt();
- KThread* prev_thread{};
- std::atomic<KThread*> current_thread{};
+ void RescheduleOtherCores(u64 cores_needing_scheduling);
+ void RescheduleCurrentCore();
+ void RescheduleCurrentCoreImpl();
- KThread* idle_thread{};
+ u64 UpdateHighestPriorityThread(KThread* thread);
- std::shared_ptr<Common::Fiber> switch_fiber{};
+private:
+ friend class KScopedDisableDispatch;
struct SchedulingState {
- std::atomic<bool> needs_scheduling{};
- bool interrupt_task_thread_runnable{};
- bool should_count_idle{};
- u64 idle_count{};
- KThread* highest_priority_thread{};
- void* idle_thread_stack{};
+ std::atomic<bool> needs_scheduling{false};
+ bool interrupt_task_runnable{false};
+ bool should_count_idle{false};
+ u64 idle_count{0};
+ KThread* highest_priority_thread{nullptr};
+ void* idle_thread_stack{nullptr};
+ std::atomic<KThread*> prev_thread{nullptr};
+ KInterruptTaskManager* interrupt_task_manager{nullptr};
};
- SchedulingState state;
-
- Core::System& system;
- u64 last_context_switch_time{};
- const s32 core_id;
-
- KSpinLock guard{};
+ KernelCore& kernel;
+ SchedulingState m_state;
+ bool m_is_active{false};
+ s32 m_core_id{0};
+ s64 m_last_context_switch_time{0};
+ KThread* m_idle_thread{nullptr};
+ std::atomic<KThread*> m_current_thread{nullptr};
+
+ std::shared_ptr<Common::Fiber> m_switch_fiber{};
+ KThread* m_switch_cur_thread{};
+ KThread* m_switch_highest_priority_thread{};
+ bool m_switch_from_schedule{};
};
-class [[nodiscard]] KScopedSchedulerLock : KScopedLock<GlobalSchedulerContext::LockType> {
+class KScopedSchedulerLock : public KScopedLock<KScheduler::LockType> {
public:
- explicit KScopedSchedulerLock(KernelCore& kernel);
- ~KScopedSchedulerLock();
+ explicit KScopedSchedulerLock(KernelCore& kernel)
+ : KScopedLock(kernel.GlobalSchedulerContext().scheduler_lock) {}
+ ~KScopedSchedulerLock() = default;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h
index 93c47f1b1..73314b45e 100644
--- a/src/core/hle/kernel/k_scheduler_lock.h
+++ b/src/core/hle/kernel/k_scheduler_lock.h
@@ -1,13 +1,15 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <atomic>
#include "common/assert.h"
+#include "core/hle/kernel/k_interrupt_manager.h"
#include "core/hle/kernel/k_spin_lock.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/physical_core.h"
namespace Kernel {
@@ -75,7 +77,7 @@ private:
KernelCore& kernel;
KAlignedSpinLock spin_lock{};
s32 lock_count{};
- KThread* owner_thread{};
+ std::atomic<KThread*> owner_thread{};
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_scoped_lock.h b/src/core/hle/kernel/k_scoped_lock.h
index 89a7ffe49..857e21156 100644
--- a/src/core/hle/kernel/k_scoped_lock.h
+++ b/src/core/hle/kernel/k_scoped_lock.h
@@ -1,9 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// This file references various implementation details from Atmosphere, an open-source firmware for
-// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_scoped_resource_reservation.h b/src/core/hle/kernel/k_scoped_resource_reservation.h
index 07272075d..436bcf9fe 100644
--- a/src/core/hle/kernel/k_scoped_resource_reservation.h
+++ b/src/core/hle/kernel/k_scoped_resource_reservation.h
@@ -1,9 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// This file references various implementation details from Atmosphere, an open-source firmware for
-// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
index 2995c492d..76c095e69 100644
--- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
+++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h
@@ -1,9 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-// This file references various implementation details from Atmosphere, an open-source firmware for
-// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_server_port.cpp b/src/core/hle/kernel/k_server_port.cpp
index 433fc98e1..e968f26ad 100644
--- a/src/core/hle/kernel/k_server_port.cpp
+++ b/src/core/hle/kernel/k_server_port.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <tuple>
#include "common/assert.h"
@@ -62,6 +61,12 @@ void KServerPort::Destroy() {
// Close our reference to our parent.
parent->Close();
+
+ // Release host emulation members.
+ session_handler.reset();
+
+ // Ensure that the global list tracking server objects does not hold on to a reference.
+ kernel.UnregisterServerObject(this);
}
bool 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..fd4f4bd20 100644
--- a/src/core/hle/kernel/k_server_port.h
+++ b/src/core/hle/kernel/k_server_port.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -30,11 +29,11 @@ public:
/// Whether or not this server port has an HLE handler available.
bool HasSessionRequestHandler() const {
- return session_handler != nullptr;
+ return !session_handler.expired();
}
/// Gets the HLE handler for this port.
- SessionRequestHandlerPtr GetSessionRequestHandler() const {
+ SessionRequestHandlerWeakPtr GetSessionRequestHandler() const {
return session_handler;
}
@@ -42,7 +41,7 @@ public:
* Sets the HLE handler template for the port. ServerSessions crated by connecting to this port
* will inherit a reference to this handler.
*/
- void SetSessionHandler(SessionRequestHandlerPtr&& handler) {
+ void SetSessionHandler(SessionRequestHandlerWeakPtr&& handler) {
session_handler = std::move(handler);
}
@@ -66,7 +65,7 @@ private:
void CleanupSessions();
SessionList session_list;
- SessionRequestHandlerPtr session_handler;
+ SessionRequestHandlerWeakPtr session_handler;
KPort* parent{};
};
diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp
index 4d94eb9cf..802c646a6 100644
--- a/src/core/hle/kernel/k_server_session.cpp
+++ b/src/core/hle/kernel/k_server_session.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <tuple>
#include <utility>
@@ -27,10 +26,7 @@ namespace Kernel {
KServerSession::KServerSession(KernelCore& kernel_) : KSynchronizationObject{kernel_} {}
-KServerSession::~KServerSession() {
- // Ensure that the global list tracking server sessions does not hold on to a reference.
- kernel.UnregisterServerSession(this);
-}
+KServerSession::~KServerSession() = default;
void KServerSession::Initialize(KSession* parent_session_, std::string&& name_,
std::shared_ptr<SessionRequestManager> manager_) {
@@ -49,6 +45,12 @@ void KServerSession::Destroy() {
parent->OnServerClosed();
parent->Close();
+
+ // Release host emulation members.
+ manager.reset();
+
+ // Ensure that the global list tracking server objects does not hold on to a reference.
+ kernel.UnregisterServerObject(this);
}
void KServerSession::OnClientClosed() {
@@ -77,7 +79,7 @@ std::size_t KServerSession::NumDomainRequestHandlers() const {
return manager->DomainHandlerCount();
}
-ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
+Result KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
if (!context.HasDomainMessageHeader()) {
return ResultSuccess;
}
@@ -95,10 +97,15 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
"object_id {} is too big! This probably means a recent service call "
"to {} needed to return a new interface!",
object_id, name);
- UNREACHABLE();
+ ASSERT(false);
return ResultSuccess; // Ignore error if asserts are off
}
- return manager->DomainHandler(object_id - 1)->HandleSyncRequest(*this, context);
+ if (auto strong_ptr = manager->DomainHandler(object_id - 1).lock()) {
+ return strong_ptr->HandleSyncRequest(*this, context);
+ } else {
+ ASSERT(false);
+ return ResultSuccess;
+ }
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
@@ -116,7 +123,7 @@ ResultCode KServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& co
return ResultSuccess;
}
-ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
+Result KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory) {
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
auto context = std::make_shared<HLERequestContext>(kernel, memory, this, thread);
@@ -136,8 +143,8 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor
return ResultSuccess;
}
-ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
- ResultCode result = ResultSuccess;
+Result KServerSession::CompleteSyncRequest(HLERequestContext& context) {
+ Result result = ResultSuccess;
// If the session has been converted to a domain, handle the domain request
if (manager->HasSessionRequestHandler(context)) {
@@ -166,8 +173,8 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) {
return result;
}
-ResultCode KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
- Core::Timing::CoreTiming& core_timing) {
+Result KServerSession::HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing) {
return QueueSyncRequest(thread, memory);
}
diff --git a/src/core/hle/kernel/k_server_session.h b/src/core/hle/kernel/k_server_session.h
index 5b76bf17c..6d0821945 100644
--- a/src/core/hle/kernel/k_server_session.h
+++ b/src/core/hle/kernel/k_server_session.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -74,10 +73,10 @@ public:
* @param memory Memory context to handle the sync request under.
* @param core_timing Core timing context to schedule the request event under.
*
- * @returns ResultCode from the operation.
+ * @returns Result from the operation.
*/
- ResultCode HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
- Core::Timing::CoreTiming& core_timing);
+ Result HandleSyncRequest(KThread* thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing);
/// Adds a new domain request handler to the collection of request handlers within
/// this ServerSession instance.
@@ -104,14 +103,14 @@ public:
private:
/// Queues a sync request from the emulated application.
- ResultCode QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
+ Result QueueSyncRequest(KThread* thread, Core::Memory::Memory& memory);
/// Completes a sync request from the emulated application.
- ResultCode CompleteSyncRequest(HLERequestContext& context);
+ Result CompleteSyncRequest(HLERequestContext& context);
/// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
/// object handle.
- ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
+ Result HandleDomainSyncRequest(Kernel::HLERequestContext& context);
/// This session's HLE request handlers
std::shared_ptr<SessionRequestManager> manager;
diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp
index a64b56b9e..ee05aa282 100644
--- a/src/core/hle/kernel/k_session.cpp
+++ b/src/core/hle/kernel/k_session.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
diff --git a/src/core/hle/kernel/k_session.h b/src/core/hle/kernel/k_session.h
index 62c328a68..c6ead403b 100644
--- a/src/core/hle/kernel/k_session.h
+++ b/src/core/hle/kernel/k_session.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index 51d7538ca..8ff1545b6 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/core.h"
@@ -18,12 +17,10 @@ KSharedMemory::~KSharedMemory() {
kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size);
}
-ResultCode KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
- KPageLinkedList&& page_list_,
- Svc::MemoryPermission owner_permission_,
- Svc::MemoryPermission user_permission_,
- PAddr physical_address_, std::size_t size_,
- std::string name_) {
+Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
+ KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_,
+ Svc::MemoryPermission user_permission_, PAddr physical_address_,
+ std::size_t size_, std::string name_) {
// Set members.
owner_process = owner_process_;
device_memory = &device_memory_;
@@ -67,8 +64,8 @@ void KSharedMemory::Finalize() {
KAutoObjectWithSlabHeapAndContainer<KSharedMemory, KAutoObjectWithList>::Finalize();
}
-ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size,
- Svc::MemoryPermission permissions) {
+Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t map_size,
+ Svc::MemoryPermission permissions) {
const u64 page_count{(map_size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
@@ -86,7 +83,7 @@ ResultCode KSharedMemory::Map(KProcess& target_process, VAddr address, std::size
ConvertToKMemoryPermission(permissions));
}
-ResultCode KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
+Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {
const u64 page_count{(unmap_size + PageSize - 1) / PageSize};
if (page_list.GetNumPages() != page_count) {
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index 81de36136..34cb98456 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -9,7 +8,7 @@
#include "common/common_types.h"
#include "core/device_memory.h"
#include "core/hle/kernel/k_memory_block.h"
-#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/result.h"
@@ -26,10 +25,10 @@ public:
explicit KSharedMemory(KernelCore& kernel_);
~KSharedMemory() override;
- ResultCode Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
- KPageLinkedList&& page_list_, Svc::MemoryPermission owner_permission_,
- Svc::MemoryPermission user_permission_, PAddr physical_address_,
- std::size_t size_, std::string name_);
+ Result Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_,
+ KPageGroup&& page_list_, Svc::MemoryPermission owner_permission_,
+ Svc::MemoryPermission user_permission_, PAddr physical_address_,
+ std::size_t size_, std::string name_);
/**
* Maps a shared memory block to an address in the target process' address space
@@ -38,8 +37,8 @@ public:
* @param map_size Size of the shared memory block to map
* @param permissions Memory block map permissions (specified by SVC field)
*/
- ResultCode Map(KProcess& target_process, VAddr address, std::size_t map_size,
- Svc::MemoryPermission permissions);
+ Result Map(KProcess& target_process, VAddr address, std::size_t map_size,
+ Svc::MemoryPermission permissions);
/**
* Unmaps a shared memory block from an address in the target process' address space
@@ -47,7 +46,7 @@ public:
* @param address Address in system memory to unmap shared memory block
* @param unmap_size Size of the shared memory block to unmap
*/
- ResultCode Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size);
+ Result Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size);
/**
* Gets a pointer to the shared memory block
@@ -77,7 +76,7 @@ public:
private:
Core::DeviceMemory* device_memory;
KProcess* owner_process{};
- KPageLinkedList page_list;
+ KPageGroup page_list;
Svc::MemoryPermission owner_permission{};
Svc::MemoryPermission user_permission{};
PAddr physical_address{};
diff --git a/src/core/hle/kernel/k_shared_memory_info.h b/src/core/hle/kernel/k_shared_memory_info.h
index 20bc19f46..e43db8515 100644
--- a/src/core/hle/kernel/k_shared_memory_info.h
+++ b/src/core/hle/kernel/k_shared_memory_info.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h
index 05c0bec9c..2b303537e 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -16,39 +15,34 @@ class KernelCore;
namespace impl {
-class KSlabHeapImpl final {
-public:
+class KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapImpl);
YUZU_NON_MOVEABLE(KSlabHeapImpl);
+public:
struct Node {
Node* next{};
};
+public:
constexpr KSlabHeapImpl() = default;
- constexpr ~KSlabHeapImpl() = default;
- void Initialize(std::size_t size) {
- ASSERT(head == nullptr);
- obj_size = size;
- }
-
- constexpr std::size_t GetObjectSize() const {
- return obj_size;
+ void Initialize() {
+ ASSERT(m_head == nullptr);
}
Node* GetHead() const {
- return head;
+ return m_head;
}
void* Allocate() {
- Node* ret = head.load();
+ Node* ret = m_head.load();
do {
if (ret == nullptr) {
break;
}
- } while (!head.compare_exchange_weak(ret, ret->next));
+ } while (!m_head.compare_exchange_weak(ret, ret->next));
return ret;
}
@@ -56,170 +50,157 @@ public:
void Free(void* obj) {
Node* node = static_cast<Node*>(obj);
- Node* cur_head = head.load();
+ Node* cur_head = m_head.load();
do {
node->next = cur_head;
- } while (!head.compare_exchange_weak(cur_head, node));
+ } while (!m_head.compare_exchange_weak(cur_head, node));
}
private:
- std::atomic<Node*> head{};
- std::size_t obj_size{};
+ std::atomic<Node*> m_head{};
};
} // namespace impl
-class KSlabHeapBase {
-public:
+template <bool SupportDynamicExpansion>
+class KSlabHeapBase : protected impl::KSlabHeapImpl {
YUZU_NON_COPYABLE(KSlabHeapBase);
YUZU_NON_MOVEABLE(KSlabHeapBase);
- constexpr KSlabHeapBase() = default;
- constexpr ~KSlabHeapBase() = default;
+private:
+ size_t m_obj_size{};
+ uintptr_t m_peak{};
+ uintptr_t m_start{};
+ uintptr_t m_end{};
- constexpr bool Contains(uintptr_t addr) const {
- return start <= addr && addr < end;
- }
+private:
+ void UpdatePeakImpl(uintptr_t obj) {
+ static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
+ std::atomic_ref<uintptr_t> peak_ref(m_peak);
- constexpr std::size_t GetSlabHeapSize() const {
- return (end - start) / GetObjectSize();
+ const uintptr_t alloc_peak = obj + this->GetObjectSize();
+ uintptr_t cur_peak = m_peak;
+ do {
+ if (alloc_peak <= cur_peak) {
+ break;
+ }
+ } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak));
}
- constexpr std::size_t GetObjectSize() const {
- return impl.GetObjectSize();
- }
+public:
+ constexpr KSlabHeapBase() = default;
- constexpr uintptr_t GetSlabHeapAddress() const {
- return start;
+ bool Contains(uintptr_t address) const {
+ return m_start <= address && address < m_end;
}
- std::size_t GetObjectIndexImpl(const void* obj) const {
- return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize();
+ void Initialize(size_t obj_size, void* memory, size_t memory_size) {
+ // Ensure we don't initialize a slab using null memory.
+ ASSERT(memory != nullptr);
+
+ // Set our object size.
+ m_obj_size = obj_size;
+
+ // Initialize the base allocator.
+ KSlabHeapImpl::Initialize();
+
+ // Set our tracking variables.
+ const size_t num_obj = (memory_size / obj_size);
+ m_start = reinterpret_cast<uintptr_t>(memory);
+ m_end = m_start + num_obj * obj_size;
+ m_peak = m_start;
+
+ // Free the objects.
+ u8* cur = reinterpret_cast<u8*>(m_end);
+
+ for (size_t i = 0; i < num_obj; i++) {
+ cur -= obj_size;
+ KSlabHeapImpl::Free(cur);
+ }
}
- std::size_t GetPeakIndex() const {
- return GetObjectIndexImpl(reinterpret_cast<const void*>(peak));
+ size_t GetSlabHeapSize() const {
+ return (m_end - m_start) / this->GetObjectSize();
}
- void* AllocateImpl() {
- return impl.Allocate();
+ size_t GetObjectSize() const {
+ return m_obj_size;
}
- void FreeImpl(void* obj) {
- // Don't allow freeing an object that wasn't allocated from this heap
- ASSERT(Contains(reinterpret_cast<uintptr_t>(obj)));
+ void* Allocate() {
+ void* obj = KSlabHeapImpl::Allocate();
- impl.Free(obj);
+ return obj;
}
- void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) {
- // Ensure we don't initialize a slab using null memory
- ASSERT(memory != nullptr);
-
- // Initialize the base allocator
- impl.Initialize(obj_size);
+ void Free(void* obj) {
+ // Don't allow freeing an object that wasn't allocated from this heap.
+ const bool contained = this->Contains(reinterpret_cast<uintptr_t>(obj));
+ ASSERT(contained);
+ KSlabHeapImpl::Free(obj);
+ }
- // Set our tracking variables
- const std::size_t num_obj = (memory_size / obj_size);
- start = reinterpret_cast<uintptr_t>(memory);
- end = start + num_obj * obj_size;
- peak = start;
+ size_t GetObjectIndex(const void* obj) const {
+ if constexpr (SupportDynamicExpansion) {
+ if (!this->Contains(reinterpret_cast<uintptr_t>(obj))) {
+ return std::numeric_limits<size_t>::max();
+ }
+ }
- // Free the objects
- u8* cur = reinterpret_cast<u8*>(end);
+ return (reinterpret_cast<uintptr_t>(obj) - m_start) / this->GetObjectSize();
+ }
- for (std::size_t i{}; i < num_obj; i++) {
- cur -= obj_size;
- impl.Free(cur);
- }
+ size_t GetPeakIndex() const {
+ return this->GetObjectIndex(reinterpret_cast<const void*>(m_peak));
}
-private:
- using Impl = impl::KSlabHeapImpl;
+ uintptr_t GetSlabHeapAddress() const {
+ return m_start;
+ }
- Impl impl;
- uintptr_t peak{};
- uintptr_t start{};
- uintptr_t end{};
+ size_t GetNumRemaining() const {
+ // Only calculate the number of remaining objects under debug configuration.
+ return 0;
+ }
};
template <typename T>
-class KSlabHeap final : public KSlabHeapBase {
-public:
- enum class AllocationType {
- Host,
- Guest,
- };
+class KSlabHeap final : public KSlabHeapBase<false> {
+private:
+ using BaseHeap = KSlabHeapBase<false>;
- explicit constexpr KSlabHeap(AllocationType allocation_type_ = AllocationType::Host)
- : KSlabHeapBase(), allocation_type{allocation_type_} {}
+public:
+ constexpr KSlabHeap() = default;
- void Initialize(void* memory, std::size_t memory_size) {
- if (allocation_type == AllocationType::Guest) {
- InitializeImpl(sizeof(T), memory, memory_size);
- }
+ void Initialize(void* memory, size_t memory_size) {
+ BaseHeap::Initialize(sizeof(T), memory, memory_size);
}
T* Allocate() {
- switch (allocation_type) {
- case AllocationType::Host:
- // Fallback for cases where we do not yet support allocating guest memory from the slab
- // heap, such as for kernel memory regions.
- return new T;
-
- case AllocationType::Guest:
- T* obj = static_cast<T*>(AllocateImpl());
- if (obj != nullptr) {
- new (obj) T();
- }
- return obj;
- }
+ T* obj = static_cast<T*>(BaseHeap::Allocate());
- UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
- return nullptr;
+ if (obj != nullptr) [[likely]] {
+ std::construct_at(obj);
+ }
+ return obj;
}
- T* AllocateWithKernel(KernelCore& kernel) {
- switch (allocation_type) {
- case AllocationType::Host:
- // Fallback for cases where we do not yet support allocating guest memory from the slab
- // heap, such as for kernel memory regions.
- return new T(kernel);
+ T* Allocate(KernelCore& kernel) {
+ T* obj = static_cast<T*>(BaseHeap::Allocate());
- case AllocationType::Guest:
- T* obj = static_cast<T*>(AllocateImpl());
- if (obj != nullptr) {
- new (obj) T(kernel);
- }
- return obj;
+ if (obj != nullptr) [[likely]] {
+ std::construct_at(obj, kernel);
}
-
- UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
- return nullptr;
+ return obj;
}
void Free(T* obj) {
- switch (allocation_type) {
- case AllocationType::Host:
- // Fallback for cases where we do not yet support allocating guest memory from the slab
- // heap, such as for kernel memory regions.
- delete obj;
- return;
-
- case AllocationType::Guest:
- FreeImpl(obj);
- return;
- }
-
- UNREACHABLE_MSG("Invalid AllocationType {}", allocation_type);
+ BaseHeap::Free(obj);
}
- constexpr std::size_t GetObjectIndex(const T* obj) const {
- return GetObjectIndexImpl(obj);
+ size_t GetObjectIndex(const T* obj) const {
+ return BaseHeap::GetObjectIndex(obj);
}
-
-private:
- const AllocationType allocation_type;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_spin_lock.cpp b/src/core/hle/kernel/k_spin_lock.cpp
index 4412aa4bb..6e16a1849 100644
--- a/src/core/hle/kernel/k_spin_lock.cpp
+++ b/src/core/hle/kernel/k_spin_lock.cpp
@@ -1,54 +1,20 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_spin_lock.h"
-#if _MSC_VER
-#include <intrin.h>
-#if _M_AMD64
-#define __x86_64__ 1
-#endif
-#if _M_ARM64
-#define __aarch64__ 1
-#endif
-#else
-#if __x86_64__
-#include <xmmintrin.h>
-#endif
-#endif
-
-namespace {
-
-void ThreadPause() {
-#if __x86_64__
- _mm_pause();
-#elif __aarch64__ && _MSC_VER
- __yield();
-#elif __aarch64__
- asm("yield");
-#endif
-}
-
-} // namespace
-
namespace Kernel {
void KSpinLock::Lock() {
- while (lck.test_and_set(std::memory_order_acquire)) {
- ThreadPause();
- }
+ lck.lock();
}
void KSpinLock::Unlock() {
- lck.clear(std::memory_order_release);
+ lck.unlock();
}
bool KSpinLock::TryLock() {
- if (lck.test_and_set(std::memory_order_acquire)) {
- return false;
- }
- return true;
+ return lck.try_lock();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_spin_lock.h b/src/core/hle/kernel/k_spin_lock.h
index 4d87d006a..397a93d21 100644
--- a/src/core/hle/kernel/k_spin_lock.h
+++ b/src/core/hle/kernel/k_spin_lock.h
@@ -1,10 +1,9 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <atomic>
+#include <mutex>
#include "core/hle/kernel/k_scoped_lock.h"
@@ -25,7 +24,7 @@ public:
[[nodiscard]] bool TryLock();
private:
- std::atomic_flag lck = ATOMIC_FLAG_INIT;
+ std::mutex lck;
};
// TODO(bunnei): Alias for now, in case we want to implement these accurately in the future.
diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp
index e4c5eb74f..802dca046 100644
--- a/src/core/hle/kernel/k_synchronization_object.cpp
+++ b/src/core/hle/kernel/k_synchronization_object.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/common_types.h"
@@ -23,7 +22,7 @@ public:
: KThreadQueueWithoutEndWait(kernel_), m_objects(o), m_nodes(n), m_count(c) {}
void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
- ResultCode wait_result) override {
+ Result wait_result) override {
// Determine the sync index, and unlink all nodes.
s32 sync_index = -1;
for (auto i = 0; i < m_count; ++i) {
@@ -46,8 +45,7 @@ public:
KThreadQueue::EndWait(waiting_thread, wait_result);
}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove all nodes from our list.
for (auto i = 0; i < m_count; ++i) {
m_objects[i]->UnlinkNode(std::addressof(m_nodes[i]));
@@ -73,9 +71,9 @@ void KSynchronizationObject::Finalize() {
KAutoObject::Finalize();
}
-ResultCode KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
- KSynchronizationObject** objects, const s32 num_objects,
- s64 timeout) {
+Result KSynchronizationObject::Wait(KernelCore& kernel_ctx, s32* out_index,
+ KSynchronizationObject** objects, const s32 num_objects,
+ s64 timeout) {
// Allocate space on stack for thread nodes.
std::vector<ThreadListNode> thread_nodes(num_objects);
@@ -149,7 +147,7 @@ KSynchronizationObject::KSynchronizationObject(KernelCore& kernel_)
KSynchronizationObject::~KSynchronizationObject() = default;
-void KSynchronizationObject::NotifyAvailable(ResultCode result) {
+void KSynchronizationObject::NotifyAvailable(Result result) {
KScopedSchedulerLock sl(kernel);
// If we're not signaled, we've nothing to notify.
diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h
index ec235437b..8d8122ab7 100644
--- a/src/core/hle/kernel/k_synchronization_object.h
+++ b/src/core/hle/kernel/k_synchronization_object.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -25,9 +24,9 @@ public:
KThread* thread{};
};
- [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index,
- KSynchronizationObject** objects, const s32 num_objects,
- s64 timeout);
+ [[nodiscard]] static Result Wait(KernelCore& kernel, s32* out_index,
+ KSynchronizationObject** objects, const s32 num_objects,
+ s64 timeout);
void Finalize() override;
@@ -73,7 +72,7 @@ protected:
virtual void OnFinalizeSynchronizationObject() {}
- void NotifyAvailable(ResultCode result);
+ void NotifyAvailable(Result result);
void NotifyAvailable() {
return this->NotifyAvailable(ResultSuccess);
}
diff --git a/src/core/hle/kernel/k_system_control.h b/src/core/hle/kernel/k_system_control.h
index d755082c2..b8292e9d0 100644
--- a/src/core/hle/kernel/k_system_control.h
+++ b/src/core/hle/kernel/k_system_control.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index de3ffe0c7..174afc80d 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <atomic>
@@ -14,9 +13,7 @@
#include "common/common_types.h"
#include "common/fiber.h"
#include "common/logging/log.h"
-#include "common/scope_exit.h"
#include "common/settings.h"
-#include "common/thread_queue_list.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
@@ -33,7 +30,6 @@
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h"
-#include "core/hle/kernel/time_manager.h"
#include "core/hle/result.h"
#include "core/memory.h"
@@ -84,8 +80,7 @@ public:
explicit ThreadQueueImplForKThreadSetProperty(KernelCore& kernel_, KThread::WaiterList* wl)
: KThreadQueue(kernel_), m_wait_list(wl) {}
- void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) override {
+ void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) override {
// Remove the thread from the wait list.
m_wait_list->erase(m_wait_list->iterator_to(*waiting_thread));
@@ -103,8 +98,8 @@ KThread::KThread(KernelCore& kernel_)
: KAutoObjectWithSlabHeapAndContainer{kernel_}, activity_pause_lock{kernel_} {}
KThread::~KThread() = default;
-ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio,
- s32 virt_core, KProcess* owner, ThreadType type) {
+Result KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio,
+ s32 virt_core, KProcess* owner, ThreadType type) {
// Assert parameters are valid.
ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) ||
(Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority));
@@ -137,7 +132,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
UNIMPLEMENTED();
break;
default:
- UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
+ ASSERT_MSG(false, "KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type));
break;
}
thread_type = type;
@@ -202,6 +197,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
resource_limit_release_hint = false;
cpu_time = 0;
+ // Set debug context.
+ stack_top = user_stack_top;
+ argument = arg;
+
// Clear our stack parameters.
std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0,
sizeof(StackParameters));
@@ -210,7 +209,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
if (owner != nullptr) {
// Setup the TLS, if needed.
if (type == ThreadType::User) {
- tls_address = owner->CreateTLSRegion();
+ R_TRY(owner->CreateThreadLocalRegion(std::addressof(tls_address)));
}
parent = owner;
@@ -225,7 +224,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
// Setup the stack parameters.
StackParameters& sp = GetStackParameters();
sp.cur_thread = this;
- sp.disable_count = 0;
+ sp.disable_count = 1;
SetInExceptionHandler();
// Set thread ID.
@@ -245,46 +244,51 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
return ResultSuccess;
}
-ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
- VAddr user_stack_top, s32 prio, s32 core, KProcess* owner,
- ThreadType type, std::function<void(void*)>&& init_func,
- void* init_func_parameter) {
+Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg,
+ VAddr user_stack_top, s32 prio, s32 core, KProcess* owner,
+ ThreadType type, std::function<void()>&& init_func) {
// Initialize the thread.
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
// Initialize emulation parameters.
- thread->host_context =
- std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
+ thread->host_context = std::make_shared<Common::Fiber>(std::move(init_func));
thread->is_single_core = !Settings::values.use_multi_core.GetValue();
return ResultSuccess;
}
-ResultCode KThread::InitializeDummyThread(KThread* thread) {
- return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy);
+Result KThread::InitializeDummyThread(KThread* thread) {
+ // Initialize the thread.
+ R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy));
+
+ // Initialize emulation parameters.
+ thread->stack_parameters.disable_count = 0;
+
+ return ResultSuccess;
+}
+
+Result KThread::InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core) {
+ return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
+ system.GetCpuManager().GetGuestActivateFunc());
}
-ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
+Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) {
return InitializeThread(thread, {}, {}, {}, IdleThreadPriority, virt_core, {}, ThreadType::Main,
- Core::CpuManager::GetIdleThreadStartFunc(),
- system.GetCpuManager().GetStartFuncParamater());
+ system.GetCpuManager().GetIdleThreadStartFunc());
}
-ResultCode KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
- KThreadFunction func, uintptr_t arg,
- s32 virt_core) {
+Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
+ KThreadFunction func, uintptr_t arg, s32 virt_core) {
return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority,
- Core::CpuManager::GetSuspendThreadStartFunc(),
- system.GetCpuManager().GetStartFuncParamater());
+ system.GetCpuManager().GetShutdownThreadStartFunc());
}
-ResultCode KThread::InitializeUserThread(Core::System& system, KThread* thread,
- KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
- s32 prio, s32 virt_core, KProcess* owner) {
+Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func,
+ uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core,
+ KProcess* owner) {
system.Kernel().GlobalSchedulerContext().AddThread(thread);
return InitializeThread(thread, func, arg, user_stack_top, prio, virt_core, owner,
- ThreadType::User, Core::CpuManager::GetGuestThreadStartFunc(),
- system.GetCpuManager().GetStartFuncParamater());
+ ThreadType::User, system.GetCpuManager().GetGuestThreadFunc());
}
void KThread::PostDestroy(uintptr_t arg) {
@@ -305,7 +309,7 @@ void KThread::Finalize() {
// If the thread has a local region, delete it.
if (tls_address != 0) {
- parent->FreeTLSRegion(tls_address);
+ ASSERT(parent->DeleteThreadLocalRegion(tls_address).IsSuccess());
}
// Release any waiters.
@@ -315,17 +319,26 @@ void KThread::Finalize() {
auto it = waiter_list.begin();
while (it != waiter_list.end()) {
- // Clear the lock owner
- it->SetLockOwner(nullptr);
+ // Get the thread.
+ KThread* const waiter = std::addressof(*it);
+
+ // The thread shouldn't be a kernel waiter.
+ ASSERT(!IsKernelAddressKey(waiter->GetAddressKey()));
+
+ // Clear the lock owner.
+ waiter->SetLockOwner(nullptr);
// Erase the waiter from our list.
it = waiter_list.erase(it);
// Cancel the thread's wait.
- it->CancelWait(ResultInvalidState, true);
+ waiter->CancelWait(ResultInvalidState, true);
}
}
+ // Release host emulation members.
+ host_context.reset();
+
// Perform inherited finalization.
KSynchronizationObject::Finalize();
}
@@ -379,7 +392,7 @@ void KThread::FinishTermination() {
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
KThread* core_thread{};
do {
- core_thread = kernel.Scheduler(i).GetCurrentThread();
+ core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
} while (core_thread == this);
}
}
@@ -484,9 +497,7 @@ void KThread::Unpin() {
// Resume any threads that began waiting on us while we were pinned.
for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) {
- if (it->GetState() == ThreadState::Waiting) {
- it->SetState(ThreadState::Runnable);
- }
+ it->EndWait(ResultSuccess);
}
}
@@ -520,7 +531,7 @@ void KThread::ClearInterruptFlag() {
memory.Write16(tls_address + offsetof(ThreadLocalRegion, interrupt_flag), 0);
}
-ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
+Result KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
KScopedSchedulerLock sl{kernel};
// Get the virtual mask.
@@ -530,7 +541,7 @@ ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
return ResultSuccess;
}
-ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
+Result KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) {
KScopedSchedulerLock sl{kernel};
ASSERT(num_core_migration_disables >= 0);
@@ -546,7 +557,7 @@ ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_m
return ResultSuccess;
}
-ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
+Result KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
ASSERT(parent != nullptr);
ASSERT(v_affinity_mask != 0);
KScopedLightLock lk(activity_pause_lock);
@@ -628,7 +639,7 @@ ResultCode KThread::SetCoreMask(s32 core_id_, u64 v_affinity_mask) {
s32 thread_core;
for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES);
++thread_core) {
- if (kernel.Scheduler(thread_core).GetCurrentThread() == this) {
+ if (kernel.Scheduler(thread_core).GetSchedulerCurrentThread() == this) {
thread_is_current = true;
break;
}
@@ -723,10 +734,10 @@ void KThread::UpdateState() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Set our suspend flags in state.
- const auto old_state = thread_state;
+ const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
const auto new_state =
static_cast<ThreadState>(this->GetSuspendFlags()) | (old_state & ThreadState::Mask);
- thread_state = new_state;
+ thread_state.store(new_state, std::memory_order_relaxed);
// Note the state change in scheduler.
if (new_state != old_state) {
@@ -738,14 +749,27 @@ void KThread::Continue() {
ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Clear our suspend flags in state.
- const auto old_state = thread_state;
- thread_state = old_state & ThreadState::Mask;
+ const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
+ thread_state.store(old_state & ThreadState::Mask, std::memory_order_relaxed);
// Note the state change in scheduler.
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
-ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
+void KThread::WaitUntilSuspended() {
+ // Make sure we have a suspend requested.
+ ASSERT(IsSuspendRequested());
+
+ // Loop until the thread is not executing on any core.
+ for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
+ KThread* core_thread{};
+ do {
+ core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
+ } while (core_thread == this);
+ }
+}
+
+Result KThread::SetActivity(Svc::ThreadActivity activity) {
// Lock ourselves.
KScopedLightLock lk(activity_pause_lock);
@@ -806,7 +830,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
// Check if the thread is currently running.
// If it is, we'll need to retry.
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
- if (kernel.Scheduler(i).GetCurrentThread() == this) {
+ if (kernel.Scheduler(i).GetSchedulerCurrentThread() == this) {
thread_is_current = true;
break;
}
@@ -818,7 +842,7 @@ ResultCode KThread::SetActivity(Svc::ThreadActivity activity) {
return ResultSuccess;
}
-ResultCode KThread::GetThreadContext3(std::vector<u8>& out) {
+Result KThread::GetThreadContext3(std::vector<u8>& out) {
// Lock ourselves.
KScopedLightLock lk{activity_pause_lock};
@@ -868,6 +892,7 @@ void KThread::AddWaiterImpl(KThread* thread) {
// Keep track of how many kernel waiters we have.
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters++) >= 0);
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
}
// Insert the waiter.
@@ -881,6 +906,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) {
// Keep track of how many kernel waiters we have.
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
}
// Remove the waiter.
@@ -956,6 +982,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
// Keep track of how many kernel waiters we have.
if (IsKernelAddressKey(thread->GetAddressKey())) {
ASSERT((num_kernel_waiters--) > 0);
+ KScheduler::SetSchedulerUpdateNeeded(kernel);
}
it = waiter_list.erase(it);
@@ -983,7 +1010,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) {
return next_lock_owner;
}
-ResultCode KThread::Run() {
+Result KThread::Run() {
while (true) {
KScopedSchedulerLock lk{kernel};
@@ -1011,8 +1038,6 @@ ResultCode KThread::Run() {
// Set our state and finish.
SetState(ThreadState::Runnable);
- DisableDispatch();
-
return ResultSuccess;
}
}
@@ -1044,9 +1069,11 @@ void KThread::Exit() {
// Register the thread as a work task.
KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this);
}
+
+ UNREACHABLE_MSG("KThread::Exit() would return");
}
-ResultCode KThread::Sleep(s64 timeout) {
+Result KThread::Sleep(s64 timeout) {
ASSERT(!kernel.GlobalSchedulerContext().IsLocked());
ASSERT(this == GetCurrentThreadPointer(kernel));
ASSERT(timeout > 0);
@@ -1079,17 +1106,12 @@ void KThread::IfDummyThreadTryWait() {
return;
}
- // Block until we can grab the lock.
- KScopedSpinLock lk{dummy_wait_lock};
-}
-
-void KThread::IfDummyThreadBeginWait() {
- if (!IsDummyThread()) {
- return;
- }
+ ASSERT(!kernel.IsPhantomModeForSingleCore());
- // Ensure the thread will block when IfDummyThreadTryWait is called.
- dummy_wait_lock.Lock();
+ // Block until we are no longer waiting.
+ std::unique_lock lk(dummy_wait_lock);
+ dummy_wait_cv.wait(
+ lk, [&] { return GetState() != ThreadState::Waiting || kernel.IsShuttingDown(); });
}
void KThread::IfDummyThreadEndWait() {
@@ -1097,8 +1119,8 @@ void KThread::IfDummyThreadEndWait() {
return;
}
- // Ensure the thread will no longer block.
- dummy_wait_lock.Unlock();
+ // Wake up the waiting thread.
+ dummy_wait_cv.notify_one();
}
void KThread::BeginWait(KThreadQueue* queue) {
@@ -1107,12 +1129,9 @@ void KThread::BeginWait(KThreadQueue* queue) {
// Set our wait queue.
wait_queue = queue;
-
- // Special case for dummy threads to ensure they block.
- IfDummyThreadBeginWait();
}
-void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_) {
+void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result_) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
@@ -1122,7 +1141,7 @@ void KThread::NotifyAvailable(KSynchronizationObject* signaled_object, ResultCod
}
}
-void KThread::EndWait(ResultCode wait_result_) {
+void KThread::EndWait(Result wait_result_) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
@@ -1141,7 +1160,7 @@ void KThread::EndWait(ResultCode wait_result_) {
}
}
-void KThread::CancelWait(ResultCode wait_result_, bool cancel_timer_task) {
+void KThread::CancelWait(Result wait_result_, bool cancel_timer_task) {
// Lock the scheduler.
KScopedSchedulerLock sl(kernel);
@@ -1158,10 +1177,11 @@ void KThread::SetState(ThreadState state) {
SetMutexWaitAddressForDebugging({});
SetWaitReasonForDebugging({});
- const ThreadState old_state = thread_state;
- thread_state =
- static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask));
- if (thread_state != old_state) {
+ const ThreadState old_state = thread_state.load(std::memory_order_relaxed);
+ thread_state.store(
+ static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)),
+ std::memory_order_relaxed);
+ if (thread_state.load(std::memory_order_relaxed) != old_state) {
KScheduler::OnThreadStateChanged(kernel, this, old_state);
}
}
@@ -1170,6 +1190,10 @@ std::shared_ptr<Common::Fiber>& KThread::GetHostContext() {
return host_context;
}
+void SetCurrentThread(KernelCore& kernel, KThread* thread) {
+ kernel.SetCurrentEmuThread(thread);
+}
+
KThread* GetCurrentThreadPointer(KernelCore& kernel) {
return kernel.GetCurrentEmuThread();
}
@@ -1188,16 +1212,13 @@ KScopedDisableDispatch::~KScopedDisableDispatch() {
return;
}
- // Skip the reschedule if single-core, as dispatch tracking is disabled here.
- if (!Settings::values.use_multi_core.GetValue()) {
- return;
- }
-
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
- auto scheduler = kernel.CurrentScheduler();
+ auto* scheduler = kernel.CurrentScheduler();
- if (scheduler) {
+ if (scheduler && !kernel.IsPhantomModeForSingleCore()) {
scheduler->RescheduleCurrentCore();
+ } else {
+ KScheduler::RescheduleCurrentHLEThread(kernel);
}
} else {
GetCurrentThread(kernel).EnableDispatch();
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index d058db62c..9ee20208e 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -1,10 +1,12 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
#include <span>
#include <string>
#include <utility>
@@ -14,6 +16,7 @@
#include "common/common_types.h"
#include "common/intrusive_red_black_tree.h"
+#include "common/spin_lock.h"
#include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.h"
#include "core/hle/kernel/k_light_lock.h"
@@ -97,6 +100,13 @@ enum class ThreadWaitReasonForDebugging : u32 {
Suspended, ///< Thread is waiting due to process suspension
};
+enum class StepState : u32 {
+ NotStepping, ///< Thread is not currently stepping
+ StepPending, ///< Thread will step when next scheduled
+ StepPerformed, ///< Thread has stepped, waiting to be scheduled again
+};
+
+void SetCurrentThread(KernelCore& kernel, KThread* thread);
[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel);
[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
@@ -166,7 +176,7 @@ public:
void SetBasePriority(s32 value);
- [[nodiscard]] ResultCode Run();
+ [[nodiscard]] Result Run();
void Exit();
@@ -198,6 +208,8 @@ public:
void Continue();
+ void WaitUntilSuspended();
+
constexpr void SetSyncedIndex(s32 index) {
synced_index = index;
}
@@ -206,11 +218,11 @@ public:
return synced_index;
}
- constexpr void SetWaitResult(ResultCode wait_res) {
+ constexpr void SetWaitResult(Result wait_res) {
wait_result = wait_res;
}
- [[nodiscard]] constexpr ResultCode GetWaitResult() const {
+ [[nodiscard]] constexpr Result GetWaitResult() const {
return wait_result;
}
@@ -255,15 +267,23 @@ public:
[[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
[[nodiscard]] ThreadState GetState() const {
- return thread_state & ThreadState::Mask;
+ return thread_state.load(std::memory_order_relaxed) & ThreadState::Mask;
}
[[nodiscard]] ThreadState GetRawState() const {
- return thread_state;
+ return thread_state.load(std::memory_order_relaxed);
}
void SetState(ThreadState state);
+ [[nodiscard]] StepState GetStepState() const {
+ return step_state;
+ }
+
+ void SetStepState(StepState state) {
+ step_state = state;
+ }
+
[[nodiscard]] s64 GetLastScheduledTick() const {
return last_scheduled_tick;
}
@@ -325,15 +345,15 @@ public:
return physical_affinity_mask;
}
- [[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
+ [[nodiscard]] Result GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
- [[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
+ [[nodiscard]] Result GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
- [[nodiscard]] ResultCode SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask);
+ [[nodiscard]] Result SetCoreMask(s32 cpu_core_id, u64 v_affinity_mask);
- [[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity);
+ [[nodiscard]] Result SetActivity(Svc::ThreadActivity activity);
- [[nodiscard]] ResultCode Sleep(s64 timeout);
+ [[nodiscard]] Result Sleep(s64 timeout);
[[nodiscard]] s64 GetYieldScheduleCount() const {
return schedule_count;
@@ -391,20 +411,22 @@ public:
static void PostDestroy(uintptr_t arg);
- [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
+ [[nodiscard]] static Result InitializeDummyThread(KThread* thread);
+
+ [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread,
+ s32 virt_core);
- [[nodiscard]] static ResultCode InitializeIdleThread(Core::System& system, KThread* thread,
- s32 virt_core);
+ [[nodiscard]] static Result InitializeIdleThread(Core::System& system, KThread* thread,
+ s32 virt_core);
- [[nodiscard]] static ResultCode InitializeHighPriorityThread(Core::System& system,
- KThread* thread,
- KThreadFunction func,
- uintptr_t arg, s32 virt_core);
+ [[nodiscard]] static Result InitializeHighPriorityThread(Core::System& system, KThread* thread,
+ KThreadFunction func, uintptr_t arg,
+ s32 virt_core);
- [[nodiscard]] static ResultCode InitializeUserThread(Core::System& system, KThread* thread,
- KThreadFunction func, uintptr_t arg,
- VAddr user_stack_top, s32 prio,
- s32 virt_core, KProcess* owner);
+ [[nodiscard]] static Result InitializeUserThread(Core::System& system, KThread* thread,
+ KThreadFunction func, uintptr_t arg,
+ VAddr user_stack_top, s32 prio, s32 virt_core,
+ KProcess* owner);
public:
struct StackParameters {
@@ -461,39 +483,16 @@ public:
return per_core_priority_queue_entry[core];
}
- [[nodiscard]] bool IsKernelThread() const {
- return GetActiveCore() == 3;
- }
-
- [[nodiscard]] bool IsDispatchTrackingDisabled() const {
- return is_single_core || IsKernelThread();
- }
-
[[nodiscard]] s32 GetDisableDispatchCount() const {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return 1;
- }
-
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return;
- }
-
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
- if (IsDispatchTrackingDisabled()) {
- // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
- return;
- }
-
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
@@ -590,7 +589,7 @@ public:
void RemoveWaiter(KThread* thread);
- [[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out);
+ [[nodiscard]] Result GetThreadContext3(std::vector<u8>& out);
[[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
@@ -616,9 +615,9 @@ public:
}
void BeginWait(KThreadQueue* queue);
- void NotifyAvailable(KSynchronizationObject* signaled_object, ResultCode wait_result_);
- void EndWait(ResultCode wait_result_);
- void CancelWait(ResultCode wait_result_, bool cancel_timer_task);
+ void NotifyAvailable(KSynchronizationObject* signaled_object, Result wait_result_);
+ void EndWait(Result wait_result_);
+ void CancelWait(Result wait_result_, bool cancel_timer_task);
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
@@ -641,9 +640,16 @@ public:
// blocking as needed.
void IfDummyThreadTryWait();
- void IfDummyThreadBeginWait();
void IfDummyThreadEndWait();
+ [[nodiscard]] uintptr_t GetArgument() const {
+ return argument;
+ }
+
+ [[nodiscard]] VAddr GetUserStackTop() const {
+ return stack_top;
+ }
+
private:
static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer {
@@ -656,7 +662,7 @@ private:
static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles));
struct ConditionVariableComparator {
- struct LightCompareType {
+ struct RedBlackKeyType {
u64 cv_key{};
s32 priority{};
@@ -672,8 +678,8 @@ private:
template <typename T>
requires(
std::same_as<T, KThread> ||
- std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
- const KThread& rhs) {
+ std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
+ const KThread& rhs) {
const u64 l_key = lhs.GetConditionVariableKey();
const u64 r_key = rhs.GetConditionVariableKey();
@@ -697,14 +703,13 @@ private:
void FinishTermination();
- [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
- s32 prio, s32 virt_core, KProcess* owner, ThreadType type);
+ [[nodiscard]] Result Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
+ s32 prio, s32 virt_core, KProcess* owner, ThreadType type);
- [[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func,
- uintptr_t arg, VAddr user_stack_top, s32 prio,
- s32 core, KProcess* owner, ThreadType type,
- std::function<void(void*)>&& init_func,
- void* init_func_parameter);
+ [[nodiscard]] static Result InitializeThread(KThread* thread, KThreadFunction func,
+ uintptr_t arg, VAddr user_stack_top, s32 prio,
+ s32 core, KProcess* owner, ThreadType type,
+ std::function<void()>&& init_func);
static void RestorePriority(KernelCore& kernel_ctx, KThread* thread);
@@ -741,7 +746,7 @@ private:
u32 suspend_request_flags{};
u32 suspend_allowed_flags{};
s32 synced_index{};
- ResultCode wait_result{ResultSuccess};
+ Result wait_result{ResultSuccess};
s32 base_priority{};
s32 physical_ideal_core_id{};
s32 virtual_ideal_core_id{};
@@ -751,7 +756,7 @@ private:
KAffinityMask original_physical_affinity_mask{};
s32 original_physical_ideal_core_id{};
s32 num_core_migration_disables{};
- ThreadState thread_state{};
+ std::atomic<ThreadState> thread_state{};
std::atomic<bool> termination_requested{};
bool wait_cancelled{};
bool cancellable{};
@@ -761,18 +766,22 @@ private:
s8 priority_inheritance_count{};
bool resource_limit_release_hint{};
StackParameters stack_parameters{};
- KSpinLock context_guard{};
- KSpinLock dummy_wait_lock{};
+ Common::SpinLock context_guard{};
// For emulation
std::shared_ptr<Common::Fiber> host_context{};
bool is_single_core{};
ThreadType thread_type{};
+ StepState step_state{};
+ std::mutex dummy_wait_lock;
+ std::condition_variable dummy_wait_cv;
// For debugging
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
VAddr mutex_wait_address_for_debugging{};
ThreadWaitReasonForDebugging wait_reason_for_debugging{};
+ uintptr_t argument;
+ VAddr stack_top;
public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
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..563560114
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_local_page.cpp
@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/scope_exit.h"
+#include "core/core.h"
+
+#include "core/hle/kernel/k_memory_block.h"
+#include "core/hle/kernel/k_page_buffer.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_thread_local_page.h"
+#include "core/hle/kernel/kernel.h"
+
+namespace Kernel {
+
+Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) {
+ // Set that this process owns us.
+ m_owner = process;
+ m_kernel = &kernel;
+
+ // Allocate a new page.
+ KPageBuffer* page_buf = KPageBuffer::Allocate(kernel);
+ R_UNLESS(page_buf != nullptr, ResultOutOfMemory);
+ auto page_buf_guard = SCOPE_GUARD({ KPageBuffer::Free(kernel, page_buf); });
+
+ // Map the address in.
+ const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf);
+ R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr,
+ KMemoryState::ThreadLocal,
+ KMemoryPermission::UserReadWrite));
+
+ // We succeeded.
+ page_buf_guard.Cancel();
+
+ return ResultSuccess;
+}
+
+Result KThreadLocalPage::Finalize() {
+ // Get the physical address of the page.
+ const PAddr phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr);
+ ASSERT(phys_addr);
+
+ // Unmap the page.
+ R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal));
+
+ // Free the page.
+ KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr));
+
+ return ResultSuccess;
+}
+
+VAddr KThreadLocalPage::Reserve() {
+ for (size_t i = 0; i < m_is_region_free.size(); i++) {
+ if (m_is_region_free[i]) {
+ m_is_region_free[i] = false;
+ return this->GetRegionAddress(i);
+ }
+ }
+
+ return 0;
+}
+
+void KThreadLocalPage::Release(VAddr addr) {
+ m_is_region_free[this->GetRegionIndex(addr)] = true;
+}
+
+} // 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..0a7f22680
--- /dev/null
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -0,0 +1,110 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <algorithm>
+#include <array>
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/intrusive_red_black_tree.h"
+#include "core/hle/kernel/memory_types.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+class KernelCore;
+class KProcess;
+
+class KThreadLocalPage final : public Common::IntrusiveRedBlackTreeBaseNode<KThreadLocalPage>,
+ public KSlabAllocated<KThreadLocalPage> {
+public:
+ static constexpr size_t RegionsPerPage = PageSize / Svc::ThreadLocalRegionSize;
+ static_assert(RegionsPerPage > 0);
+
+public:
+ constexpr explicit KThreadLocalPage(VAddr addr = {}) : m_virt_addr(addr) {
+ m_is_region_free.fill(true);
+ }
+
+ constexpr VAddr GetAddress() const {
+ return m_virt_addr;
+ }
+
+ Result Initialize(KernelCore& kernel, KProcess* process);
+ Result Finalize();
+
+ VAddr Reserve();
+ void Release(VAddr addr);
+
+ bool IsAllUsed() const {
+ return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
+ [](bool is_free) { return !is_free; });
+ }
+
+ bool IsAllFree() const {
+ return std::ranges::all_of(m_is_region_free.begin(), m_is_region_free.end(),
+ [](bool is_free) { return is_free; });
+ }
+
+ bool IsAnyUsed() const {
+ return !this->IsAllFree();
+ }
+
+ bool IsAnyFree() const {
+ return !this->IsAllUsed();
+ }
+
+public:
+ using RedBlackKeyType = VAddr;
+
+ static constexpr RedBlackKeyType GetRedBlackKey(const RedBlackKeyType& v) {
+ return v;
+ }
+ static constexpr RedBlackKeyType GetRedBlackKey(const KThreadLocalPage& v) {
+ return v.GetAddress();
+ }
+
+ template <typename T>
+ requires(std::same_as<T, KThreadLocalPage> ||
+ std::same_as<T, RedBlackKeyType>) static constexpr int Compare(const T& lhs,
+ const KThreadLocalPage&
+ rhs) {
+ const VAddr lval = GetRedBlackKey(lhs);
+ const VAddr rval = GetRedBlackKey(rhs);
+
+ if (lval < rval) {
+ return -1;
+ } else if (lval == rval) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+
+private:
+ constexpr VAddr GetRegionAddress(size_t i) const {
+ return this->GetAddress() + i * Svc::ThreadLocalRegionSize;
+ }
+
+ constexpr bool Contains(VAddr addr) const {
+ return this->GetAddress() <= addr && addr < this->GetAddress() + PageSize;
+ }
+
+ constexpr size_t GetRegionIndex(VAddr addr) const {
+ ASSERT(Common::IsAligned(addr, Svc::ThreadLocalRegionSize));
+ ASSERT(this->Contains(addr));
+ return (addr - this->GetAddress()) / Svc::ThreadLocalRegionSize;
+ }
+
+private:
+ VAddr m_virt_addr{};
+ KProcess* m_owner{};
+ KernelCore* m_kernel{};
+ std::array<bool, RegionsPerPage> m_is_region_free{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.cpp b/src/core/hle/kernel/k_thread_queue.cpp
index d5248b547..9f4e081ba 100644
--- a/src/core/hle/kernel/k_thread_queue.cpp
+++ b/src/core/hle/kernel/k_thread_queue.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_thread_queue.h"
#include "core/hle/kernel/kernel.h"
@@ -10,9 +9,9 @@ namespace Kernel {
void KThreadQueue::NotifyAvailable([[maybe_unused]] KThread* waiting_thread,
[[maybe_unused]] KSynchronizationObject* signaled_object,
- [[maybe_unused]] ResultCode wait_result) {}
+ [[maybe_unused]] Result wait_result) {}
-void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
+void KThreadQueue::EndWait(KThread* waiting_thread, Result wait_result) {
// Set the thread's wait result.
waiting_thread->SetWaitResult(wait_result);
@@ -26,8 +25,7 @@ void KThreadQueue::EndWait(KThread* waiting_thread, ResultCode wait_result) {
kernel.TimeManager().UnscheduleTimeEvent(waiting_thread);
}
-void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task) {
+void KThreadQueue::CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task) {
// Set the thread's wait result.
waiting_thread->SetWaitResult(wait_result);
@@ -44,6 +42,6 @@ void KThreadQueue::CancelWait(KThread* waiting_thread, ResultCode wait_result,
}
void KThreadQueueWithoutEndWait::EndWait([[maybe_unused]] KThread* waiting_thread,
- [[maybe_unused]] ResultCode wait_result) {}
+ [[maybe_unused]] Result wait_result) {}
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h
index ccb718e49..8d76ece81 100644
--- a/src/core/hle/kernel/k_thread_queue.h
+++ b/src/core/hle/kernel/k_thread_queue.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -15,10 +14,9 @@ public:
virtual ~KThreadQueue() = default;
virtual void NotifyAvailable(KThread* waiting_thread, KSynchronizationObject* signaled_object,
- ResultCode wait_result);
- virtual void EndWait(KThread* waiting_thread, ResultCode wait_result);
- virtual void CancelWait(KThread* waiting_thread, ResultCode wait_result,
- bool cancel_timer_task);
+ Result wait_result);
+ virtual void EndWait(KThread* waiting_thread, Result wait_result);
+ virtual void CancelWait(KThread* waiting_thread, Result wait_result, bool cancel_timer_task);
private:
KernelCore& kernel;
@@ -29,7 +27,7 @@ class KThreadQueueWithoutEndWait : public KThreadQueue {
public:
explicit KThreadQueueWithoutEndWait(KernelCore& kernel_) : KThreadQueue(kernel_) {}
- void EndWait(KThread* waiting_thread, ResultCode wait_result) override final;
+ void EndWait(KThread* waiting_thread, Result wait_result) override final;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_trace.h b/src/core/hle/kernel/k_trace.h
index d3fed1888..d61af4830 100644
--- a/src/core/hle/kernel/k_trace.h
+++ b/src/core/hle/kernel/k_trace.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index 1732925c9..b0320eb73 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -14,8 +13,8 @@ KTransferMemory::KTransferMemory(KernelCore& kernel_)
KTransferMemory::~KTransferMemory() = default;
-ResultCode KTransferMemory::Initialize(VAddr address_, std::size_t size_,
- Svc::MemoryPermission owner_perm_) {
+Result KTransferMemory::Initialize(VAddr address_, std::size_t size_,
+ Svc::MemoryPermission owner_perm_) {
// Set members.
owner = kernel.CurrentProcess();
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h
index cb7521823..85d508ee7 100644
--- a/src/core/hle/kernel/k_transfer_memory.h
+++ b/src/core/hle/kernel/k_transfer_memory.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,7 +7,7 @@
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
-union ResultCode;
+union Result;
namespace Core::Memory {
class Memory;
@@ -27,7 +26,7 @@ public:
explicit KTransferMemory(KernelCore& kernel_);
~KTransferMemory() override;
- ResultCode Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_);
+ Result Initialize(VAddr address_, std::size_t size_, Svc::MemoryPermission owner_perm_);
void Finalize() override;
diff --git a/src/core/hle/kernel/k_worker_task.h b/src/core/hle/kernel/k_worker_task.h
index b7794c6a8..ef591d831 100644
--- a/src/core/hle/kernel/k_worker_task.h
+++ b/src/core/hle/kernel/k_worker_task.h
@@ -1,6 +1,5 @@
-// Copyright 2022 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_worker_task_manager.cpp b/src/core/hle/kernel/k_worker_task_manager.cpp
index 785e08111..04042bf8f 100644
--- a/src/core/hle/kernel/k_worker_task_manager.cpp
+++ b/src/core/hle/kernel/k_worker_task_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2022 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/hle/kernel/k_process.h"
@@ -24,7 +23,7 @@ void KWorkerTask::DoWorkerTask() {
}
}
-KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "yuzu:KWorkerTaskManager") {}
+KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
ASSERT(type <= WorkerType::Count);
diff --git a/src/core/hle/kernel/k_worker_task_manager.h b/src/core/hle/kernel/k_worker_task_manager.h
index 43d1bfcec..f6618883e 100644
--- a/src/core/hle/kernel/k_worker_task_manager.h
+++ b/src/core/hle/kernel/k_worker_task_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2022 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_writable_event.cpp b/src/core/hle/kernel/k_writable_event.cpp
index bdb1db6d5..ff88c5acd 100644
--- a/src/core/hle/kernel/k_writable_event.cpp
+++ b/src/core/hle/kernel/k_writable_event.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
@@ -19,11 +18,11 @@ void KWritableEvent::Initialize(KEvent* parent_event_, std::string&& name_) {
parent->GetReadableEvent().Open();
}
-ResultCode KWritableEvent::Signal() {
+Result KWritableEvent::Signal() {
return parent->GetReadableEvent().Signal();
}
-ResultCode KWritableEvent::Clear() {
+Result KWritableEvent::Clear() {
return parent->GetReadableEvent().Clear();
}
diff --git a/src/core/hle/kernel/k_writable_event.h b/src/core/hle/kernel/k_writable_event.h
index 858d982c4..3fd0c7d0a 100644
--- a/src/core/hle/kernel/k_writable_event.h
+++ b/src/core/hle/kernel/k_writable_event.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -26,8 +25,8 @@ public:
static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
void Initialize(KEvent* parent_, std::string&& name_);
- ResultCode Signal();
- ResultCode Clear();
+ Result Signal();
+ Result Clear();
KEvent* GetParent() const {
return parent;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 49c0714ed..9251f29ad 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <atomic>
@@ -18,13 +17,10 @@
#include "common/thread.h"
#include "common/thread_worker.h"
#include "core/arm/arm_interface.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
-#include "core/core_timing_util.h"
#include "core/cpu_manager.h"
-#include "core/device_memory.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
#include "core/hle/kernel/k_client_port.h"
@@ -35,7 +31,6 @@
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_shared_memory.h"
-#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/k_worker_task_manager.h"
#include "core/hle/kernel/kernel.h"
@@ -52,42 +47,41 @@ namespace Kernel {
struct KernelCore::Impl {
explicit Impl(Core::System& system_, KernelCore& kernel_)
- : time_manager{system_}, object_list_container{kernel_},
- service_threads_manager{1, "yuzu:ServiceThreadsManager"}, system{system_} {}
+ : time_manager{system_},
+ service_threads_manager{1, "ServiceThreadsManager"}, system{system_} {}
void SetMulticore(bool is_multi) {
is_multicore = is_multi;
}
void Initialize(KernelCore& kernel) {
+ global_object_list_container = std::make_unique<KAutoObjectWithListContainer>(kernel);
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
global_handle_table = std::make_unique<Kernel::KHandleTable>(kernel);
global_handle_table->Initialize(KHandleTable::MaxTableSize);
+ default_service_thread = CreateServiceThread(kernel, "DefaultServiceThread");
is_phantom_mode_for_singlecore = false;
- InitializePhysicalCores();
-
// Derive the initial memory layout from the emulated board
Init::InitializeSlabResourceCounts(kernel);
- KMemoryLayout memory_layout;
- DeriveInitialMemoryLayout(memory_layout);
- Init::InitializeSlabHeaps(system, memory_layout);
+ DeriveInitialMemoryLayout();
+ Init::InitializeSlabHeaps(system, *memory_layout);
// Initialize kernel memory and resources.
- InitializeSystemResourceLimit(kernel, system.CoreTiming(), memory_layout);
- InitializeMemoryLayout(memory_layout);
- InitializePageSlab();
- InitializeSchedulers();
- InitializeSuspendThreads();
+ InitializeSystemResourceLimit(kernel, system.CoreTiming());
+ InitializeMemoryLayout();
+ Init::InitializeKPageBufferSlabHeap(system);
+ InitializeShutdownThreads();
InitializePreemption(kernel);
+ InitializePhysicalCores();
RegisterHostThread();
}
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- cores[core_id].Initialize(current_process->Is64BitProcess());
+ cores[core_id]->Initialize((*current_process).Is64BitProcess());
system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
@@ -98,39 +92,16 @@ struct KernelCore::Impl {
process_list.clear();
- // Close all open server ports.
- std::unordered_set<KServerPort*> server_ports_;
- {
- std::lock_guard lk(server_ports_lock);
- server_ports_ = server_ports;
- server_ports.clear();
- }
- for (auto* server_port : server_ports_) {
- server_port->Close();
- }
- // Close all open server sessions.
- std::unordered_set<KServerSession*> server_sessions_;
- {
- std::lock_guard lk(server_sessions_lock);
- server_sessions_ = server_sessions;
- server_sessions.clear();
- }
- for (auto* server_session : server_sessions_) {
- server_session->Close();
- }
-
- // Ensure that the object list container is finalized and properly shutdown.
- object_list_container.Finalize();
-
- // Ensures all service threads gracefully shutdown.
- ClearServiceThreads();
+ CloseServices();
next_object_id = 0;
next_kernel_process_id = KProcess::InitialKIPIDMin;
next_user_process_id = KProcess::ProcessIDMin;
next_thread_id = 1;
- cores.clear();
+ for (auto& core : cores) {
+ core = nullptr;
+ }
global_handle_table->Finalize();
global_handle_table.reset();
@@ -155,15 +126,15 @@ struct KernelCore::Impl {
CleanupObject(font_shared_mem);
CleanupObject(irs_shared_mem);
CleanupObject(time_shared_mem);
+ CleanupObject(hidbus_shared_mem);
CleanupObject(system_resource_limit);
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- if (suspend_threads[core_id]) {
- suspend_threads[core_id]->Close();
- suspend_threads[core_id] = nullptr;
+ if (shutdown_threads[core_id]) {
+ shutdown_threads[core_id]->Close();
+ shutdown_threads[core_id] = nullptr;
}
- schedulers[core_id]->Finalize();
schedulers[core_id].reset();
}
@@ -172,7 +143,7 @@ struct KernelCore::Impl {
// Close kernel objects that were not freed on shutdown
{
- std::lock_guard lk(registered_in_use_objects_lock);
+ std::scoped_lock lk{registered_in_use_objects_lock};
if (registered_in_use_objects.size()) {
for (auto& object : registered_in_use_objects) {
object->Close();
@@ -183,48 +154,76 @@ struct KernelCore::Impl {
// Shutdown all processes.
if (current_process) {
- current_process->Finalize();
+ (*current_process).Finalize();
// current_process->Close();
// TODO: The current process should be destroyed based on accurate ref counting after
// calling Close(). Adding a manual Destroy() call instead to avoid a memory leak.
- current_process->Destroy();
+ (*current_process).Destroy();
current_process = nullptr;
}
// Track kernel objects that were not freed on shutdown
{
- std::lock_guard lk(registered_objects_lock);
+ std::scoped_lock lk{registered_objects_lock};
if (registered_objects.size()) {
- LOG_WARNING(Kernel, "{} kernel objects were dangling on shutdown!",
- registered_objects.size());
+ LOG_DEBUG(Kernel, "{} kernel objects were dangling on shutdown!",
+ registered_objects.size());
registered_objects.clear();
}
}
+
+ // Ensure that the object list container is finalized and properly shutdown.
+ global_object_list_container->Finalize();
+ global_object_list_container.reset();
+ }
+
+ void CloseServices() {
+ // Close all open server sessions and ports.
+ std::unordered_set<KAutoObject*> server_objects_;
+ {
+ std::scoped_lock lk(server_objects_lock);
+ server_objects_ = server_objects;
+ server_objects.clear();
+ }
+ for (auto* server_object : server_objects_) {
+ server_object->Close();
+ }
+
+ // Ensures all service threads gracefully shutdown.
+ ClearServiceThreads();
}
void InitializePhysicalCores() {
exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES);
for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
- cores.emplace_back(i, system, *schedulers[i], interrupts);
- }
- }
+ const s32 core{static_cast<s32>(i)};
- void InitializeSchedulers() {
- for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
- cores[i].Scheduler().Initialize();
+ schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
+ cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]);
+
+ auto* main_thread{Kernel::KThread::Create(system.Kernel())};
+ main_thread->SetName(fmt::format("MainThread:{}", core));
+ main_thread->SetCurrentCore(core);
+ ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
+
+ auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
+ idle_thread->SetCurrentCore(core);
+ ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
+
+ schedulers[i]->Initialize(main_thread, idle_thread, core);
}
}
// Creates the default system resource limit
void InitializeSystemResourceLimit(KernelCore& kernel,
- const Core::Timing::CoreTiming& core_timing,
- const KMemoryLayout& memory_layout) {
+ const Core::Timing::CoreTiming& core_timing) {
system_resource_limit = KResourceLimit::Create(system.Kernel());
system_resource_limit->Initialize(&core_timing);
- const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes();
+ const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
+ const auto total_size{sizes.first};
+ const auto kernel_size{sizes.second};
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
@@ -240,37 +239,31 @@ struct KernelCore::Impl {
constexpr u64 secure_applet_memory_size{4_MiB};
ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
secure_applet_memory_size));
-
- // This memory seems to be reserved on hardware, but is not reserved/used by yuzu.
- // Likely Horizon OS reserved memory
- // TODO(ameerj): Derive the memory rather than hardcode it.
- constexpr u64 unknown_reserved_memory{0x2f896000};
- ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
- unknown_reserved_memory));
}
void InitializePreemption(KernelCore& kernel) {
preemption_event = Core::Timing::CreateEvent(
- "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) {
+ "PreemptionCallback",
+ [this, &kernel](std::uintptr_t, s64 time,
+ std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
{
KScopedSchedulerLock lock(kernel);
global_scheduler_context->PreemptThreads();
}
- const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
- system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
+ return std::nullopt;
});
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
- system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
+ system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
}
- void InitializeSuspendThreads() {
+ void InitializeShutdownThreads() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- suspend_threads[core_id] = KThread::Create(system.Kernel());
- ASSERT(KThread::InitializeHighPriorityThread(system, suspend_threads[core_id], {}, {},
+ shutdown_threads[core_id] = KThread::Create(system.Kernel());
+ ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {},
core_id)
.IsSuccess());
- suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
+ shutdown_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
}
}
@@ -300,15 +293,16 @@ struct KernelCore::Impl {
// Gets the dummy KThread for the caller, allocating a new one if this is the first time
KThread* GetHostDummyThread() {
- auto make_thread = [this]() {
- KThread* thread = KThread::Create(system.Kernel());
+ auto initialize = [this](KThread* thread) {
ASSERT(KThread::InitializeDummyThread(thread).IsSuccess());
thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId()));
return thread;
};
- thread_local KThread* saved_thread = make_thread();
- return saved_thread;
+ thread_local auto raw_thread = KThread(system.Kernel());
+ thread_local auto thread = initialize(&raw_thread);
+
+ return thread;
}
/// Registers a CPU core thread by allocating a host thread ID for it
@@ -347,6 +341,8 @@ struct KernelCore::Impl {
return is_shutting_down.load(std::memory_order_relaxed);
}
+ static inline thread_local KThread* current_thread{nullptr};
+
KThread* GetCurrentEmuThread() {
// If we are shutting down the kernel, none of this is relevant anymore.
if (IsShuttingDown()) {
@@ -357,19 +353,26 @@ struct KernelCore::Impl {
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
}
- return schedulers[thread_id]->GetCurrentThread();
+
+ return current_thread;
+ }
+
+ void SetCurrentEmuThread(KThread* thread) {
+ current_thread = thread;
}
- void DeriveInitialMemoryLayout(KMemoryLayout& memory_layout) {
+ void DeriveInitialMemoryLayout() {
+ memory_layout = std::make_unique<KMemoryLayout>();
+
// Insert the root region for the virtual memory tree, from which all other regions will
// derive.
- memory_layout.GetVirtualMemoryRegionTree().InsertDirectly(
+ memory_layout->GetVirtualMemoryRegionTree().InsertDirectly(
KernelVirtualAddressSpaceBase,
KernelVirtualAddressSpaceBase + KernelVirtualAddressSpaceSize - 1);
// Insert the root region for the physical memory tree, from which all other regions will
// derive.
- memory_layout.GetPhysicalMemoryRegionTree().InsertDirectly(
+ memory_layout->GetPhysicalMemoryRegionTree().InsertDirectly(
KernelPhysicalAddressSpaceBase,
KernelPhysicalAddressSpaceBase + KernelPhysicalAddressSpaceSize - 1);
@@ -386,7 +389,7 @@ struct KernelCore::Impl {
if (!(kernel_region_start + KernelRegionSize - 1 <= KernelVirtualAddressSpaceLast)) {
kernel_region_size = KernelVirtualAddressSpaceEnd - kernel_region_start;
}
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
kernel_region_start, kernel_region_size, KMemoryRegionType_Kernel));
// Setup the code region.
@@ -395,11 +398,11 @@ struct KernelCore::Impl {
Common::AlignDown(code_start_virt_addr, CodeRegionAlign);
constexpr VAddr code_region_end = Common::AlignUp(code_end_virt_addr, CodeRegionAlign);
constexpr size_t code_region_size = code_region_end - code_region_start;
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
code_region_start, code_region_size, KMemoryRegionType_KernelCode));
// Setup board-specific device physical regions.
- Init::SetupDevicePhysicalMemoryRegions(memory_layout);
+ Init::SetupDevicePhysicalMemoryRegions(*memory_layout);
// Determine the amount of space needed for the misc region.
size_t misc_region_needed_size;
@@ -408,7 +411,7 @@ struct KernelCore::Impl {
misc_region_needed_size = Core::Hardware::NUM_CPU_CORES * (3 * (PageSize + PageSize));
// Account for each auto-map device.
- for (const auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
+ for (const auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
if (region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) {
// Check that the region is valid.
ASSERT(region.GetEndAddress() != 0);
@@ -433,22 +436,22 @@ struct KernelCore::Impl {
// Setup the misc region.
const VAddr misc_region_start =
- memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
+ memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
misc_region_size, MiscRegionAlign, KMemoryRegionType_Kernel);
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
misc_region_start, misc_region_size, KMemoryRegionType_KernelMisc));
// Setup the stack region.
constexpr size_t StackRegionSize = 14_MiB;
constexpr size_t StackRegionAlign = KernelAslrAlignment;
const VAddr stack_region_start =
- memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
+ memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel);
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
stack_region_start, StackRegionSize, KMemoryRegionType_KernelStack));
// Determine the size of the resource region.
- const size_t resource_region_size = memory_layout.GetResourceRegionSizeForInit();
+ const size_t resource_region_size = memory_layout->GetResourceRegionSizeForInit();
// Determine the size of the slab region.
const size_t slab_region_size =
@@ -465,23 +468,23 @@ struct KernelCore::Impl {
Common::AlignUp(code_end_phys_addr + slab_region_size, SlabRegionAlign) -
Common::AlignDown(code_end_phys_addr, SlabRegionAlign);
const VAddr slab_region_start =
- memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
+ memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
slab_region_needed_size, SlabRegionAlign, KMemoryRegionType_Kernel) +
(code_end_phys_addr % SlabRegionAlign);
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
slab_region_start, slab_region_size, KMemoryRegionType_KernelSlab));
// Setup the temp region.
constexpr size_t TempRegionSize = 128_MiB;
constexpr size_t TempRegionAlign = KernelAslrAlignment;
const VAddr temp_region_start =
- memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
+ memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegion(
TempRegionSize, TempRegionAlign, KMemoryRegionType_Kernel);
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize,
- KMemoryRegionType_KernelTemp));
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(temp_region_start, TempRegionSize,
+ KMemoryRegionType_KernelTemp));
// Automatically map in devices that have auto-map attributes.
- for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
+ for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
// We only care about kernel regions.
if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) {
continue;
@@ -508,21 +511,21 @@ struct KernelCore::Impl {
const size_t map_size =
Common::AlignUp(region.GetEndAddress(), PageSize) - map_phys_addr;
const VAddr map_virt_addr =
- memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
+ memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
map_size, PageSize, KMemoryRegionType_KernelMisc, PageSize);
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
map_virt_addr, map_size, KMemoryRegionType_KernelMiscMappedDevice));
region.SetPairAddress(map_virt_addr + region.GetAddress() - map_phys_addr);
}
- Init::SetupDramPhysicalMemoryRegions(memory_layout);
+ Init::SetupDramPhysicalMemoryRegions(*memory_layout);
// Insert a physical region for the kernel code region.
- ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
code_start_phys_addr, code_region_size, KMemoryRegionType_DramKernelCode));
// Insert a physical region for the kernel slab region.
- ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
slab_start_phys_addr, slab_region_size, KMemoryRegionType_DramKernelSlab));
// Determine size available for kernel page table heaps, requiring > 8 MB.
@@ -531,12 +534,12 @@ struct KernelCore::Impl {
ASSERT(page_table_heap_size / 4_MiB > 2);
// Insert a physical region for the kernel page table heap region
- ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
slab_end_phys_addr, page_table_heap_size, KMemoryRegionType_DramKernelPtHeap));
// All DRAM regions that we haven't tagged by this point will be mapped under the linear
// mapping. Tag them.
- for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
+ for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
if (region.GetType() == KMemoryRegionType_Dram) {
// Check that the region is valid.
ASSERT(region.GetEndAddress() != 0);
@@ -548,7 +551,7 @@ struct KernelCore::Impl {
// Get the linear region extents.
const auto linear_extents =
- memory_layout.GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
+ memory_layout->GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(
KMemoryRegionAttr_LinearMapped);
ASSERT(linear_extents.GetEndAddress() != 0);
@@ -560,7 +563,7 @@ struct KernelCore::Impl {
Common::AlignUp(linear_extents.GetEndAddress(), LinearRegionAlign) -
aligned_linear_phys_start;
const VAddr linear_region_start =
- memory_layout.GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
+ memory_layout->GetVirtualMemoryRegionTree().GetRandomAlignedRegionWithGuard(
linear_region_size, LinearRegionAlign, KMemoryRegionType_None, LinearRegionAlign);
const u64 linear_region_phys_to_virt_diff = linear_region_start - aligned_linear_phys_start;
@@ -569,7 +572,7 @@ struct KernelCore::Impl {
{
PAddr cur_phys_addr = 0;
u64 cur_size = 0;
- for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
+ for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) {
continue;
}
@@ -588,55 +591,49 @@ struct KernelCore::Impl {
const VAddr region_virt_addr =
region.GetAddress() + linear_region_phys_to_virt_diff;
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
region_virt_addr, region.GetSize(),
GetTypeForVirtualLinearMapping(region.GetType())));
region.SetPairAddress(region_virt_addr);
KMemoryRegion* virt_region =
- memory_layout.GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr);
+ memory_layout->GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr);
ASSERT(virt_region != nullptr);
virt_region->SetPairAddress(region.GetAddress());
}
}
// Insert regions for the initial page table region.
- ASSERT(memory_layout.GetPhysicalMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
resource_end_phys_addr, KernelPageTableHeapSize, KMemoryRegionType_DramKernelInitPt));
- ASSERT(memory_layout.GetVirtualMemoryRegionTree().Insert(
+ ASSERT(memory_layout->GetVirtualMemoryRegionTree().Insert(
resource_end_phys_addr + linear_region_phys_to_virt_diff, KernelPageTableHeapSize,
KMemoryRegionType_VirtualDramKernelInitPt));
// All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to
// some pool partition. Tag them.
- for (auto& region : memory_layout.GetPhysicalMemoryRegionTree()) {
+ for (auto& region : memory_layout->GetPhysicalMemoryRegionTree()) {
if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) {
region.SetType(KMemoryRegionType_DramPoolPartition);
}
}
// Setup all other memory regions needed to arrange the pool partitions.
- Init::SetupPoolPartitionMemoryRegions(memory_layout);
+ Init::SetupPoolPartitionMemoryRegions(*memory_layout);
// Cache all linear regions in their own trees for faster access, later.
- memory_layout.InitializeLinearMemoryRegionTrees(aligned_linear_phys_start,
- linear_region_start);
+ memory_layout->InitializeLinearMemoryRegionTrees(aligned_linear_phys_start,
+ linear_region_start);
}
- void InitializeMemoryLayout(const KMemoryLayout& memory_layout) {
- const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents();
- const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents();
- const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents();
+ void InitializeMemoryLayout() {
+ const auto system_pool = memory_layout->GetKernelSystemPoolRegionPhysicalExtents();
- // Initialize memory managers
+ // Initialize the memory manager.
memory_manager = std::make_unique<KMemoryManager>(system);
- memory_manager->InitializeManager(KMemoryManager::Pool::Application,
- application_pool.GetAddress(),
- application_pool.GetEndAddress());
- memory_manager->InitializeManager(KMemoryManager::Pool::Applet, applet_pool.GetAddress(),
- applet_pool.GetEndAddress());
- memory_manager->InitializeManager(KMemoryManager::Pool::System, system_pool.GetAddress(),
- system_pool.GetEndAddress());
+ const auto& management_region = memory_layout->GetPoolManagementRegion();
+ ASSERT(management_region.GetEndAddress() != 0);
+ memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
// Setup memory regions for emulated processes
// TODO(bunnei): These should not be hardcoded regions initialized within the kernel
@@ -644,16 +641,20 @@ struct KernelCore::Impl {
constexpr std::size_t font_size{0x1100000};
constexpr std::size_t irs_size{0x8000};
constexpr std::size_t time_size{0x1000};
+ constexpr std::size_t hidbus_size{0x1000};
const PAddr hid_phys_addr{system_pool.GetAddress()};
const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
+ const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size +
+ time_size};
hid_shared_mem = KSharedMemory::Create(system.Kernel());
font_shared_mem = KSharedMemory::Create(system.Kernel());
irs_shared_mem = KSharedMemory::Create(system.Kernel());
time_shared_mem = KSharedMemory::Create(system.Kernel());
+ hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
hid_shared_mem->Initialize(system.DeviceMemory(), nullptr,
{hid_phys_addr, hid_size / PageSize},
@@ -671,22 +672,10 @@ struct KernelCore::Impl {
{time_phys_addr, time_size / PageSize},
Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
time_phys_addr, time_size, "Time:SharedMemory");
- }
-
- void InitializePageSlab() {
- // Allocate slab heaps
- user_slab_heap_pages =
- std::make_unique<KSlabHeap<Page>>(KSlabHeap<Page>::AllocationType::Guest);
-
- // TODO(ameerj): This should be derived, not hardcoded within the kernel
- constexpr u64 user_slab_heap_size{0x3de000};
- // Reserve slab heaps
- ASSERT(
- system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
- // Initialize slab heap
- user_slab_heap_pages->Initialize(
- system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase),
- user_slab_heap_size);
+ hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr,
+ {hidbus_phys_addr, hidbus_size / PageSize},
+ Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
+ hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");
}
KClientPort* CreateNamedServicePort(std::string name) {
@@ -697,13 +686,20 @@ struct KernelCore::Impl {
}
KClientPort* port = &search->second(system.ServiceManager(), system);
- {
- std::lock_guard lk(server_ports_lock);
- server_ports.insert(&port->GetParent()->GetServerPort());
- }
+ RegisterServerObject(&port->GetParent()->GetServerPort());
return port;
}
+ void RegisterServerObject(KAutoObject* server_object) {
+ std::scoped_lock lk(server_objects_lock);
+ server_objects.insert(server_object);
+ }
+
+ void UnregisterServerObject(KAutoObject* server_object) {
+ std::scoped_lock lk(server_objects_lock);
+ server_objects.erase(server_object);
+ }
+
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(KernelCore& kernel,
const std::string& name) {
auto service_thread = std::make_shared<Kernel::ServiceThread>(kernel, 1, name);
@@ -716,6 +712,12 @@ struct KernelCore::Impl {
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
if (auto strong_ptr = service_thread.lock()) {
+ if (strong_ptr == default_service_thread.lock()) {
+ // Nothing to do here, the service is using default_service_thread, which will be
+ // released on shutdown.
+ return;
+ }
+
service_threads_manager.QueueWork(
[this, strong_ptr{std::move(strong_ptr)}]() { service_threads.erase(strong_ptr); });
}
@@ -725,8 +727,7 @@ struct KernelCore::Impl {
service_threads_manager.QueueWork([this]() { service_threads.clear(); });
}
- std::mutex server_ports_lock;
- std::mutex server_sessions_lock;
+ std::mutex server_objects_lock;
std::mutex registered_objects_lock;
std::mutex registered_in_use_objects_lock;
@@ -737,7 +738,7 @@ struct KernelCore::Impl {
// Lists all processes that exist in the current session.
std::vector<KProcess*> process_list;
- KProcess* current_process{};
+ std::atomic<KProcess*> current_process{};
std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
Kernel::TimeManager time_manager;
@@ -750,39 +751,41 @@ struct KernelCore::Impl {
// stores all the objects in place.
std::unique_ptr<KHandleTable> global_handle_table;
- KAutoObjectWithListContainer object_list_container;
+ std::unique_ptr<KAutoObjectWithListContainer> global_object_list_container;
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
NamedPortTable named_ports;
- std::unordered_set<KServerPort*> server_ports;
- std::unordered_set<KServerSession*> server_sessions;
+ std::unordered_set<KAutoObject*> server_objects;
std::unordered_set<KAutoObject*> registered_objects;
std::unordered_set<KAutoObject*> registered_in_use_objects;
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
- std::vector<Kernel::PhysicalCore> cores;
+ std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
// Kernel memory management
std::unique_ptr<KMemoryManager> memory_manager;
- std::unique_ptr<KSlabHeap<Page>> user_slab_heap_pages;
// Shared memory for services
Kernel::KSharedMemory* hid_shared_mem{};
Kernel::KSharedMemory* font_shared_mem{};
Kernel::KSharedMemory* irs_shared_mem{};
Kernel::KSharedMemory* time_shared_mem{};
+ Kernel::KSharedMemory* hidbus_shared_mem{};
+
+ // Memory layout
+ std::unique_ptr<KMemoryLayout> memory_layout;
// Threads used for services
- std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
+ std::unordered_set<std::shared_ptr<ServiceThread>> service_threads;
+ std::weak_ptr<ServiceThread> default_service_thread;
Common::ThreadWorker service_threads_manager;
- std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
- std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
+ std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{};
@@ -818,6 +821,10 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
+void KernelCore::CloseServices() {
+ impl->CloseServices();
+}
+
const KResourceLimit* KernelCore::GetSystemResourceLimit() const {
return impl->system_resource_limit;
}
@@ -867,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const {
}
Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
- return impl->cores[id];
+ return *impl->cores[id];
}
const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
- return impl->cores[id];
+ return *impl->cores[id];
}
size_t KernelCore::CurrentPhysicalCoreIndex() const {
@@ -883,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const {
}
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
- return impl->cores[CurrentPhysicalCoreIndex()];
+ return *impl->cores[CurrentPhysicalCoreIndex()];
}
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
- return impl->cores[CurrentPhysicalCoreIndex()];
+ return *impl->cores[CurrentPhysicalCoreIndex()];
}
Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -899,15 +906,6 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
return impl->schedulers[core_id].get();
}
-std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() {
- return impl->interrupts;
-}
-
-const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts()
- const {
- return impl->interrupts;
-}
-
Kernel::TimeManager& KernelCore::TimeManager() {
return impl->time_manager;
}
@@ -925,25 +923,25 @@ const Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() const {
}
KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
- return impl->object_list_container;
+ return *impl->global_object_list_container;
}
const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
- return impl->object_list_container;
+ return *impl->global_object_list_container;
}
void KernelCore::InvalidateAllInstructionCaches() {
for (auto& physical_core : impl->cores) {
- physical_core.ArmInterface().ClearInstructionCache();
+ physical_core->ArmInterface().ClearInstructionCache();
}
}
void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) {
for (auto& physical_core : impl->cores) {
- if (!physical_core.IsInitialized()) {
+ if (!physical_core->IsInitialized()) {
continue;
}
- physical_core.ArmInterface().InvalidateCacheRange(addr, size);
+ physical_core->ArmInterface().InvalidateCacheRange(addr, size);
}
}
@@ -959,33 +957,31 @@ KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
return impl->CreateNamedServicePort(std::move(name));
}
-void KernelCore::RegisterServerSession(KServerSession* server_session) {
- std::lock_guard lk(impl->server_sessions_lock);
- impl->server_sessions.insert(server_session);
+void KernelCore::RegisterServerObject(KAutoObject* server_object) {
+ impl->RegisterServerObject(server_object);
}
-void KernelCore::UnregisterServerSession(KServerSession* server_session) {
- std::lock_guard lk(impl->server_sessions_lock);
- impl->server_sessions.erase(server_session);
+void KernelCore::UnregisterServerObject(KAutoObject* server_object) {
+ impl->UnregisterServerObject(server_object);
}
void KernelCore::RegisterKernelObject(KAutoObject* object) {
- std::lock_guard lk(impl->registered_objects_lock);
+ std::scoped_lock lk{impl->registered_objects_lock};
impl->registered_objects.insert(object);
}
void KernelCore::UnregisterKernelObject(KAutoObject* object) {
- std::lock_guard lk(impl->registered_objects_lock);
+ std::scoped_lock lk{impl->registered_objects_lock};
impl->registered_objects.erase(object);
}
void KernelCore::RegisterInUseObject(KAutoObject* object) {
- std::lock_guard lk(impl->registered_in_use_objects_lock);
+ std::scoped_lock lk{impl->registered_in_use_objects_lock};
impl->registered_in_use_objects.insert(object);
}
void KernelCore::UnregisterInUseObject(KAutoObject* object) {
- std::lock_guard lk(impl->registered_in_use_objects_lock);
+ std::scoped_lock lk{impl->registered_in_use_objects_lock};
impl->registered_in_use_objects.erase(object);
}
@@ -1033,6 +1029,10 @@ KThread* KernelCore::GetCurrentEmuThread() const {
return impl->GetCurrentEmuThread();
}
+void KernelCore::SetCurrentEmuThread(KThread* thread) {
+ impl->SetCurrentEmuThread(thread);
+}
+
KMemoryManager& KernelCore::MemoryManager() {
return *impl->memory_manager;
}
@@ -1041,14 +1041,6 @@ const KMemoryManager& KernelCore::MemoryManager() const {
return *impl->memory_manager;
}
-KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() {
- return *impl->user_slab_heap_pages;
-}
-
-const KSlabHeap<Page>& KernelCore::GetUserSlabHeapPages() const {
- return *impl->user_slab_heap_pages;
-}
-
Kernel::KSharedMemory& KernelCore::GetHidSharedMem() {
return *impl->hid_shared_mem;
}
@@ -1081,22 +1073,38 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
return *impl->time_shared_mem;
}
-void KernelCore::Suspend(bool in_suspention) {
- const bool should_suspend = exception_exited || in_suspention;
- {
- KScopedSchedulerLock lock(*this);
- const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting;
- for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- impl->suspend_threads[core_id]->SetState(state);
- impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
- ThreadWaitReasonForDebugging::Suspended);
- if (!should_suspend) {
- impl->suspend_threads[core_id]->DisableDispatch();
+Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() {
+ return *impl->hidbus_shared_mem;
+}
+
+const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
+ return *impl->hidbus_shared_mem;
+}
+
+void KernelCore::Suspend(bool suspended) {
+ const bool should_suspend{exception_exited || suspended};
+ const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
+
+ for (auto* process : GetProcessList()) {
+ process->SetActivity(activity);
+
+ if (should_suspend) {
+ // Wait for execution to stop
+ for (auto* thread : process->GetThreadList()) {
+ thread->WaitUntilSuspended();
}
}
}
}
+void KernelCore::ShutdownCores() {
+ KScopedSchedulerLock lk{*this};
+
+ for (auto* thread : impl->shutdown_threads) {
+ void(thread->Run());
+ }
+}
+
bool KernelCore::IsMulticore() const {
return impl->is_multicore;
}
@@ -1122,6 +1130,10 @@ std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::
return impl->CreateServiceThread(*this, name);
}
+std::weak_ptr<Kernel::ServiceThread> KernelCore::GetDefaultServiceThread() const {
+ return impl->default_service_thread;
+}
+
void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
impl->ReleaseServiceThread(service_thread);
}
@@ -1142,6 +1154,10 @@ const KWorkerTaskManager& KernelCore::WorkerTaskManager() const {
return impl->worker_task_manager;
}
+const KMemoryLayout& KernelCore::MemoryLayout() const {
+ return *impl->memory_layout;
+}
+
bool KernelCore::IsPhantomModeForSingleCore() const {
return impl->IsPhantomModeForSingleCore();
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 0e04fc3bb..bcf016a97 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,15 +9,12 @@
#include <string>
#include <unordered_map>
#include <vector>
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_slab_heap.h"
-#include "core/hle/kernel/memory_types.h"
#include "core/hle/kernel/svc_common.h"
namespace Core {
-class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -41,7 +37,9 @@ class KClientSession;
class KEvent;
class KHandleTable;
class KLinkedListNode;
+class KMemoryLayout;
class KMemoryManager;
+class KPageBuffer;
class KPort;
class KProcess;
class KResourceLimit;
@@ -51,6 +49,7 @@ class KSession;
class KSharedMemory;
class KSharedMemoryInfo;
class KThread;
+class KThreadLocalPage;
class KTransferMemory;
class KWorkerTaskManager;
class KWritableEvent;
@@ -108,6 +107,9 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
+ /// Close all active services in use by the kernel instance.
+ void CloseServices();
+
/// Retrieves a shared pointer to the system resource limit instance.
const KResourceLimit* GetSystemResourceLimit() const;
@@ -179,10 +181,6 @@ public:
const KAutoObjectWithListContainer& ObjectListContainer() const;
- std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts();
-
- const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
-
void InvalidateAllInstructionCaches();
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
@@ -193,13 +191,13 @@ public:
/// Opens a port to a service previously registered with RegisterNamedService.
KClientPort* CreateNamedServicePort(std::string name);
- /// Registers a server session with the gobal emulation state, to be freed on shutdown. This is
- /// necessary because we do not emulate processes for HLE sessions.
- void RegisterServerSession(KServerSession* server_session);
+ /// Registers a server session or port with the gobal emulation state, to be freed on shutdown.
+ /// This is necessary because we do not emulate processes for HLE sessions and ports.
+ void RegisterServerObject(KAutoObject* server_object);
- /// Unregisters a server session previously registered with RegisterServerSession when it was
- /// destroyed during the current emulation session.
- void UnregisterServerSession(KServerSession* server_session);
+ /// Unregisters a server session or port previously registered with RegisterServerSession when
+ /// it was destroyed during the current emulation session.
+ void UnregisterServerObject(KAutoObject* server_object);
/// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown.
@@ -223,6 +221,9 @@ public:
/// Gets the current host_thread/guest_thread pointer.
KThread* GetCurrentEmuThread() const;
+ /// Sets the current guest_thread pointer.
+ void SetCurrentEmuThread(KThread* thread);
+
/// Gets the current host_thread handle.
u32 GetCurrentHostThreadID() const;
@@ -238,12 +239,6 @@ public:
/// Gets the virtual memory manager for the kernel.
const KMemoryManager& MemoryManager() const;
- /// Gets the slab heap allocated for user space pages.
- KSlabHeap<Page>& GetUserSlabHeapPages();
-
- /// Gets the slab heap allocated for user space pages.
- const KSlabHeap<Page>& GetUserSlabHeapPages() const;
-
/// Gets the shared memory object for HID services.
Kernel::KSharedMemory& GetHidSharedMem();
@@ -268,12 +263,21 @@ public:
/// Gets the shared memory object for Time services.
const Kernel::KSharedMemory& GetTimeSharedMem() const;
- /// Suspend/unsuspend the OS.
- void Suspend(bool in_suspention);
+ /// Gets the shared memory object for HIDBus services.
+ Kernel::KSharedMemory& GetHidBusSharedMem();
- /// Exceptional exit the OS.
+ /// Gets the shared memory object for HIDBus services.
+ const Kernel::KSharedMemory& GetHidBusSharedMem() const;
+
+ /// Suspend/unsuspend all processes.
+ void Suspend(bool suspend);
+
+ /// Exceptional exit all processes.
void ExceptionalExit();
+ /// Notify emulated CPU cores to shut down.
+ void ShutdownCores();
+
bool IsMulticore() const;
bool IsShuttingDown() const;
@@ -283,9 +287,11 @@ public:
void ExitSVCProfile();
/**
- * Creates an HLE service thread, which are used to execute service routines asynchronously.
- * While these are allocated per ServerSession, these need to be owned and managed outside
- * of ServerSession to avoid a circular dependency.
+ * Creates a host thread to execute HLE service requests, which are used to execute service
+ * routines asynchronously. While these are allocated per ServerSession, these need to be owned
+ * and managed outside of ServerSession to avoid a circular dependency. In general, most
+ * services can just use the default service thread, and not need their own host service thread.
+ * See GetDefaultServiceThread.
* @param name String name for the ServerSession creating this thread, used for debug
* purposes.
* @returns The a weak pointer newly created service thread.
@@ -293,6 +299,14 @@ public:
std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
/**
+ * Gets the default host service thread, which executes HLE service requests. Unless service
+ * requests need to block on the host, the default service thread should be used in favor of
+ * creating a new service thread.
+ * @returns The a weak pointer for the default service thread.
+ */
+ std::weak_ptr<Kernel::ServiceThread> GetDefaultServiceThread() const;
+
+ /**
* Releases a HLE service thread, instructing KernelCore to free it. This should be called when
* the ServerSession associated with the thread is destroyed.
* @param service_thread Service thread to release.
@@ -335,6 +349,10 @@ public:
return slab_heap_container->writeable_event;
} else if constexpr (std::is_same_v<T, KCodeMemory>) {
return slab_heap_container->code_memory;
+ } else if constexpr (std::is_same_v<T, KPageBuffer>) {
+ return slab_heap_container->page_buffer;
+ } else if constexpr (std::is_same_v<T, KThreadLocalPage>) {
+ return slab_heap_container->thread_local_page;
}
}
@@ -350,6 +368,9 @@ public:
/// Gets the current worker task manager, used for dispatching KThread/KProcess tasks.
const KWorkerTaskManager& WorkerTaskManager() const;
+ /// Gets the memory layout.
+ const KMemoryLayout& MemoryLayout() const;
+
private:
friend class KProcess;
friend class KThread;
@@ -393,6 +414,8 @@ private:
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KWritableEvent> writeable_event;
KSlabHeap<KCodeMemory> code_memory;
+ KSlabHeap<KPageBuffer> page_buffer;
+ KSlabHeap<KThreadLocalPage> thread_local_page;
};
std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/memory_types.h b/src/core/hle/kernel/memory_types.h
index d458f0eca..3975507bd 100644
--- a/src/core/hle/kernel/memory_types.h
+++ b/src/core/hle/kernel/memory_types.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 7477668e4..d4375962f 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -1,9 +1,6 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
-#include "common/spin_lock.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/core.h"
@@ -13,16 +10,14 @@
namespace Kernel {
-PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
- Core::CPUInterrupts& interrupts_)
- : core_index{core_index_}, system{system_}, scheduler{scheduler_},
- interrupts{interrupts_}, guard{std::make_unique<Common::SpinLock>()} {
+PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
+ : core_index{core_index_}, system{system_}, scheduler{scheduler_} {
#ifdef ARCHITECTURE_x86_64
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
auto& kernel = system.Kernel();
arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
- system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
+ system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
#else
#error Platform not supported yet.
#endif
@@ -36,7 +31,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
if (!is_64_bit) {
// We already initialized a 64-bit core, replace with a 32-bit one.
arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
- system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
+ system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
}
#else
#error Platform not supported yet.
@@ -45,26 +40,30 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
void PhysicalCore::Run() {
arm_interface->Run();
+ arm_interface->ClearExclusiveState();
}
void PhysicalCore::Idle() {
- interrupts[core_index].AwaitInterrupt();
+ std::unique_lock lk{guard};
+ on_interrupt.wait(lk, [this] { return is_interrupted; });
}
bool PhysicalCore::IsInterrupted() const {
- return interrupts[core_index].IsInterrupted();
+ return is_interrupted;
}
void PhysicalCore::Interrupt() {
- guard->lock();
- interrupts[core_index].SetInterrupt(true);
- guard->unlock();
+ std::unique_lock lk{guard};
+ is_interrupted = true;
+ arm_interface->SignalInterrupt();
+ on_interrupt.notify_all();
}
void PhysicalCore::ClearInterrupt() {
- guard->lock();
- interrupts[core_index].SetInterrupt(false);
- guard->unlock();
+ std::unique_lock lk{guard};
+ is_interrupted = false;
+ arm_interface->ClearInterrupt();
+ on_interrupt.notify_all();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 16a032e89..2fc8d4be2 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -1,24 +1,19 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include <memory>
+#include <mutex>
#include "core/arm/arm_interface.h"
-namespace Common {
-class SpinLock;
-}
-
namespace Kernel {
class KScheduler;
} // namespace Kernel
namespace Core {
-class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -27,15 +22,11 @@ namespace Kernel {
class PhysicalCore {
public:
- PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
- Core::CPUInterrupts& interrupts_);
+ PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_);
~PhysicalCore();
- PhysicalCore(const PhysicalCore&) = delete;
- PhysicalCore& operator=(const PhysicalCore&) = delete;
-
- PhysicalCore(PhysicalCore&&) = default;
- PhysicalCore& operator=(PhysicalCore&&) = delete;
+ YUZU_NON_COPYABLE(PhysicalCore);
+ YUZU_NON_MOVEABLE(PhysicalCore);
/// Initialize the core for the specified parameters.
void Initialize(bool is_64_bit);
@@ -90,9 +81,11 @@ private:
const std::size_t core_index;
Core::System& system;
Kernel::KScheduler& scheduler;
- Core::CPUInterrupts& interrupts;
- std::unique_ptr<Common::SpinLock> guard;
+
+ std::mutex guard;
+ std::condition_variable on_interrupt;
std::unique_ptr<Core::ARM_Interface> arm_interface;
+ bool is_interrupted;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h
index 7a0266780..253fa4563 100644
--- a/src/core/hle/kernel/physical_memory.h
+++ b/src/core/hle/kernel/physical_memory.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
index 31a0867d3..773319ad8 100644
--- a/src/core/hle/kernel/process_capability.cpp
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <bit>
@@ -69,9 +68,9 @@ u32 GetFlagBitOffset(CapabilityType type) {
} // Anonymous namespace
-ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
- std::size_t num_capabilities,
- KPageTable& page_table) {
+Result ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
+ std::size_t num_capabilities,
+ KPageTable& page_table) {
Clear();
// Allow all cores and priorities.
@@ -82,9 +81,9 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti
return ParseCapabilities(capabilities, num_capabilities, page_table);
}
-ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
- std::size_t num_capabilities,
- KPageTable& page_table) {
+Result ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
+ std::size_t num_capabilities,
+ KPageTable& page_table) {
Clear();
return ParseCapabilities(capabilities, num_capabilities, page_table);
@@ -108,9 +107,8 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
can_force_debug = true;
}
-ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
- std::size_t num_capabilities,
- KPageTable& page_table) {
+Result ProcessCapabilities::ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
+ KPageTable& page_table) {
u32 set_flags = 0;
u32 set_svc_bits = 0;
@@ -156,8 +154,8 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
return ResultSuccess;
}
-ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
- u32 flag, KPageTable& page_table) {
+Result ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
+ KPageTable& page_table) {
const auto type = GetCapabilityType(flag);
if (type == CapabilityType::Unset) {
@@ -225,7 +223,7 @@ void ProcessCapabilities::Clear() {
can_force_debug = false;
}
-ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
+Result ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
if (priority_mask != 0 || core_mask != 0) {
LOG_ERROR(Kernel, "Core or priority mask are not zero! priority_mask={}, core_mask={}",
priority_mask, core_mask);
@@ -267,7 +265,7 @@ ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
+Result ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
const u32 index = flags >> 29;
const u32 svc_bit = 1U << index;
@@ -291,23 +289,23 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
- KPageTable& page_table) {
+Result ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
+ KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
+Result ProcessCapabilities::HandleMapIOFlags(u32 flags, KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) {
+Result ProcessCapabilities::HandleMapRegionFlags(u32 flags, KPageTable& page_table) {
// TODO(Lioncache): Implement once the memory manager can handle this.
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
+Result ProcessCapabilities::HandleInterruptFlags(u32 flags) {
constexpr u32 interrupt_ignore_value = 0x3FF;
const u32 interrupt0 = (flags >> 12) & 0x3FF;
const u32 interrupt1 = (flags >> 22) & 0x3FF;
@@ -334,7 +332,7 @@ ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
+Result ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
const u32 reserved = flags >> 17;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
@@ -345,7 +343,7 @@ ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
+Result ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
// Yes, the internal member variable is checked in the actual kernel here.
// This might look odd for options that are only allowed to be initialized
// just once, however the kernel has a separate initialization function for
@@ -365,7 +363,7 @@ ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
+Result ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
const u32 reserved = flags >> 26;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
@@ -376,7 +374,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
return ResultSuccess;
}
-ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
+Result ProcessCapabilities::HandleDebugFlags(u32 flags) {
const u32 reserved = flags >> 19;
if (reserved != 0) {
LOG_ERROR(Kernel, "Reserved value is non-zero! reserved={}", reserved);
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
index a9b44325b..ff05dc5ff 100644
--- a/src/core/hle/kernel/process_capability.h
+++ b/src/core/hle/kernel/process_capability.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,7 +7,7 @@
#include "common/common_types.h"
-union ResultCode;
+union Result;
namespace Kernel {
@@ -87,8 +86,8 @@ public:
/// @returns ResultSuccess if this capabilities instance was able to be initialized,
/// otherwise, an error code upon failure.
///
- ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
- KPageTable& page_table);
+ Result InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
+ KPageTable& page_table);
/// Initializes this process capabilities instance for a userland process.
///
@@ -100,8 +99,8 @@ public:
/// @returns ResultSuccess if this capabilities instance was able to be initialized,
/// otherwise, an error code upon failure.
///
- ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
- KPageTable& page_table);
+ Result InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
+ KPageTable& page_table);
/// Initializes this process capabilities instance for a process that does not
/// have any metadata to parse.
@@ -186,8 +185,8 @@ private:
///
/// @return ResultSuccess if no errors occur, otherwise an error code.
///
- ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
- KPageTable& page_table);
+ Result ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
+ KPageTable& page_table);
/// Attempts to parse a capability descriptor that is only represented by a
/// single flag set.
@@ -201,8 +200,8 @@ private:
///
/// @return ResultSuccess if no errors occurred, otherwise an error code.
///
- ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
- KPageTable& page_table);
+ Result ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
+ KPageTable& page_table);
/// Clears the internal state of this process capability instance. Necessary,
/// to have a sane starting point due to us allowing running executables without
@@ -220,34 +219,34 @@ private:
void Clear();
/// Handles flags related to the priority and core number capability flags.
- ResultCode HandlePriorityCoreNumFlags(u32 flags);
+ Result HandlePriorityCoreNumFlags(u32 flags);
/// Handles flags related to determining the allowable SVC mask.
- ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
+ Result HandleSyscallFlags(u32& set_svc_bits, u32 flags);
/// Handles flags related to mapping physical memory pages.
- ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
+ Result HandleMapPhysicalFlags(u32 flags, u32 size_flags, KPageTable& page_table);
/// Handles flags related to mapping IO pages.
- ResultCode HandleMapIOFlags(u32 flags, KPageTable& page_table);
+ Result HandleMapIOFlags(u32 flags, KPageTable& page_table);
/// Handles flags related to mapping physical memory regions.
- ResultCode HandleMapRegionFlags(u32 flags, KPageTable& page_table);
+ Result HandleMapRegionFlags(u32 flags, KPageTable& page_table);
/// Handles flags related to the interrupt capability flags.
- ResultCode HandleInterruptFlags(u32 flags);
+ Result HandleInterruptFlags(u32 flags);
/// Handles flags related to the program type.
- ResultCode HandleProgramTypeFlags(u32 flags);
+ Result HandleProgramTypeFlags(u32 flags);
/// Handles flags related to the handle table size.
- ResultCode HandleHandleTableFlags(u32 flags);
+ Result HandleHandleTableFlags(u32 flags);
/// Handles flags related to the kernel version capability flags.
- ResultCode HandleKernelVersionFlags(u32 flags);
+ Result HandleKernelVersionFlags(u32 flags);
/// Handles flags related to debug-specific capabilities.
- ResultCode HandleDebugFlags(u32 flags);
+ Result HandleDebugFlags(u32 flags);
SyscallCapabilities svc_capabilities;
InterruptCapabilities interrupt_capabilities;
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index 4eb3a5988..d23d76706 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <condition_variable>
#include <functional>
@@ -37,7 +36,7 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
: service_name{name} {
for (std::size_t i = 0; i < num_threads; ++i) {
threads.emplace_back([this, &kernel](std::stop_token stop_token) {
- Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
+ Common::SetCurrentThreadName(std::string{service_name}.c_str());
// Wait for first request before trying to acquire a render context
{
@@ -49,12 +48,9 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std
return;
}
+ // Allocate a dummy guest thread for this host thread.
kernel.RegisterHostThread();
- // Ensure the dummy thread allocated for this host thread is closed on exit.
- auto* dummy_thread = kernel.GetCurrentEmuThread();
- SCOPE_EXIT({ dummy_thread->Close(); });
-
while (true) {
std::function<void()> task;
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
index 6a7fd7c56..c5896f2bd 100644
--- a/src/core/hle/kernel/service_thread.h
+++ b/src/core/hle/kernel/service_thread.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/slab_helpers.h b/src/core/hle/kernel/slab_helpers.h
index f1c11256e..299a981a8 100644
--- a/src/core/hle/kernel/slab_helpers.h
+++ b/src/core/hle/kernel/slab_helpers.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -59,7 +58,7 @@ class KAutoObjectWithSlabHeapAndContainer : public Base {
private:
static Derived* Allocate(KernelCore& kernel) {
- return kernel.SlabHeap<Derived>().AllocateWithKernel(kernel);
+ return kernel.SlabHeap<Derived>().Allocate(kernel);
}
static void Free(KernelCore& kernel, Derived* obj) {
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 9387373c1..27e5a805d 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cinttypes>
@@ -16,6 +15,7 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/debugger/debugger.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
#include "core/hle/kernel/k_code_memory.h"
@@ -58,8 +58,8 @@ constexpr bool IsValidAddressRange(VAddr address, u64 size) {
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
-ResultCode MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
- u64 size) {
+Result MapUnmapMemorySanityChecks(const KPageTable& manager, VAddr dst_addr, VAddr src_addr,
+ u64 size) {
if (!Common::Is4KBAligned(dst_addr)) {
LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
return ResultInvalidAddress;
@@ -135,7 +135,7 @@ enum class ResourceLimitValueType {
} // Anonymous namespace
/// Set the process heap to a given Size. It can both extend and shrink the heap.
-static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
+static Result SetHeapSize(Core::System& system, VAddr* out_address, u64 size) {
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", size);
// Validate size.
@@ -148,9 +148,9 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* out_address, u64 size
return ResultSuccess;
}
-static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
+static Result SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_size) {
VAddr temp_heap_addr{};
- const ResultCode result{SetHeapSize(system, &temp_heap_addr, heap_size)};
+ const Result result{SetHeapSize(system, &temp_heap_addr, heap_size)};
*heap_addr = static_cast<u32>(temp_heap_addr);
return result;
}
@@ -166,8 +166,8 @@ constexpr bool IsValidSetMemoryPermission(MemoryPermission perm) {
}
}
-static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 size,
- MemoryPermission perm) {
+static Result SetMemoryPermission(Core::System& system, VAddr address, u64 size,
+ MemoryPermission perm) {
LOG_DEBUG(Kernel_SVC, "called, address=0x{:016X}, size=0x{:X}, perm=0x{:08X", address, size,
perm);
@@ -188,8 +188,8 @@ static ResultCode SetMemoryPermission(Core::System& system, VAddr address, u64 s
return page_table.SetMemoryPermission(address, size, perm);
}
-static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
- u32 attr) {
+static Result SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,
+ u32 attr) {
LOG_DEBUG(Kernel_SVC,
"called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
size, mask, attr);
@@ -213,19 +213,19 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si
return page_table.SetMemoryAttribute(address, size, mask, attr);
}
-static ResultCode SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
- u32 attr) {
+static Result SetMemoryAttribute32(Core::System& system, u32 address, u32 size, u32 mask,
+ u32 attr) {
return SetMemoryAttribute(system, address, size, mask, attr);
}
/// Maps a memory range into a different range.
-static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
+static Result MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
- if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
+ if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
result.IsError()) {
return result;
}
@@ -233,18 +233,18 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr
return page_table.MapMemory(dst_addr, src_addr, size);
}
-static ResultCode MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
+static Result MapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
return MapMemory(system, dst_addr, src_addr, size);
}
/// Unmaps a region that was previously mapped with svcMapMemory
-static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
+static Result UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
auto& page_table{system.Kernel().CurrentProcess()->PageTable()};
- if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
+ if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)};
result.IsError()) {
return result;
}
@@ -252,12 +252,12 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
return page_table.UnmapMemory(dst_addr, src_addr, size);
}
-static ResultCode UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
+static Result UnmapMemory32(Core::System& system, u32 dst_addr, u32 src_addr, u32 size) {
return UnmapMemory(system, dst_addr, src_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
-static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
+static Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
auto& memory = system.Memory();
if (!memory.IsValidVirtualAddress(port_name_address)) {
LOG_ERROR(Kernel_SVC,
@@ -307,14 +307,14 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out, VAddr po
return ResultSuccess;
}
-static ResultCode ConnectToNamedPort32(Core::System& system, Handle* out_handle,
- u32 port_name_address) {
+static Result ConnectToNamedPort32(Core::System& system, Handle* out_handle,
+ u32 port_name_address) {
return ConnectToNamedPort(system, out_handle, port_name_address);
}
/// Makes a blocking IPC call to an OS service.
-static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
+static Result SendSyncRequest(Core::System& system, Handle handle) {
auto& kernel = system.Kernel();
// Create the wait queue.
@@ -327,7 +327,6 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName());
- auto thread = kernel.CurrentScheduler()->GetCurrentThread();
{
KScopedSchedulerLock lock(kernel);
@@ -337,15 +336,15 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
session->SendSyncRequest(&GetCurrentThread(kernel), system.Memory(), system.CoreTiming());
}
- return thread->GetWaitResult();
+ return GetCurrentThread(kernel).GetWaitResult();
}
-static ResultCode SendSyncRequest32(Core::System& system, Handle handle) {
+static Result SendSyncRequest32(Core::System& system, Handle handle) {
return SendSyncRequest(system, handle);
}
/// Get the ID for the specified thread.
-static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
+static Result GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) {
// Get the thread from its handle.
KScopedAutoObject thread =
system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KThread>(thread_handle);
@@ -356,10 +355,10 @@ static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle t
return ResultSuccess;
}
-static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
- u32* out_thread_id_high, Handle thread_handle) {
+static Result GetThreadId32(Core::System& system, u32* out_thread_id_low, u32* out_thread_id_high,
+ Handle thread_handle) {
u64 out_thread_id{};
- const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)};
+ const Result result{GetThreadId(system, &out_thread_id, thread_handle)};
*out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
*out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
@@ -368,7 +367,7 @@ static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
}
/// Gets the ID of the specified process or a specified thread's owning process.
-static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
+static Result GetProcessId(Core::System& system, u64* out_process_id, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
// Get the object from the handle table.
@@ -399,8 +398,8 @@ static ResultCode GetProcessId(Core::System& system, u64* out_process_id, Handle
return ResultSuccess;
}
-static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
- u32* out_process_id_high, Handle handle) {
+static Result GetProcessId32(Core::System& system, u32* out_process_id_low,
+ u32* out_process_id_high, Handle handle) {
u64 out_process_id{};
const auto result = GetProcessId(system, &out_process_id, handle);
*out_process_id_low = static_cast<u32>(out_process_id);
@@ -409,8 +408,8 @@ static ResultCode GetProcessId32(Core::System& system, u32* out_process_id_low,
}
/// Wait for the given handles to synchronize, timeout after the specified nanoseconds
-static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
- s32 num_handles, s64 nano_seconds) {
+static Result WaitSynchronization(Core::System& system, s32* index, VAddr handles_address,
+ s32 num_handles, s64 nano_seconds) {
LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, num_handles={}, nano_seconds={}",
handles_address, num_handles, nano_seconds);
@@ -445,14 +444,14 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha
nano_seconds);
}
-static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
- s32 num_handles, u32 timeout_high, s32* index) {
+static Result WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address,
+ s32 num_handles, u32 timeout_high, s32* index) {
const s64 nano_seconds{(static_cast<s64>(timeout_high) << 32) | static_cast<s64>(timeout_low)};
return WaitSynchronization(system, index, handles_address, num_handles, nano_seconds);
}
/// Resumes a thread waiting on WaitSynchronization
-static ResultCode CancelSynchronization(Core::System& system, Handle handle) {
+static Result CancelSynchronization(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "called handle=0x{:X}", handle);
// Get the thread from its handle.
@@ -465,13 +464,12 @@ static ResultCode CancelSynchronization(Core::System& system, Handle handle) {
return ResultSuccess;
}
-static ResultCode CancelSynchronization32(Core::System& system, Handle handle) {
+static Result CancelSynchronization32(Core::System& system, Handle handle) {
return CancelSynchronization(system, handle);
}
/// Attempts to locks a mutex
-static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address,
- u32 tag) {
+static Result ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, u32 tag) {
LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}",
thread_handle, address, tag);
@@ -489,13 +487,12 @@ static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAdd
return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag);
}
-static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address,
- u32 tag) {
+static Result ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, u32 tag) {
return ArbitrateLock(system, thread_handle, address, tag);
}
/// Unlock a mutex
-static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
+static Result ArbitrateUnlock(Core::System& system, VAddr address) {
LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address);
// Validate the input address.
@@ -513,7 +510,7 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) {
return system.Kernel().CurrentProcess()->SignalToAddress(address);
}
-static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) {
+static Result ArbitrateUnlock32(Core::System& system, u32 address) {
return ArbitrateUnlock(system, address);
}
@@ -624,10 +621,16 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
handle_debug_buffer(info1, info2);
- auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
+ auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const auto thread_processor_id = current_thread->GetActiveCore();
system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace();
}
+
+ if (system.DebuggerEnabled()) {
+ auto* thread = system.Kernel().GetCurrentEmuThread();
+ system.GetDebugger().NotifyThreadStopped(thread);
+ thread->RequestSuspend(Kernel::SuspendType::Debug);
+ }
}
static void Break32(Core::System& system, u32 reason, u32 info1, u32 info2) {
@@ -645,9 +648,13 @@ static void OutputDebugString(Core::System& system, VAddr address, u64 len) {
LOG_DEBUG(Debug_Emulated, "{}", str);
}
+static void OutputDebugString32(Core::System& system, u32 address, u32 len) {
+ OutputDebugString(system, address, len);
+}
+
/// Gets system/memory information for the current process
-static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
- u64 info_sub_id) {
+static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle handle,
+ u64 info_sub_id) {
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
@@ -682,6 +689,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
// 6.0.0+
TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
TotalPhysicalMemoryUsedWithoutSystemResource = 22,
+
+ // Homebrew only
+ MesosphereCurrentProcess = 65001,
};
const auto info_id_type = static_cast<GetInfoType>(info_id);
@@ -874,10 +884,10 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
const auto& core_timing = system.CoreTiming();
const auto& scheduler = *system.Kernel().CurrentScheduler();
- const auto* const current_thread = scheduler.GetCurrentThread();
+ const auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
const bool same_thread = current_thread == thread.GetPointerUnsafe();
- const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
+ const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTime();
u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetCpuTime();
@@ -896,7 +906,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
// Verify the requested core is valid.
const bool core_valid =
- (info_sub_id == static_cast<u64>(-1ULL)) ||
+ (info_sub_id == 0xFFFFFFFFFFFFFFFF) ||
(info_sub_id == static_cast<u64>(system.Kernel().CurrentPhysicalCoreIndex()));
R_UNLESS(core_valid, ResultInvalidCombination);
@@ -904,18 +914,39 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
*result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime();
return ResultSuccess;
}
+ case GetInfoType::MesosphereCurrentProcess: {
+ // Verify the input handle is invalid.
+ R_UNLESS(handle == InvalidHandle, ResultInvalidHandle);
+
+ // Verify the sub-type is valid.
+ R_UNLESS(info_sub_id == 0, ResultInvalidCombination);
+
+ // Get the handle table.
+ KProcess* current_process = system.Kernel().CurrentProcess();
+ KHandleTable& handle_table = current_process->GetHandleTable();
+
+ // Get a new handle for the current process.
+ Handle tmp;
+ R_TRY(handle_table.Add(&tmp, current_process));
+
+ // Set the output.
+ *result = tmp;
+
+ // We succeeded.
+ return ResultSuccess;
+ }
default:
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
return ResultInvalidEnumValue;
}
}
-static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
- u32 info_id, u32 handle, u32 sub_id_high) {
+static Result GetInfo32(Core::System& system, u32* result_low, u32* result_high, u32 sub_id_low,
+ u32 info_id, u32 handle, u32 sub_id_high) {
const u64 sub_id{u64{sub_id_low} | (u64{sub_id_high} << 32)};
u64 res_value{};
- const ResultCode result{GetInfo(system, &res_value, info_id, handle, sub_id)};
+ const Result result{GetInfo(system, &res_value, info_id, handle, sub_id)};
*result_high = static_cast<u32>(res_value >> 32);
*result_low = static_cast<u32>(res_value & std::numeric_limits<u32>::max());
@@ -923,7 +954,7 @@ static ResultCode GetInfo32(Core::System& system, u32* result_low, u32* result_h
}
/// Maps memory at a desired address
-static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+static Result MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
@@ -971,12 +1002,12 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)
return page_table.MapPhysicalMemory(addr, size);
}
-static ResultCode MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
+static Result MapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
return MapPhysicalMemory(system, addr, size);
}
/// Unmaps memory previously mapped via MapPhysicalMemory
-static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+static Result UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
if (!Common::Is4KBAligned(addr)) {
@@ -1024,13 +1055,13 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size
return page_table.UnmapPhysicalMemory(addr, size);
}
-static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
+static Result UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size) {
return UnmapPhysicalMemory(system, addr, size);
}
/// Sets the thread activity
-static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
- ThreadActivity thread_activity) {
+static Result SetThreadActivity(Core::System& system, Handle thread_handle,
+ ThreadActivity thread_activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
thread_activity);
@@ -1055,13 +1086,13 @@ static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
return ResultSuccess;
}
-static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle,
- Svc::ThreadActivity thread_activity) {
+static Result SetThreadActivity32(Core::System& system, Handle thread_handle,
+ Svc::ThreadActivity thread_activity) {
return SetThreadActivity(system, thread_handle, thread_activity);
}
/// Gets the thread context
-static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
+static Result GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context,
thread_handle);
@@ -1093,7 +1124,7 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
if (thread->GetRawState() != ThreadState::Runnable) {
bool current = false;
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
- if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
+ if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetSchedulerCurrentThread()) {
current = true;
break;
}
@@ -1118,12 +1149,12 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
return ResultSuccess;
}
-static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
+static Result GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
return GetThreadContext(system, out_context, thread_handle);
}
/// Gets the priority for the specified thread
-static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
+static Result GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) {
LOG_TRACE(Kernel_SVC, "called");
// Get the thread from its handle.
@@ -1136,12 +1167,12 @@ static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Han
return ResultSuccess;
}
-static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
+static Result GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
return GetThreadPriority(system, out_priority, handle);
}
/// Sets the priority for the specified thread
-static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
+static Result SetThreadPriority(Core::System& system, Handle thread_handle, u32 priority) {
// Get the current process.
KProcess& process = *system.Kernel().CurrentProcess();
@@ -1159,7 +1190,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle thread_handle,
return ResultSuccess;
}
-static ResultCode SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
+static Result SetThreadPriority32(Core::System& system, Handle thread_handle, u32 priority) {
return SetThreadPriority(system, thread_handle, priority);
}
@@ -1219,8 +1250,8 @@ constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission p
} // Anonymous namespace
-static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
- u64 size, Svc::MemoryPermission map_perm) {
+static Result MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address, u64 size,
+ Svc::MemoryPermission map_perm) {
LOG_TRACE(Kernel_SVC,
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
shmem_handle, address, size, map_perm);
@@ -1260,13 +1291,13 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAd
return ResultSuccess;
}
-static ResultCode MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
- u32 size, Svc::MemoryPermission map_perm) {
+static Result MapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address, u32 size,
+ Svc::MemoryPermission map_perm) {
return MapSharedMemory(system, shmem_handle, address, size, map_perm);
}
-static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
- u64 size) {
+static Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
+ u64 size) {
// Validate the address/size.
R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
@@ -1293,13 +1324,13 @@ static ResultCode UnmapSharedMemory(Core::System& system, Handle shmem_handle, V
return ResultSuccess;
}
-static ResultCode UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
- u32 size) {
+static Result UnmapSharedMemory32(Core::System& system, Handle shmem_handle, u32 address,
+ u32 size) {
return UnmapSharedMemory(system, shmem_handle, address, size);
}
-static ResultCode SetProcessMemoryPermission(Core::System& system, Handle process_handle,
- VAddr address, u64 size, Svc::MemoryPermission perm) {
+static Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, VAddr address,
+ u64 size, Svc::MemoryPermission perm) {
LOG_TRACE(Kernel_SVC,
"called, process_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
process_handle, address, size, perm);
@@ -1328,8 +1359,8 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
return page_table.SetProcessMemoryPermission(address, size, perm);
}
-static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
- VAddr src_address, u64 size) {
+static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
LOG_TRACE(Kernel_SVC,
"called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
dst_address, process_handle, src_address, size);
@@ -1358,8 +1389,11 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
ResultInvalidMemoryRegion);
// Create a new page group.
- KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
- KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
+ KPageGroup pg;
+ R_TRY(src_pt.MakeAndOpenPageGroup(
+ std::addressof(pg), src_address, size / PageSize, KMemoryState::FlagCanMapProcess,
+ KMemoryState::FlagCanMapProcess, KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::All, KMemoryAttribute::None));
// Map the group.
R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
@@ -1368,8 +1402,8 @@ static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Hand
return ResultSuccess;
}
-static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
- VAddr src_address, u64 size) {
+static Result UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
LOG_TRACE(Kernel_SVC,
"called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
dst_address, process_handle, src_address, size);
@@ -1403,9 +1437,9 @@ static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Ha
return ResultSuccess;
}
-static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
- LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
- static_cast<void*>(out), address, size);
+static Result CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, size=0x{:X}", address, size);
+
// Get kernel instance.
auto& kernel = system.Kernel();
@@ -1438,8 +1472,12 @@ static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr addr
return ResultSuccess;
}
-static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
- VAddr address, size_t size, Svc::MemoryPermission perm) {
+static Result CreateCodeMemory32(Core::System& system, Handle* out, u32 address, u32 size) {
+ return CreateCodeMemory(system, out, address, size);
+}
+
+static Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
+ VAddr address, size_t size, Svc::MemoryPermission perm) {
LOG_TRACE(Kernel_SVC,
"called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
@@ -1517,9 +1555,13 @@ static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_han
return ResultSuccess;
}
-static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
- VAddr page_info_address, Handle process_handle,
- VAddr address) {
+static Result ControlCodeMemory32(Core::System& system, Handle code_memory_handle, u32 operation,
+ u64 address, u64 size, Svc::MemoryPermission perm) {
+ return ControlCodeMemory(system, code_memory_handle, operation, address, size, perm);
+}
+
+static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address,
+ VAddr page_info_address, Handle process_handle, VAddr address) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle);
@@ -1547,8 +1589,8 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add
return ResultSuccess;
}
-static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
- VAddr page_info_address, VAddr query_address) {
+static Result QueryMemory(Core::System& system, VAddr memory_info_address, VAddr page_info_address,
+ VAddr query_address) {
LOG_TRACE(Kernel_SVC,
"called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
"query_address=0x{:016X}",
@@ -1558,13 +1600,13 @@ static ResultCode QueryMemory(Core::System& system, VAddr memory_info_address,
query_address);
}
-static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,
- u32 page_info_address, u32 query_address) {
+static Result QueryMemory32(Core::System& system, u32 memory_info_address, u32 page_info_address,
+ u32 query_address) {
return QueryMemory(system, memory_info_address, page_info_address, query_address);
}
-static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
- u64 src_address, u64 size) {
+static Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
+ u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, "
"src_address=0x{:016X}, size=0x{:016X}",
@@ -1631,8 +1673,8 @@ static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_hand
return page_table.MapCodeMemory(dst_address, src_address, size);
}
-static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle,
- u64 dst_address, u64 src_address, u64 size) {
+static Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address,
+ u64 src_address, u64 size) {
LOG_DEBUG(Kernel_SVC,
"called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, "
"size=0x{:016X}",
@@ -1650,7 +1692,7 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ResultInvalidAddress;
}
- if (size == 0 || Common::Is4KBAligned(size)) {
+ if (size == 0 || !Common::Is4KBAligned(size)) {
LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size);
return ResultInvalidSize;
}
@@ -1696,17 +1738,19 @@ static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_ha
return ResultInvalidMemoryRegion;
}
- return page_table.UnmapCodeMemory(dst_address, src_address, size);
+ return page_table.UnmapCodeMemory(dst_address, src_address, size,
+ KPageTable::ICacheInvalidationStrategy::InvalidateAll);
}
/// Exits the current process
static void ExitProcess(Core::System& system) {
auto* current_process = system.Kernel().CurrentProcess();
- UNIMPLEMENTED();
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
"Process has already exited");
+
+ system.Exit();
}
static void ExitProcess32(Core::System& system) {
@@ -1722,8 +1766,8 @@ constexpr bool IsValidVirtualCoreId(int32_t core_id) {
} // Anonymous namespace
/// Creates a new thread
-static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
- VAddr stack_bottom, u32 priority, s32 core_id) {
+static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg,
+ VAddr stack_bottom, u32 priority, s32 core_id) {
LOG_DEBUG(Kernel_SVC,
"called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
"priority=0x{:08X}, core_id=0x{:08X}",
@@ -1794,13 +1838,13 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
return ResultSuccess;
}
-static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
- u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
+static Result CreateThread32(Core::System& system, Handle* out_handle, u32 priority,
+ u32 entry_point, u32 arg, u32 stack_top, s32 processor_id) {
return CreateThread(system, out_handle, entry_point, arg, stack_top, priority, processor_id);
}
/// Starts the thread for the provided handle
-static ResultCode StartThread(Core::System& system, Handle thread_handle) {
+static Result StartThread(Core::System& system, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
// Get the thread from its handle.
@@ -1818,7 +1862,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
return ResultSuccess;
}
-static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
+static Result StartThread32(Core::System& system, Handle thread_handle) {
return StartThread(system, thread_handle);
}
@@ -1826,7 +1870,7 @@ static ResultCode StartThread32(Core::System& system, Handle thread_handle) {
static void ExitThread(Core::System& system) {
LOG_DEBUG(Kernel_SVC, "called, pc=0x{:08X}", system.CurrentArmInterface().GetPC());
- auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
+ auto* const current_thread = GetCurrentThreadPointer(system.Kernel());
system.GlobalSchedulerContext().RemoveThread(current_thread);
current_thread->Exit();
system.Kernel().UnregisterInUseObject(current_thread);
@@ -1859,7 +1903,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
KScheduler::YieldToAnyThread(kernel);
} else {
// Nintendo does nothing at all if an otherwise invalid value is passed.
- UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
+ ASSERT_MSG(false, "Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
}
@@ -1869,8 +1913,8 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec
}
/// Wait process wide key atomic
-static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key,
- u32 tag, s64 timeout_ns) {
+static Result WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, u32 tag,
+ s64 timeout_ns) {
LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address,
cv_key, tag, timeout_ns);
@@ -1905,8 +1949,8 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address,
address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout);
}
-static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
- u32 timeout_ns_low, u32 timeout_ns_high) {
+static Result WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag,
+ u32 timeout_ns_low, u32 timeout_ns_high) {
const auto timeout_ns = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns);
}
@@ -1951,8 +1995,8 @@ constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) {
} // namespace
// Wait for an address (via Address Arbiter)
-static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
- s32 value, s64 timeout_ns) {
+static Result WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type,
+ s32 value, s64 timeout_ns) {
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}",
address, arb_type, value, timeout_ns);
@@ -1989,15 +2033,15 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::Arbit
return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout);
}
-static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
- s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
+static Result WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type,
+ s32 value, u32 timeout_ns_low, u32 timeout_ns_high) {
const auto timeout = static_cast<s64>(timeout_ns_low | (u64{timeout_ns_high} << 32));
return WaitForAddress(system, address, arb_type, value, timeout);
}
// Signals to an address (via Address Arbiter)
-static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
- s32 value, s32 count) {
+static Result SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type,
+ s32 value, s32 count) {
LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}",
address, signal_type, value, count);
@@ -2038,8 +2082,8 @@ static void SynchronizePreemptionState(Core::System& system) {
}
}
-static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
- s32 value, s32 count) {
+static Result SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type,
+ s32 value, s32 count) {
return SignalToAddress(system, address, signal_type, value, count);
}
@@ -2077,7 +2121,7 @@ static void GetSystemTick32(Core::System& system, u32* time_low, u32* time_high)
}
/// Close a handle
-static ResultCode CloseHandle(Core::System& system, Handle handle) {
+static Result CloseHandle(Core::System& system, Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
// Remove the handle.
@@ -2087,12 +2131,12 @@ static ResultCode CloseHandle(Core::System& system, Handle handle) {
return ResultSuccess;
}
-static ResultCode CloseHandle32(Core::System& system, Handle handle) {
+static Result CloseHandle32(Core::System& system, Handle handle) {
return CloseHandle(system, handle);
}
/// Clears the signaled state of an event or process.
-static ResultCode ResetSignal(Core::System& system, Handle handle) {
+static Result ResetSignal(Core::System& system, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
// Get the current handle table.
@@ -2119,7 +2163,7 @@ static ResultCode ResetSignal(Core::System& system, Handle handle) {
return ResultInvalidHandle;
}
-static ResultCode ResetSignal32(Core::System& system, Handle handle) {
+static Result ResetSignal32(Core::System& system, Handle handle) {
return ResetSignal(system, handle);
}
@@ -2139,8 +2183,8 @@ constexpr bool IsValidTransferMemoryPermission(MemoryPermission perm) {
} // Anonymous namespace
/// Creates a TransferMemory object
-static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
- MemoryPermission map_perm) {
+static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr address, u64 size,
+ MemoryPermission map_perm) {
auto& kernel = system.Kernel();
// Validate the size.
@@ -2186,13 +2230,13 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* out, VAddr
return ResultSuccess;
}
-static ResultCode CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
- MemoryPermission map_perm) {
+static Result CreateTransferMemory32(Core::System& system, Handle* out, u32 address, u32 size,
+ MemoryPermission map_perm) {
return CreateTransferMemory(system, out, address, size, map_perm);
}
-static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
- u64* out_affinity_mask) {
+static Result GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
+ u64* out_affinity_mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
// Get the thread from its handle.
@@ -2206,8 +2250,8 @@ static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle,
return ResultSuccess;
}
-static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
- u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
+static Result GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
+ u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
u64 out_affinity_mask{};
const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
*out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
@@ -2215,8 +2259,8 @@ static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle
return result;
}
-static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
- u64 affinity_mask) {
+static Result SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id,
+ u64 affinity_mask) {
// Determine the core id/affinity mask.
if (core_id == IdealCoreUseProcessValue) {
core_id = system.Kernel().CurrentProcess()->GetIdealCoreId();
@@ -2247,13 +2291,13 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
return ResultSuccess;
}
-static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
- u32 affinity_mask_low, u32 affinity_mask_high) {
+static Result SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
+ u32 affinity_mask_low, u32 affinity_mask_high) {
const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32);
return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask);
}
-static ResultCode SignalEvent(Core::System& system, Handle event_handle) {
+static Result SignalEvent(Core::System& system, Handle event_handle) {
LOG_DEBUG(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
// Get the current handle table.
@@ -2266,11 +2310,11 @@ static ResultCode SignalEvent(Core::System& system, Handle event_handle) {
return writable_event->Signal();
}
-static ResultCode SignalEvent32(Core::System& system, Handle event_handle) {
+static Result SignalEvent32(Core::System& system, Handle event_handle) {
return SignalEvent(system, event_handle);
}
-static ResultCode ClearEvent(Core::System& system, Handle event_handle) {
+static Result ClearEvent(Core::System& system, Handle event_handle) {
LOG_TRACE(Kernel_SVC, "called, event_handle=0x{:08X}", event_handle);
// Get the current handle table.
@@ -2297,11 +2341,11 @@ static ResultCode ClearEvent(Core::System& system, Handle event_handle) {
return ResultInvalidHandle;
}
-static ResultCode ClearEvent32(Core::System& system, Handle event_handle) {
+static Result ClearEvent32(Core::System& system, Handle event_handle) {
return ClearEvent(system, event_handle);
}
-static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
+static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_read) {
LOG_DEBUG(Kernel_SVC, "called");
// Get the kernel reference and handle table.
@@ -2318,7 +2362,7 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o
R_UNLESS(event != nullptr, ResultOutOfResource);
// Initialize the event.
- event->Initialize("CreateEvent");
+ event->Initialize("CreateEvent", kernel.CurrentProcess());
// Commit the thread reservation.
event_reservation.Commit();
@@ -2346,11 +2390,11 @@ static ResultCode CreateEvent(Core::System& system, Handle* out_write, Handle* o
return ResultSuccess;
}
-static ResultCode CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
+static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out_read) {
return CreateEvent(system, out_write, out_read);
}
-static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
+static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
// This function currently only allows retrieving a process' status.
@@ -2376,7 +2420,7 @@ static ResultCode GetProcessInfo(Core::System& system, u64* out, Handle process_
return ResultSuccess;
}
-static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) {
+static Result CreateResourceLimit(Core::System& system, Handle* out_handle) {
LOG_DEBUG(Kernel_SVC, "called");
// Create a new resource limit.
@@ -2399,9 +2443,8 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle)
return ResultSuccess;
}
-static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
- Handle resource_limit_handle,
- LimitableResource which) {
+static Result GetResourceLimitLimitValue(Core::System& system, u64* out_limit_value,
+ Handle resource_limit_handle, LimitableResource which) {
LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
which);
@@ -2420,9 +2463,8 @@ static ResultCode GetResourceLimitLimitValue(Core::System& system, u64* out_limi
return ResultSuccess;
}
-static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
- Handle resource_limit_handle,
- LimitableResource which) {
+static Result GetResourceLimitCurrentValue(Core::System& system, u64* out_current_value,
+ Handle resource_limit_handle, LimitableResource which) {
LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}", resource_limit_handle,
which);
@@ -2441,8 +2483,8 @@ static ResultCode GetResourceLimitCurrentValue(Core::System& system, u64* out_cu
return ResultSuccess;
}
-static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
- LimitableResource which, u64 limit_value) {
+static Result SetResourceLimitLimitValue(Core::System& system, Handle resource_limit_handle,
+ LimitableResource which, u64 limit_value) {
LOG_DEBUG(Kernel_SVC, "called, resource_limit_handle={:08X}, which={}, limit_value={}",
resource_limit_handle, which, limit_value);
@@ -2461,8 +2503,8 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour
return ResultSuccess;
}
-static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
- VAddr out_process_ids, u32 out_process_ids_size) {
+static Result GetProcessList(Core::System& system, u32* out_num_processes, VAddr out_process_ids,
+ u32 out_process_ids_size) {
LOG_DEBUG(Kernel_SVC, "called. out_process_ids=0x{:016X}, out_process_ids_size={}",
out_process_ids, out_process_ids_size);
@@ -2498,8 +2540,8 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,
return ResultSuccess;
}
-static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
- u32 out_thread_ids_size, Handle debug_handle) {
+static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr out_thread_ids,
+ u32 out_thread_ids_size, Handle debug_handle) {
// TODO: Handle this case when debug events are supported.
UNIMPLEMENTED_IF(debug_handle != InvalidHandle);
@@ -2513,7 +2555,7 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
return ResultOutOfRange;
}
- const auto* const current_process = system.Kernel().CurrentProcess();
+ auto* const current_process = system.Kernel().CurrentProcess();
const auto total_copy_size = out_thread_ids_size * sizeof(u64);
if (out_thread_ids_size > 0 &&
@@ -2538,9 +2580,9 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd
return ResultSuccess;
}
-static ResultCode FlushProcessDataCache32([[maybe_unused]] Core::System& system,
- [[maybe_unused]] Handle handle,
- [[maybe_unused]] u32 address, [[maybe_unused]] u32 size) {
+static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system,
+ [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address,
+ [[maybe_unused]] u32 size) {
// Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op,
// as all emulation is done in the same cache level in host architecture, thus data cache
// does not need flushing.
@@ -2559,9 +2601,9 @@ struct FunctionDef {
} // namespace
static const FunctionDef SVC_Table_32[] = {
- {0x00, nullptr, "Unknown"},
+ {0x00, nullptr, "Unknown0"},
{0x01, SvcWrap32<SetHeapSize32>, "SetHeapSize32"},
- {0x02, nullptr, "Unknown"},
+ {0x02, nullptr, "SetMemoryPermission32"},
{0x03, SvcWrap32<SetMemoryAttribute32>, "SetMemoryAttribute32"},
{0x04, SvcWrap32<MapMemory32>, "MapMemory32"},
{0x05, SvcWrap32<UnmapMemory32>, "UnmapMemory32"},
@@ -2591,97 +2633,97 @@ static const FunctionDef SVC_Table_32[] = {
{0x1d, SvcWrap32<SignalProcessWideKey32>, "SignalProcessWideKey32"},
{0x1e, SvcWrap32<GetSystemTick32>, "GetSystemTick32"},
{0x1f, SvcWrap32<ConnectToNamedPort32>, "ConnectToNamedPort32"},
- {0x20, nullptr, "Unknown"},
+ {0x20, nullptr, "SendSyncRequestLight32"},
{0x21, SvcWrap32<SendSyncRequest32>, "SendSyncRequest32"},
{0x22, nullptr, "SendSyncRequestWithUserBuffer32"},
- {0x23, nullptr, "Unknown"},
+ {0x23, nullptr, "SendAsyncRequestWithUserBuffer32"},
{0x24, SvcWrap32<GetProcessId32>, "GetProcessId32"},
{0x25, SvcWrap32<GetThreadId32>, "GetThreadId32"},
{0x26, SvcWrap32<Break32>, "Break32"},
- {0x27, nullptr, "OutputDebugString32"},
- {0x28, nullptr, "Unknown"},
+ {0x27, SvcWrap32<OutputDebugString32>, "OutputDebugString32"},
+ {0x28, nullptr, "ReturnFromException32"},
{0x29, SvcWrap32<GetInfo32>, "GetInfo32"},
- {0x2a, nullptr, "Unknown"},
- {0x2b, nullptr, "Unknown"},
+ {0x2a, nullptr, "FlushEntireDataCache32"},
+ {0x2b, nullptr, "FlushDataCache32"},
{0x2c, SvcWrap32<MapPhysicalMemory32>, "MapPhysicalMemory32"},
{0x2d, SvcWrap32<UnmapPhysicalMemory32>, "UnmapPhysicalMemory32"},
- {0x2e, nullptr, "Unknown"},
- {0x2f, nullptr, "Unknown"},
- {0x30, nullptr, "Unknown"},
- {0x31, nullptr, "Unknown"},
+ {0x2e, nullptr, "GetDebugFutureThreadInfo32"},
+ {0x2f, nullptr, "GetLastThreadInfo32"},
+ {0x30, nullptr, "GetResourceLimitLimitValue32"},
+ {0x31, nullptr, "GetResourceLimitCurrentValue32"},
{0x32, SvcWrap32<SetThreadActivity32>, "SetThreadActivity32"},
{0x33, SvcWrap32<GetThreadContext32>, "GetThreadContext32"},
{0x34, SvcWrap32<WaitForAddress32>, "WaitForAddress32"},
{0x35, SvcWrap32<SignalToAddress32>, "SignalToAddress32"},
{0x36, SvcWrap32<SynchronizePreemptionState>, "SynchronizePreemptionState32"},
- {0x37, nullptr, "Unknown"},
- {0x38, nullptr, "Unknown"},
- {0x39, nullptr, "Unknown"},
- {0x3a, nullptr, "Unknown"},
- {0x3b, nullptr, "Unknown"},
- {0x3c, nullptr, "Unknown"},
- {0x3d, nullptr, "Unknown"},
- {0x3e, nullptr, "Unknown"},
- {0x3f, nullptr, "Unknown"},
+ {0x37, nullptr, "GetResourceLimitPeakValue32"},
+ {0x38, nullptr, "Unknown38"},
+ {0x39, nullptr, "CreateIoPool32"},
+ {0x3a, nullptr, "CreateIoRegion32"},
+ {0x3b, nullptr, "Unknown3b"},
+ {0x3c, nullptr, "KernelDebug32"},
+ {0x3d, nullptr, "ChangeKernelTraceState32"},
+ {0x3e, nullptr, "Unknown3e"},
+ {0x3f, nullptr, "Unknown3f"},
{0x40, nullptr, "CreateSession32"},
{0x41, nullptr, "AcceptSession32"},
- {0x42, nullptr, "Unknown"},
+ {0x42, nullptr, "ReplyAndReceiveLight32"},
{0x43, nullptr, "ReplyAndReceive32"},
- {0x44, nullptr, "Unknown"},
+ {0x44, nullptr, "ReplyAndReceiveWithUserBuffer32"},
{0x45, SvcWrap32<CreateEvent32>, "CreateEvent32"},
- {0x46, nullptr, "Unknown"},
- {0x47, nullptr, "Unknown"},
- {0x48, nullptr, "Unknown"},
- {0x49, nullptr, "Unknown"},
- {0x4a, nullptr, "Unknown"},
- {0x4b, nullptr, "Unknown"},
- {0x4c, nullptr, "Unknown"},
- {0x4d, nullptr, "Unknown"},
- {0x4e, nullptr, "Unknown"},
- {0x4f, nullptr, "Unknown"},
- {0x50, nullptr, "Unknown"},
- {0x51, nullptr, "Unknown"},
- {0x52, nullptr, "Unknown"},
- {0x53, nullptr, "Unknown"},
- {0x54, nullptr, "Unknown"},
- {0x55, nullptr, "Unknown"},
- {0x56, nullptr, "Unknown"},
- {0x57, nullptr, "Unknown"},
- {0x58, nullptr, "Unknown"},
- {0x59, nullptr, "Unknown"},
- {0x5a, nullptr, "Unknown"},
- {0x5b, nullptr, "Unknown"},
- {0x5c, nullptr, "Unknown"},
- {0x5d, nullptr, "Unknown"},
- {0x5e, nullptr, "Unknown"},
+ {0x46, nullptr, "MapIoRegion32"},
+ {0x47, nullptr, "UnmapIoRegion32"},
+ {0x48, nullptr, "MapPhysicalMemoryUnsafe32"},
+ {0x49, nullptr, "UnmapPhysicalMemoryUnsafe32"},
+ {0x4a, nullptr, "SetUnsafeLimit32"},
+ {0x4b, SvcWrap32<CreateCodeMemory32>, "CreateCodeMemory32"},
+ {0x4c, SvcWrap32<ControlCodeMemory32>, "ControlCodeMemory32"},
+ {0x4d, nullptr, "SleepSystem32"},
+ {0x4e, nullptr, "ReadWriteRegister32"},
+ {0x4f, nullptr, "SetProcessActivity32"},
+ {0x50, nullptr, "CreateSharedMemory32"},
+ {0x51, nullptr, "MapTransferMemory32"},
+ {0x52, nullptr, "UnmapTransferMemory32"},
+ {0x53, nullptr, "CreateInterruptEvent32"},
+ {0x54, nullptr, "QueryPhysicalAddress32"},
+ {0x55, nullptr, "QueryIoMapping32"},
+ {0x56, nullptr, "CreateDeviceAddressSpace32"},
+ {0x57, nullptr, "AttachDeviceAddressSpace32"},
+ {0x58, nullptr, "DetachDeviceAddressSpace32"},
+ {0x59, nullptr, "MapDeviceAddressSpaceByForce32"},
+ {0x5a, nullptr, "MapDeviceAddressSpaceAligned32"},
+ {0x5b, nullptr, "MapDeviceAddressSpace32"},
+ {0x5c, nullptr, "UnmapDeviceAddressSpace32"},
+ {0x5d, nullptr, "InvalidateProcessDataCache32"},
+ {0x5e, nullptr, "StoreProcessDataCache32"},
{0x5F, SvcWrap32<FlushProcessDataCache32>, "FlushProcessDataCache32"},
- {0x60, nullptr, "Unknown"},
- {0x61, nullptr, "Unknown"},
- {0x62, nullptr, "Unknown"},
- {0x63, nullptr, "Unknown"},
- {0x64, nullptr, "Unknown"},
+ {0x60, nullptr, "StoreProcessDataCache32"},
+ {0x61, nullptr, "BreakDebugProcess32"},
+ {0x62, nullptr, "TerminateDebugProcess32"},
+ {0x63, nullptr, "GetDebugEvent32"},
+ {0x64, nullptr, "ContinueDebugEvent32"},
{0x65, nullptr, "GetProcessList32"},
- {0x66, nullptr, "Unknown"},
- {0x67, nullptr, "Unknown"},
- {0x68, nullptr, "Unknown"},
- {0x69, nullptr, "Unknown"},
- {0x6A, nullptr, "Unknown"},
- {0x6B, nullptr, "Unknown"},
- {0x6C, nullptr, "Unknown"},
- {0x6D, nullptr, "Unknown"},
- {0x6E, nullptr, "Unknown"},
+ {0x66, nullptr, "GetThreadList"},
+ {0x67, nullptr, "GetDebugThreadContext32"},
+ {0x68, nullptr, "SetDebugThreadContext32"},
+ {0x69, nullptr, "QueryDebugProcessMemory32"},
+ {0x6A, nullptr, "ReadDebugProcessMemory32"},
+ {0x6B, nullptr, "WriteDebugProcessMemory32"},
+ {0x6C, nullptr, "SetHardwareBreakPoint32"},
+ {0x6D, nullptr, "GetDebugThreadParam32"},
+ {0x6E, nullptr, "Unknown6E"},
{0x6f, nullptr, "GetSystemInfo32"},
{0x70, nullptr, "CreatePort32"},
{0x71, nullptr, "ManageNamedPort32"},
{0x72, nullptr, "ConnectToPort32"},
{0x73, nullptr, "SetProcessMemoryPermission32"},
- {0x74, nullptr, "Unknown"},
- {0x75, nullptr, "Unknown"},
- {0x76, nullptr, "Unknown"},
+ {0x74, nullptr, "MapProcessMemory32"},
+ {0x75, nullptr, "UnmapProcessMemory32"},
+ {0x76, nullptr, "QueryProcessMemory32"},
{0x77, nullptr, "MapProcessCodeMemory32"},
{0x78, nullptr, "UnmapProcessCodeMemory32"},
- {0x79, nullptr, "Unknown"},
- {0x7A, nullptr, "Unknown"},
+ {0x79, nullptr, "CreateProcess32"},
+ {0x7A, nullptr, "StartProcess32"},
{0x7B, nullptr, "TerminateProcess32"},
{0x7C, nullptr, "GetProcessInfo32"},
{0x7D, nullptr, "CreateResourceLimit32"},
@@ -2754,7 +2796,7 @@ static const FunctionDef SVC_Table_32[] = {
};
static const FunctionDef SVC_Table_64[] = {
- {0x00, nullptr, "Unknown"},
+ {0x00, nullptr, "Unknown0"},
{0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"},
{0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"},
{0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},
@@ -2809,23 +2851,23 @@ static const FunctionDef SVC_Table_64[] = {
{0x34, SvcWrap64<WaitForAddress>, "WaitForAddress"},
{0x35, SvcWrap64<SignalToAddress>, "SignalToAddress"},
{0x36, SvcWrap64<SynchronizePreemptionState>, "SynchronizePreemptionState"},
- {0x37, nullptr, "Unknown"},
- {0x38, nullptr, "Unknown"},
- {0x39, nullptr, "Unknown"},
- {0x3A, nullptr, "Unknown"},
- {0x3B, nullptr, "Unknown"},
+ {0x37, nullptr, "GetResourceLimitPeakValue"},
+ {0x38, nullptr, "Unknown38"},
+ {0x39, nullptr, "CreateIoPool"},
+ {0x3A, nullptr, "CreateIoRegion"},
+ {0x3B, nullptr, "Unknown3B"},
{0x3C, SvcWrap64<KernelDebug>, "KernelDebug"},
{0x3D, SvcWrap64<ChangeKernelTraceState>, "ChangeKernelTraceState"},
- {0x3E, nullptr, "Unknown"},
- {0x3F, nullptr, "Unknown"},
+ {0x3E, nullptr, "Unknown3e"},
+ {0x3F, nullptr, "Unknown3f"},
{0x40, nullptr, "CreateSession"},
{0x41, nullptr, "AcceptSession"},
{0x42, nullptr, "ReplyAndReceiveLight"},
{0x43, nullptr, "ReplyAndReceive"},
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
{0x45, SvcWrap64<CreateEvent>, "CreateEvent"},
- {0x46, nullptr, "Unknown"},
- {0x47, nullptr, "Unknown"},
+ {0x46, nullptr, "MapIoRegion"},
+ {0x47, nullptr, "UnmapIoRegion"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
@@ -2864,7 +2906,7 @@ static const FunctionDef SVC_Table_64[] = {
{0x6B, nullptr, "WriteDebugProcessMemory"},
{0x6C, nullptr, "SetHardwareBreakPoint"},
{0x6D, nullptr, "GetDebugThreadParam"},
- {0x6E, nullptr, "Unknown"},
+ {0x6E, nullptr, "Unknown6E"},
{0x6F, nullptr, "GetSystemInfo"},
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},
@@ -2965,11 +3007,10 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) {
}
void Call(Core::System& system, u32 immediate) {
- system.ExitDynarmicProfile();
auto& kernel = system.Kernel();
kernel.EnterSVCProfile();
- auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
+ auto* thread = GetCurrentThreadPointer(kernel);
thread->SetIsCallingSvc();
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
@@ -2985,13 +3026,6 @@ void Call(Core::System& system, u32 immediate) {
}
kernel.ExitSVCProfile();
-
- if (!thread->IsCallingSvc()) {
- auto* host_context = thread->GetHostContext().get();
- host_context->Rewind();
- }
-
- system.EnterDynarmicProfile();
}
} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index 46e64277e..13f061b83 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/svc_common.h b/src/core/hle/kernel/svc_common.h
index 25de6e437..95750c3eb 100644
--- a/src/core/hle/kernel/svc_common.h
+++ b/src/core/hle/kernel/svc_common.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h
index 53a940723..f27cade33 100644
--- a/src/core/hle/kernel/svc_results.h
+++ b/src/core/hle/kernel/svc_results.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,34 +9,34 @@ namespace Kernel {
// Confirmed Switch kernel error codes
-constexpr ResultCode ResultOutOfSessions{ErrorModule::Kernel, 7};
-constexpr ResultCode ResultInvalidArgument{ErrorModule::Kernel, 14};
-constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
-constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
-constexpr ResultCode ResultInvalidSize{ErrorModule::Kernel, 101};
-constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
-constexpr ResultCode ResultOutOfResource{ErrorModule::Kernel, 103};
-constexpr ResultCode ResultOutOfMemory{ErrorModule::Kernel, 104};
-constexpr ResultCode ResultOutOfHandles{ErrorModule::Kernel, 105};
-constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
-constexpr ResultCode ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108};
-constexpr ResultCode ResultInvalidMemoryRegion{ErrorModule::Kernel, 110};
-constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112};
-constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113};
-constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114};
-constexpr ResultCode ResultInvalidPointer{ErrorModule::Kernel, 115};
-constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116};
-constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117};
-constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118};
-constexpr ResultCode ResultOutOfRange{ErrorModule::Kernel, 119};
-constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
-constexpr ResultCode ResultNotFound{ErrorModule::Kernel, 121};
-constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
-constexpr ResultCode ResultSessionClosed{ErrorModule::Kernel, 123};
-constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
-constexpr ResultCode ResultReservedUsed{ErrorModule::Kernel, 126};
-constexpr ResultCode ResultPortClosed{ErrorModule::Kernel, 131};
-constexpr ResultCode ResultLimitReached{ErrorModule::Kernel, 132};
-constexpr ResultCode ResultInvalidId{ErrorModule::Kernel, 519};
+constexpr Result ResultOutOfSessions{ErrorModule::Kernel, 7};
+constexpr Result ResultInvalidArgument{ErrorModule::Kernel, 14};
+constexpr Result ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
+constexpr Result ResultTerminationRequested{ErrorModule::Kernel, 59};
+constexpr Result ResultInvalidSize{ErrorModule::Kernel, 101};
+constexpr Result ResultInvalidAddress{ErrorModule::Kernel, 102};
+constexpr Result ResultOutOfResource{ErrorModule::Kernel, 103};
+constexpr Result ResultOutOfMemory{ErrorModule::Kernel, 104};
+constexpr Result ResultOutOfHandles{ErrorModule::Kernel, 105};
+constexpr Result ResultInvalidCurrentMemory{ErrorModule::Kernel, 106};
+constexpr Result ResultInvalidNewMemoryPermission{ErrorModule::Kernel, 108};
+constexpr Result ResultInvalidMemoryRegion{ErrorModule::Kernel, 110};
+constexpr Result ResultInvalidPriority{ErrorModule::Kernel, 112};
+constexpr Result ResultInvalidCoreId{ErrorModule::Kernel, 113};
+constexpr Result ResultInvalidHandle{ErrorModule::Kernel, 114};
+constexpr Result ResultInvalidPointer{ErrorModule::Kernel, 115};
+constexpr Result ResultInvalidCombination{ErrorModule::Kernel, 116};
+constexpr Result ResultTimedOut{ErrorModule::Kernel, 117};
+constexpr Result ResultCancelled{ErrorModule::Kernel, 118};
+constexpr Result ResultOutOfRange{ErrorModule::Kernel, 119};
+constexpr Result ResultInvalidEnumValue{ErrorModule::Kernel, 120};
+constexpr Result ResultNotFound{ErrorModule::Kernel, 121};
+constexpr Result ResultBusy{ErrorModule::Kernel, 122};
+constexpr Result ResultSessionClosed{ErrorModule::Kernel, 123};
+constexpr Result ResultInvalidState{ErrorModule::Kernel, 125};
+constexpr Result ResultReservedUsed{ErrorModule::Kernel, 126};
+constexpr Result ResultPortClosed{ErrorModule::Kernel, 131};
+constexpr Result ResultLimitReached{ErrorModule::Kernel, 132};
+constexpr Result ResultInvalidId{ErrorModule::Kernel, 519};
} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h
index 365e22e4e..79e15183a 100644
--- a/src/core/hle/kernel/svc_types.h
+++ b/src/core/hle/kernel/svc_types.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -96,4 +95,6 @@ constexpr inline s32 IdealCoreNoUpdate = -3;
constexpr inline s32 LowestThreadPriority = 63;
constexpr inline s32 HighestThreadPriority = 0;
+constexpr inline size_t ThreadLocalRegionSize = 0x200;
+
} // namespace Kernel::Svc
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index a60adfcab..4bc49087e 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -34,24 +33,24 @@ static inline void FuncReturn32(Core::System& system, u32 result) {
}
////////////////////////////////////////////////////////////////////////////////////////////////////
-// Function wrappers that return type ResultCode
+// Function wrappers that return type Result
-template <ResultCode func(Core::System&, u64)>
+template <Result func(Core::System&, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0)).raw);
}
-template <ResultCode func(Core::System&, u64, u64)>
+template <Result func(Core::System&, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
}
-template <ResultCode func(Core::System&, u32)>
+template <Result func(Core::System&, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
-template <ResultCode func(Core::System&, u32, u32)>
+template <Result func(Core::System&, u32, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
@@ -59,14 +58,14 @@ void SvcWrap64(Core::System& system) {
}
// Used by SetThreadActivity
-template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
+template <Result func(Core::System&, Handle, Svc::ThreadActivity)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<Svc::ThreadActivity>(Param(system, 1)))
.raw);
}
-template <ResultCode func(Core::System&, u32, u64, u64, u64)>
+template <Result func(Core::System&, u32, u64, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
Param(system, 2), Param(system, 3))
@@ -74,7 +73,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by MapProcessMemory and UnmapProcessMemory
-template <ResultCode func(Core::System&, u64, u32, u64, u64)>
+template <Result func(Core::System&, u64, u32, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
Param(system, 2), Param(system, 3))
@@ -82,7 +81,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by ControlCodeMemory
-template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
@@ -90,7 +89,7 @@ void SvcWrap64(Core::System& system) {
.raw);
}
-template <ResultCode func(Core::System&, u32*)>
+template <Result func(Core::System&, u32*)>
void SvcWrap64(Core::System& system) {
u32 param = 0;
const u32 retval = func(system, &param).raw;
@@ -98,7 +97,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u32)>
+template <Result func(Core::System&, u32*, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
@@ -106,7 +105,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u32*)>
+template <Result func(Core::System&, u32*, u32*)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
@@ -119,7 +118,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u64)>
+template <Result func(Core::System&, u32*, u64)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
@@ -127,7 +126,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u64, u32)>
+template <Result func(Core::System&, u32*, u64, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval =
@@ -137,7 +136,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64*, u32)>
+template <Result func(Core::System&, u64*, u32)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1))).raw;
@@ -146,12 +145,12 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64, u32)>
+template <Result func(Core::System&, u64, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1))).raw);
}
-template <ResultCode func(Core::System&, u64*, u64)>
+template <Result func(Core::System&, u64*, u64)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1)).raw;
@@ -160,7 +159,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64*, u32, u32)>
+template <Result func(Core::System&, u64*, u32, u32)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<u32>(Param(system, 1)),
@@ -172,7 +171,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by GetResourceLimitLimitValue.
-template <ResultCode func(Core::System&, u64*, Handle, LimitableResource)>
+template <Result func(Core::System&, u64*, Handle, LimitableResource)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, static_cast<Handle>(Param(system, 1)),
@@ -183,13 +182,13 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32, u64)>
+template <Result func(Core::System&, u32, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1)).raw);
}
// Used by SetResourceLimitLimitValue
-template <ResultCode func(Core::System&, Handle, LimitableResource, u64)>
+template <Result func(Core::System&, Handle, LimitableResource, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
static_cast<LimitableResource>(Param(system, 1)), Param(system, 2))
@@ -197,7 +196,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by SetThreadCoreMask
-template <ResultCode func(Core::System&, Handle, s32, u64)>
+template <Result func(Core::System&, Handle, s32, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)),
static_cast<s32>(Param(system, 1)), Param(system, 2))
@@ -205,44 +204,44 @@ void SvcWrap64(Core::System& system) {
}
// Used by GetThreadCoreMask
-template <ResultCode func(Core::System&, Handle, s32*, u64*)>
+template <Result func(Core::System&, Handle, s32*, u64*)>
void SvcWrap64(Core::System& system) {
s32 param_1 = 0;
u64 param_2 = 0;
- const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
+ const Result retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
system.CurrentArmInterface().SetReg(1, param_1);
system.CurrentArmInterface().SetReg(2, param_2);
FuncReturn(system, retval.raw);
}
-template <ResultCode func(Core::System&, u64, u64, u32, u32)>
+template <Result func(Core::System&, u64, u64, u32, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3)))
.raw);
}
-template <ResultCode func(Core::System&, u64, u64, u32, u64)>
+template <Result func(Core::System&, u64, u64, u32, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), Param(system, 3))
.raw);
}
-template <ResultCode func(Core::System&, u32, u64, u32)>
+template <Result func(Core::System&, u32, u64, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
static_cast<u32>(Param(system, 2)))
.raw);
}
-template <ResultCode func(Core::System&, u64, u64, u64)>
+template <Result func(Core::System&, u64, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1), Param(system, 2)).raw);
}
-template <ResultCode func(Core::System&, u64, u64, u32)>
+template <Result func(Core::System&, u64, u64, u32)>
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
@@ -250,7 +249,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by SetMemoryPermission
-template <ResultCode func(Core::System&, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<Svc::MemoryPermission>(Param(system, 2)))
@@ -258,14 +257,14 @@ void SvcWrap64(Core::System& system) {
}
// Used by MapSharedMemory
-template <ResultCode func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), Param(system, 1),
Param(system, 2), static_cast<Svc::MemoryPermission>(Param(system, 3)))
.raw);
}
-template <ResultCode func(Core::System&, u32, u64, u64)>
+template <Result func(Core::System&, u32, u64, u64)>
void SvcWrap64(Core::System& system) {
FuncReturn(
system,
@@ -273,7 +272,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by WaitSynchronization
-template <ResultCode func(Core::System&, s32*, u64, s32, s64)>
+template <Result func(Core::System&, s32*, u64, s32, s64)>
void SvcWrap64(Core::System& system) {
s32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<s32>(Param(system, 2)),
@@ -284,7 +283,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u64, u64, u32, s64)>
+template <Result func(Core::System&, u64, u64, u32, s64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0), Param(system, 1),
static_cast<u32>(Param(system, 2)), static_cast<s64>(Param(system, 3)))
@@ -292,7 +291,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by GetInfo
-template <ResultCode func(Core::System&, u64*, u64, Handle, u64)>
+template <Result func(Core::System&, u64*, u64, Handle, u64)>
void SvcWrap64(Core::System& system) {
u64 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1),
@@ -303,7 +302,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, u32*, u64, u64, u64, u32, s32)>
+template <Result func(Core::System&, u32*, u64, u64, u64, u32, s32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2), Param(system, 3),
@@ -315,7 +314,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by CreateTransferMemory
-template <ResultCode func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle*, u64, u64, Svc::MemoryPermission)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2),
@@ -327,7 +326,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by CreateCodeMemory
-template <ResultCode func(Core::System&, Handle*, u64, u64)>
+template <Result func(Core::System&, Handle*, u64, u64)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
@@ -336,7 +335,7 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
-template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
+template <Result func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param(system, 1), static_cast<u32>(Param(system, 2)),
@@ -348,7 +347,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by WaitForAddress
-template <ResultCode func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
+template <Result func(Core::System&, u64, Svc::ArbitrationType, s32, s64)>
void SvcWrap64(Core::System& system) {
FuncReturn(system,
func(system, Param(system, 0), static_cast<Svc::ArbitrationType>(Param(system, 1)),
@@ -357,7 +356,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by SignalToAddress
-template <ResultCode func(Core::System&, u64, Svc::SignalType, s32, s32)>
+template <Result func(Core::System&, u64, Svc::SignalType, s32, s32)>
void SvcWrap64(Core::System& system) {
FuncReturn(system,
func(system, Param(system, 0), static_cast<Svc::SignalType>(Param(system, 1)),
@@ -426,7 +425,7 @@ void SvcWrap64(Core::System& system) {
}
// Used by QueryMemory32, ArbitrateLock32
-template <ResultCode func(Core::System&, u32, u32, u32)>
+template <Result func(Core::System&, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
FuncReturn32(system,
func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2)).raw);
@@ -457,7 +456,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by CreateThread32
-template <ResultCode func(Core::System&, Handle*, u32, u32, u32, u32, s32)>
+template <Result func(Core::System&, Handle*, u32, u32, u32, u32, s32)>
void SvcWrap32(Core::System& system) {
Handle param_1 = 0;
@@ -470,7 +469,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetInfo32
-template <ResultCode func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
+template <Result func(Core::System&, u32*, u32*, u32, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
@@ -485,7 +484,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetThreadPriority32, ConnectToNamedPort32
-template <ResultCode func(Core::System&, u32*, u32)>
+template <Result func(Core::System&, u32*, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
const u32 retval = func(system, &param_1, Param32(system, 1)).raw;
@@ -494,7 +493,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetThreadId32
-template <ResultCode func(Core::System&, u32*, u32*, u32)>
+template <Result func(Core::System&, u32*, u32*, u32)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
@@ -517,7 +516,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by CreateEvent32
-template <ResultCode func(Core::System&, Handle*, Handle*)>
+template <Result func(Core::System&, Handle*, Handle*)>
void SvcWrap32(Core::System& system) {
Handle param_1 = 0;
Handle param_2 = 0;
@@ -529,7 +528,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetThreadId32
-template <ResultCode func(Core::System&, Handle, u32*, u32*, u32*)>
+template <Result func(Core::System&, Handle, u32*, u32*, u32*)>
void SvcWrap32(Core::System& system) {
u32 param_1 = 0;
u32 param_2 = 0;
@@ -543,7 +542,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by GetThreadCoreMask32
-template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)>
+template <Result func(Core::System&, Handle, s32*, u32*, u32*)>
void SvcWrap32(Core::System& system) {
s32 param_1 = 0;
u32 param_2 = 0;
@@ -563,7 +562,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SetThreadActivity32
-template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)>
+template <Result func(Core::System&, Handle, Svc::ThreadActivity)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
static_cast<Svc::ThreadActivity>(Param(system, 1)))
@@ -572,7 +571,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SetThreadPriority32
-template <ResultCode func(Core::System&, Handle, u32)>
+template <Result func(Core::System&, Handle, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw;
@@ -580,7 +579,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SetMemoryAttribute32
-template <ResultCode func(Core::System&, Handle, u32, u32, u32)>
+template <Result func(Core::System&, Handle, u32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<Handle>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
@@ -590,7 +589,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by MapSharedMemory32
-template <ResultCode func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle, u32, u32, Svc::MemoryPermission)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<Handle>(Param(system, 0)),
static_cast<u32>(Param(system, 1)), static_cast<u32>(Param(system, 2)),
@@ -600,7 +599,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SetThreadCoreMask32
-template <ResultCode func(Core::System&, Handle, s32, u32, u32)>
+template <Result func(Core::System&, Handle, s32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)),
@@ -610,7 +609,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by WaitProcessWideKeyAtomic32
-template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)>
+template <Result func(Core::System&, u32, u32, Handle, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval =
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1)),
@@ -621,7 +620,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by WaitForAddress32
-template <ResultCode func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>
+template <Result func(Core::System&, u32, Svc::ArbitrationType, s32, u32, u32)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
static_cast<Svc::ArbitrationType>(Param(system, 1)),
@@ -632,7 +631,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by SignalToAddress32
-template <ResultCode func(Core::System&, u32, Svc::SignalType, s32, s32)>
+template <Result func(Core::System&, u32, Svc::SignalType, s32, s32)>
void SvcWrap32(Core::System& system) {
const u32 retval = func(system, static_cast<u32>(Param(system, 0)),
static_cast<Svc::SignalType>(Param(system, 1)),
@@ -642,13 +641,13 @@ void SvcWrap32(Core::System& system) {
}
// Used by SendSyncRequest32, ArbitrateUnlock32
-template <ResultCode func(Core::System&, u32)>
+template <Result func(Core::System&, u32)>
void SvcWrap32(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
}
// Used by CreateTransferMemory32
-template <ResultCode func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)>
+template <Result func(Core::System&, Handle*, u32, u32, Svc::MemoryPermission)>
void SvcWrap32(Core::System& system) {
Handle handle = 0;
const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2),
@@ -659,7 +658,7 @@ void SvcWrap32(Core::System& system) {
}
// Used by WaitSynchronization32
-template <ResultCode func(Core::System&, u32, u32, s32, u32, s32*)>
+template <Result func(Core::System&, u32, u32, s32, u32, s32*)>
void SvcWrap32(Core::System& system) {
s32 param_1 = 0;
const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2),
@@ -669,4 +668,26 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval);
}
+// Used by CreateCodeMemory32
+template <Result func(Core::System&, Handle*, u32, u32)>
+void SvcWrap32(Core::System& system) {
+ Handle handle = 0;
+
+ const u32 retval = func(system, &handle, Param32(system, 1), Param32(system, 2)).raw;
+
+ system.CurrentArmInterface().SetReg(1, handle);
+ FuncReturn(system, retval);
+}
+
+// Used by ControlCodeMemory32
+template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
+void SvcWrap32(Core::System& system) {
+ const u32 retval =
+ func(system, Param32(system, 0), Param32(system, 1), Param(system, 2), Param(system, 4),
+ static_cast<Svc::MemoryPermission>(Param32(system, 6)))
+ .raw;
+
+ FuncReturn(system, retval);
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index aa985d820..5ee72c432 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/core.h"
@@ -12,19 +11,21 @@
namespace Kernel {
TimeManager::TimeManager(Core::System& system_) : system{system_} {
- time_manager_event_type =
- Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
- [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
- KThread* thread = reinterpret_cast<KThread*>(thread_handle);
- {
- KScopedSchedulerLock sl(system.Kernel());
- thread->OnTimer();
- }
- });
+ time_manager_event_type = Core::Timing::CreateEvent(
+ "Kernel::TimeManagerCallback",
+ [this](std::uintptr_t thread_handle, s64 time,
+ std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
+ KThread* thread = reinterpret_cast<KThread*>(thread_handle);
+ {
+ KScopedSchedulerLock sl(system.Kernel());
+ thread->OnTimer();
+ }
+ return std::nullopt;
+ });
}
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
if (nanoseconds > 0) {
ASSERT(thread);
ASSERT(thread->GetState() != ThreadState::Runnable);
@@ -35,7 +36,7 @@ void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
}
void TimeManager::UnscheduleTimeEvent(KThread* thread) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
system.CoreTiming().UnscheduleEvent(time_manager_event_type,
reinterpret_cast<uintptr_t>(thread));
}
diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h
index b1fa26e8c..94d16b3b4 100644
--- a/src/core/hle/kernel/time_manager.h
+++ b/src/core/hle/kernel/time_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index 3807b9aa8..47a1b829b 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -112,15 +111,16 @@ enum class ErrorModule : u32 {
};
/// Encapsulates a Horizon OS error code, allowing it to be separated into its constituent fields.
-union ResultCode {
+union Result {
u32 raw;
BitField<0, 9, ErrorModule> module;
BitField<9, 13, u32> description;
- constexpr explicit ResultCode(u32 raw_) : raw(raw_) {}
+ Result() = default;
+ constexpr explicit Result(u32 raw_) : raw(raw_) {}
- constexpr ResultCode(ErrorModule module_, u32 description_)
+ constexpr Result(ErrorModule module_, u32 description_)
: raw(module.FormatValue(module_) | description.FormatValue(description_)) {}
[[nodiscard]] constexpr bool IsSuccess() const {
@@ -131,19 +131,20 @@ union ResultCode {
return !IsSuccess();
}
};
+static_assert(std::is_trivial_v<Result>);
-[[nodiscard]] constexpr bool operator==(const ResultCode& a, const ResultCode& b) {
+[[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) {
return a.raw == b.raw;
}
-[[nodiscard]] constexpr bool operator!=(const ResultCode& a, const ResultCode& b) {
+[[nodiscard]] constexpr bool operator!=(const Result& a, const Result& b) {
return !operator==(a, b);
}
// Convenience functions for creating some common kinds of errors:
-/// The default success `ResultCode`.
-constexpr ResultCode ResultSuccess(0);
+/// The default success `Result`.
+constexpr Result ResultSuccess(0);
/**
* Placeholder result code used for unknown error codes.
@@ -151,10 +152,52 @@ constexpr ResultCode ResultSuccess(0);
* @note This should only be used when a particular error code
* is not known yet.
*/
-constexpr ResultCode ResultUnknown(UINT32_MAX);
+constexpr Result ResultUnknown(UINT32_MAX);
/**
- * This is an optional value type. It holds a `ResultCode` and, if that code is ResultSuccess, it
+ * A ResultRange defines an inclusive range of error descriptions within an error module.
+ * This can be used to check whether the description of a given Result falls within the range.
+ * The conversion function returns a Result with its description set to description_start.
+ *
+ * An example of how it could be used:
+ * \code
+ * constexpr ResultRange ResultCommonError{ErrorModule::Common, 0, 9999};
+ *
+ * Result Example(int value) {
+ * const Result result = OtherExample(value);
+ *
+ * // This will only evaluate to true if result.module is ErrorModule::Common and
+ * // result.description is in between 0 and 9999 inclusive.
+ * if (ResultCommonError.Includes(result)) {
+ * // This returns Result{ErrorModule::Common, 0};
+ * return ResultCommonError;
+ * }
+ *
+ * return ResultSuccess;
+ * }
+ * \endcode
+ */
+class ResultRange {
+public:
+ consteval ResultRange(ErrorModule module, u32 description_start, u32 description_end_)
+ : code{module, description_start}, description_end{description_end_} {}
+
+ [[nodiscard]] constexpr operator Result() const {
+ return code;
+ }
+
+ [[nodiscard]] constexpr bool Includes(Result other) const {
+ return code.module == other.module && code.description <= other.description &&
+ other.description <= description_end;
+ }
+
+private:
+ Result code;
+ u32 description_end;
+};
+
+/**
+ * This is an optional value type. It holds a `Result` and, if that code is ResultSuccess, it
* also holds a result of type `T`. If the code is an error code (not ResultSuccess), then trying
* to access the inner value with operator* is undefined behavior and will assert with Unwrap().
* Users of this class must be cognizant to check the status of the ResultVal with operator bool(),
@@ -165,7 +208,7 @@ constexpr ResultCode ResultUnknown(UINT32_MAX);
* ResultVal<int> Frobnicate(float strength) {
* if (strength < 0.f || strength > 1.0f) {
* // Can't frobnicate too weakly or too strongly
- * return ResultCode{ErrorModule::Common, 1};
+ * return Result{ErrorModule::Common, 1};
* } else {
* // Frobnicated! Give caller a cookie
* return 42;
@@ -188,7 +231,9 @@ class ResultVal {
public:
constexpr ResultVal() : expected{} {}
- constexpr ResultVal(ResultCode code) : expected{Common::Unexpected(code)} {}
+ constexpr ResultVal(Result code) : expected{Common::Unexpected(code)} {}
+
+ constexpr ResultVal(ResultRange range) : expected{Common::Unexpected(range)} {}
template <typename U>
constexpr ResultVal(U&& val) : expected{std::forward<U>(val)} {}
@@ -208,7 +253,7 @@ public:
return expected.has_value();
}
- [[nodiscard]] constexpr ResultCode Code() const {
+ [[nodiscard]] constexpr Result Code() const {
return expected.has_value() ? ResultSuccess : expected.error();
}
@@ -275,8 +320,8 @@ public:
}
private:
- // TODO: Replace this with std::expected once it is standardized in the STL.
- Common::Expected<T, ResultCode> expected;
+ // TODO (Morph): Replace this with C++23 std::expected.
+ Common::Expected<T, Result> expected;
};
/**
@@ -293,7 +338,7 @@ private:
target = std::move(*CONCAT2(check_result_L, __LINE__))
/**
- * Analogous to CASCADE_RESULT, but for a bare ResultCode. The code will be propagated if
+ * Analogous to CASCADE_RESULT, but for a bare Result. The code will be propagated if
* non-success, or discarded otherwise.
*/
#define CASCADE_CODE(source) \
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index e34ef5a78..bb838e285 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
@@ -16,7 +15,6 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/kernel.h"
#include "core/hle/service/acc/acc.h"
#include "core/hle/service/acc/acc_aa.h"
#include "core/hle/service/acc/acc_su.h"
@@ -30,11 +28,11 @@
namespace Service::Account {
-constexpr ResultCode ERR_INVALID_USER_ID{ErrorModule::Account, 20};
-constexpr ResultCode ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22};
-constexpr ResultCode ERR_INVALID_BUFFER{ErrorModule::Account, 30};
-constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31};
-constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
+constexpr Result ERR_INVALID_USER_ID{ErrorModule::Account, 20};
+constexpr Result ERR_INVALID_APPLICATION_ID{ErrorModule::Account, 22};
+constexpr Result ERR_INVALID_BUFFER{ErrorModule::Account, 30};
+constexpr Result ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 31};
+constexpr Result ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
// Thumbnails are hard coded to be at least this size
constexpr std::size_t THUMBNAIL_SIZE = 0x24000;
@@ -292,7 +290,7 @@ protected:
void Get(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called user_id=0x{}", user_id.RawString());
ProfileBase profile_base{};
- ProfileData data{};
+ UserData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 16};
@@ -375,18 +373,18 @@ protected:
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.RawString());
- if (user_data.size() < sizeof(ProfileData)) {
- LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
+ if (user_data.size() < sizeof(UserData)) {
+ LOG_ERROR(Service_ACC, "UserData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER);
return;
}
- ProfileData data;
- std::memcpy(&data, user_data.data(), sizeof(ProfileData));
+ UserData data;
+ std::memcpy(&data, user_data.data(), sizeof(UserData));
if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
- LOG_ERROR(Service_ACC, "Failed to update profile data and base!");
+ LOG_ERROR(Service_ACC, "Failed to update user data and base!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_FAILED_SAVE_DATA);
return;
@@ -408,15 +406,15 @@ protected:
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.RawString());
- if (user_data.size() < sizeof(ProfileData)) {
- LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
+ if (user_data.size() < sizeof(UserData)) {
+ LOG_ERROR(Service_ACC, "UserData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER);
return;
}
- ProfileData data;
- std::memcpy(&data, user_data.data(), sizeof(ProfileData));
+ UserData data;
+ std::memcpy(&data, user_data.data(), sizeof(UserData));
Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write,
Common::FS::FileType::BinaryFile);
@@ -507,7 +505,7 @@ protected:
void Cancel() override {}
- ResultCode GetResult() const override {
+ Result GetResult() const override {
return ResultSuccess;
}
};
@@ -536,7 +534,7 @@ public:
private:
void CheckAvailability(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_ACC, "(STUBBED) called");
+ LOG_DEBUG(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(false); // TODO: Check when this is supposed to return true and when not
@@ -749,7 +747,7 @@ void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestCo
rb.Push(InitializeApplicationInfoBase());
}
-ResultCode Module::Interface::InitializeApplicationInfoBase() {
+Result Module::Interface::InitializeApplicationInfoBase() {
if (application_info) {
LOG_ERROR(Service_ACC, "Application already initialized");
return ERR_ACCOUNTINFO_ALREADY_INITIALIZED;
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index f7e9bc4f8..1621e7c0a 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -42,7 +41,7 @@ public:
void StoreSaveDataThumbnailSystem(Kernel::HLERequestContext& ctx);
private:
- ResultCode InitializeApplicationInfoBase();
+ Result InitializeApplicationInfoBase();
void StoreSaveDataThumbnail(Kernel::HLERequestContext& ctx, const Common::UUID& uuid,
const u64 tid);
diff --git a/src/core/hle/service/acc/acc_aa.cpp b/src/core/hle/service/acc/acc_aa.cpp
index e498fb64d..90ed0f519 100644
--- a/src/core/hle/service/acc/acc_aa.cpp
+++ b/src/core/hle/service/acc/acc_aa.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/acc/acc_aa.h"
diff --git a/src/core/hle/service/acc/acc_aa.h b/src/core/hle/service/acc/acc_aa.h
index d1be20ff3..623daeaef 100644
--- a/src/core/hle/service/acc/acc_aa.h
+++ b/src/core/hle/service/acc/acc_aa.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index f4034d591..b6bfd6155 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/acc/acc_su.h"
diff --git a/src/core/hle/service/acc/acc_su.h b/src/core/hle/service/acc/acc_su.h
index 132a126b4..8daef38b8 100644
--- a/src/core/hle/service/acc/acc_su.h
+++ b/src/core/hle/service/acc/acc_su.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index df77c58f0..65023b8c2 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/acc/acc_u0.h"
diff --git a/src/core/hle/service/acc/acc_u0.h b/src/core/hle/service/acc/acc_u0.h
index 4c2600b67..35cd4b492 100644
--- a/src/core/hle/service/acc/acc_u0.h
+++ b/src/core/hle/service/acc/acc_u0.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index 991921984..92f704c2f 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/acc/acc_u1.h"
diff --git a/src/core/hle/service/acc/acc_u1.h b/src/core/hle/service/acc/acc_u1.h
index 2d478324a..e711d3925 100644
--- a/src/core/hle/service/acc/acc_u1.h
+++ b/src/core/hle/service/acc/acc_u1.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/acc/async_context.cpp b/src/core/hle/service/acc/async_context.cpp
index a49dfdec7..c85b2e43a 100644
--- a/src/core/hle/service/acc/async_context.cpp
+++ b/src/core/hle/service/acc/async_context.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/service/acc/async_context.h b/src/core/hle/service/acc/async_context.h
index cc3a0a9fe..26332d241 100644
--- a/src/core/hle/service/acc/async_context.h
+++ b/src/core/hle/service/acc/async_context.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -27,7 +26,7 @@ public:
protected:
virtual bool IsComplete() const = 0;
virtual void Cancel() = 0;
- virtual ResultCode GetResult() const = 0;
+ virtual Result GetResult() const = 0;
void MarkComplete();
diff --git a/src/core/hle/service/acc/errors.h b/src/core/hle/service/acc/errors.h
index 1f0577239..e9c16b951 100644
--- a/src/core/hle/service/acc/errors.h
+++ b/src/core/hle/service/acc/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,7 +7,7 @@
namespace Service::Account {
-constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22};
-constexpr ResultCode ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41};
+constexpr Result ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22};
+constexpr Result ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41};
} // namespace Service::Account
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index fba847142..a58da4d5f 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <random>
@@ -23,7 +22,7 @@ struct UserRaw {
UUID uuid2{};
u64 timestamp{};
ProfileUsername username{};
- ProfileData extra_data{};
+ UserData extra_data{};
};
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
@@ -34,9 +33,9 @@ struct ProfileDataRaw {
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
// TODO(ogniK): Get actual error codes
-constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1));
-constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2));
-constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
+constexpr Result ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1));
+constexpr Result ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2));
+constexpr Result ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "system/save/8000000000000010/su/avators";
@@ -88,7 +87,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
}
/// Helper function to register a user to the system
-ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
+Result ProfileManager::AddUser(const ProfileInfo& user) {
if (!AddToProfiles(user)) {
return ERROR_TOO_MANY_USERS;
}
@@ -97,7 +96,7 @@ ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
/// Create a new user on the system. If the uuid of the user already exists, the user is not
/// created.
-ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) {
+Result ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& username) {
if (user_count == MAX_USERS) {
return ERROR_TOO_MANY_USERS;
}
@@ -124,7 +123,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern
/// Creates a new user on the system. This function allows a much simpler method of registration
/// specifically by allowing an std::string for the username. This is required specifically since
/// we're loading a string straight from the config
-ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
+Result ProfileManager::CreateNewUser(UUID uuid, const std::string& username) {
ProfileUsername username_output{};
if (username.size() > username_output.size()) {
@@ -264,7 +263,7 @@ UUID ProfileManager::GetLastOpenedUser() const {
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
- ProfileData& data) const {
+ UserData& data) const {
if (GetProfileBase(index, profile)) {
data = profiles[*index].data;
return true;
@@ -273,15 +272,14 @@ bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, Pro
}
/// Return the users profile base and the unknown arbitary data.
-bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
- ProfileData& data) const {
+bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, UserData& data) const {
const auto idx = GetUserIndex(uuid);
return GetProfileBaseAndData(idx, profile, data);
}
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
- ProfileData& data) const {
+ UserData& data) const {
return GetProfileBaseAndData(user.user_uuid, profile, data);
}
@@ -319,7 +317,7 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
}
bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
- const ProfileData& data_new) {
+ const UserData& data_new) {
const auto index = GetUserIndex(uuid);
if (index.has_value() && SetProfileBase(uuid, profile_new)) {
profiles[*index].data = data_new;
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 17347f7ef..135f7d0d5 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -1,12 +1,12 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <optional>
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "common/uuid.h"
@@ -22,7 +22,7 @@ using UserIDArray = std::array<Common::UUID, MAX_USERS>;
/// Contains extra data related to a user.
/// TODO: RE this structure
-struct ProfileData {
+struct UserData {
INSERT_PADDING_WORDS_NOINIT(1);
u32 icon_id;
u8 bg_color_id;
@@ -30,7 +30,7 @@ struct ProfileData {
INSERT_PADDING_BYTES_NOINIT(0x10);
INSERT_PADDING_BYTES_NOINIT(0x60);
};
-static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
+static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
@@ -38,7 +38,7 @@ struct ProfileInfo {
Common::UUID user_uuid{};
ProfileUsername username{};
u64 creation_time{};
- ProfileData data{}; // TODO(ognik): Work out what this is
+ UserData data{}; // TODO(ognik): Work out what this is
bool is_open{};
};
@@ -64,9 +64,9 @@ public:
ProfileManager();
~ProfileManager();
- ResultCode AddUser(const ProfileInfo& user);
- ResultCode CreateNewUser(Common::UUID uuid, const ProfileUsername& username);
- ResultCode CreateNewUser(Common::UUID uuid, const std::string& username);
+ Result AddUser(const ProfileInfo& user);
+ Result CreateNewUser(Common::UUID uuid, const ProfileUsername& username);
+ Result CreateNewUser(Common::UUID uuid, const std::string& username);
std::optional<Common::UUID> GetUser(std::size_t index) const;
std::optional<std::size_t> GetUserIndex(const Common::UUID& uuid) const;
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
@@ -74,10 +74,9 @@ public:
bool GetProfileBase(Common::UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
- ProfileData& data) const;
- bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, ProfileData& data) const;
- bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
- ProfileData& data) const;
+ UserData& data) const;
+ bool GetProfileBaseAndData(Common::UUID uuid, ProfileBase& profile, UserData& data) const;
+ bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, UserData& data) const;
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
bool UserExists(Common::UUID uuid) const;
@@ -93,7 +92,7 @@ public:
bool RemoveUser(Common::UUID uuid);
bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
- const ProfileData& data_new);
+ const UserData& data_new);
private:
void ParseUserSaveFile();
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 773dc9f29..6fb7e198e 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1,12 +1,10 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstring>
-#include "audio_core/audio_renderer.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
@@ -41,9 +39,9 @@
namespace Service::AM {
-constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2};
-constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 3};
-constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503};
+constexpr Result ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 2};
+constexpr Result ERR_NO_MESSAGES{ErrorModule::AM, 3};
+constexpr Result ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 503};
enum class LaunchParameterKind : u32 {
ApplicationSpecific = 1,
@@ -239,6 +237,7 @@ IDebugFunctions::IDebugFunctions(Core::System& system_)
{130, nullptr, "FriendInvitationSetApplicationParameter"},
{131, nullptr, "FriendInvitationClearApplicationParameter"},
{132, nullptr, "FriendInvitationPushApplicationParameter"},
+ {140, nullptr, "RestrictPowerOperationForSecureLaunchModeForDebug"},
{900, nullptr, "GetGrcProcessLaunchedSystemEvent"},
};
// clang-format on
@@ -286,7 +285,7 @@ ISelfController::ISelfController(Core::System& system_, NVFlinger::NVFlinger& nv
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
{63, &ISelfController::GetIdleTimeDetectionExtension, "GetIdleTimeDetectionExtension"},
{64, nullptr, "SetInputDetectionSourceSet"},
- {65, nullptr, "ReportUserIsActive"},
+ {65, &ISelfController::ReportUserIsActive, "ReportUserIsActive"},
{66, nullptr, "GetCurrentIlluminance"},
{67, nullptr, "IsIlluminanceAvailable"},
{68, &ISelfController::SetAutoSleepDisabled, "SetAutoSleepDisabled"},
@@ -366,7 +365,7 @@ void ISelfController::LeaveFatalSection(Kernel::HLERequestContext& ctx) {
// Entry and exit of fatal sections must be balanced.
if (num_fatal_sections_entered == 0) {
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode{ErrorModule::AM, 512});
+ rb.Push(Result{ErrorModule::AM, 512});
return;
}
@@ -518,6 +517,13 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
rb.Push<u32>(idle_time_detection_extension);
}
+void ISelfController::ReportUserIsActive(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ISelfController::SetAutoSleepDisabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
is_auto_sleep_disabled = rp.Pop<bool>();
@@ -618,7 +624,7 @@ void AppletMessageQueue::PushMessage(AppletMessage msg) {
AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
if (messages.empty()) {
on_new_message->GetWritableEvent().Clear();
- return AppletMessage::NoMessage;
+ return AppletMessage::None;
}
auto msg = messages.front();
messages.pop();
@@ -633,7 +639,11 @@ std::size_t AppletMessageQueue::GetMessageCount() const {
}
void AppletMessageQueue::RequestExit() {
- PushMessage(AppletMessage::ExitRequested);
+ PushMessage(AppletMessage::Exit);
+}
+
+void AppletMessageQueue::RequestResume() {
+ PushMessage(AppletMessage::Resume);
}
void AppletMessageQueue::FocusStateChanged() {
@@ -687,7 +697,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
{67, nullptr, "CancelCpuBoostMode"},
{68, nullptr, "GetBuiltInDisplayType"},
- {80, nullptr, "PerformSystemButtonPressingIfInFocus"},
+ {80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"},
{90, nullptr, "SetPerformanceConfigurationChangedNotification"},
{91, nullptr, "GetCurrentPerformanceConfiguration"},
{100, nullptr, "SetHandlingHomeButtonShortPressedEnabled"},
@@ -732,7 +742,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
const auto message = msg_queue->PopMessage();
IPC::ResponseBuilder rb{ctx, 3};
- if (message == AppletMessageQueue::AppletMessage::NoMessage) {
+ if (message == AppletMessageQueue::AppletMessage::None) {
LOG_ERROR(Service_AM, "Message queue is empty");
rb.Push(ERR_NO_MESSAGES);
rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
@@ -744,7 +754,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -827,6 +837,16 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {
apm_sys->SetCpuBoostMode(ctx);
}
+void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto system_button{rp.PopEnum<SystemButtonType>()};
+
+ LOG_WARNING(Service_AM, "(STUBBED) called, system_button={}", system_button);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(
Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -980,7 +1000,7 @@ private:
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
- applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>());
+ applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1007,7 +1027,7 @@ private:
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
- applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>());
+ applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock());
ASSERT(applet->IsInitialized());
applet->ExecuteInteractive();
@@ -1301,6 +1321,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
{34, nullptr, "SelectApplicationLicense"},
{35, nullptr, "GetDeviceSaveDataSizeMax"},
+ {36, nullptr, "GetLimitedApplicationLicense"},
+ {37, nullptr, "GetLimitedApplicationLicenseUpgradableEvent"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -1337,7 +1359,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{200, nullptr, "GetLastApplicationExitReason"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
- {1001, nullptr, "PrepareForJit"},
+ {1001, &IApplicationFunctions::PrepareForJit, "PrepareForJit"},
};
// clang-format on
@@ -1787,6 +1809,13 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(Kernel::HLERe
rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
}
+void IApplicationFunctions::PrepareForJit(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
Core::System& system) {
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 2a578aea5..bb75c6281 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -22,6 +21,7 @@ class NVFlinger;
namespace Service::AM {
+// This is nn::settings::Language
enum SystemLanguage {
Japanese = 0,
English = 1, // en-US
@@ -41,16 +41,44 @@ enum SystemLanguage {
// 4.0.0+
SimplifiedChinese = 15,
TraditionalChinese = 16,
+ // 10.1.0+
+ BrazilianPortuguese = 17,
};
class AppletMessageQueue {
public:
+ // This is nn::am::AppletMessage
enum class AppletMessage : u32 {
- NoMessage = 0,
- ExitRequested = 4,
+ None = 0,
+ ChangeIntoForeground = 1,
+ ChangeIntoBackground = 2,
+ Exit = 4,
+ ApplicationExited = 6,
FocusStateChanged = 15,
+ Resume = 16,
+ DetectShortPressingHomeButton = 20,
+ DetectLongPressingHomeButton = 21,
+ DetectShortPressingPowerButton = 22,
+ DetectMiddlePressingPowerButton = 23,
+ DetectLongPressingPowerButton = 24,
+ RequestToPrepareSleep = 25,
+ FinishedSleepSequence = 26,
+ SleepRequiredByHighTemperature = 27,
+ SleepRequiredByLowBattery = 28,
+ AutoPowerDown = 29,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
+ DetectReceivingCecSystemStandby = 32,
+ SdCardRemoved = 33,
+ LaunchApplicationRequested = 50,
+ RequestToDisplay = 51,
+ ShowApplicationLogo = 55,
+ HideApplicationLogo = 56,
+ ForceHideApplicationLogo = 57,
+ FloatingApplicationDetected = 60,
+ DetectShortPressingCaptureButton = 90,
+ AlbumScreenShotTaken = 92,
+ AlbumRecordingSaved = 93,
};
explicit AppletMessageQueue(Core::System& system);
@@ -62,6 +90,7 @@ public:
AppletMessage PopMessage();
std::size_t GetMessageCount() const;
void RequestExit();
+ void RequestResume();
void FocusStateChanged();
void OperationModeChanged();
@@ -146,6 +175,7 @@ private:
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
+ void ReportUserIsActive(Kernel::HLERequestContext& ctx);
void SetAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void IsAutoSleepDisabled(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
@@ -179,16 +209,31 @@ public:
~ICommonStateGetter() override;
private:
+ // This is nn::oe::FocusState
enum class FocusState : u8 {
InFocus = 1,
NotInFocus = 2,
+ Background = 3,
};
+ // This is nn::oe::OperationMode
enum class OperationMode : u8 {
Handheld = 0,
Docked = 1,
};
+ // This is nn::am::service::SystemButtonType
+ enum class SystemButtonType {
+ None,
+ HomeButtonShortPressing,
+ HomeButtonLongPressing,
+ PowerButtonShortPressing,
+ PowerButtonLongPressing,
+ ShutdownSystem,
+ CaptureButtonShortPressing,
+ CaptureButtonLongPressing,
+ };
+
void GetEventHandle(Kernel::HLERequestContext& ctx);
void ReceiveMessage(Kernel::HLERequestContext& ctx);
void GetCurrentFocusState(Kernel::HLERequestContext& ctx);
@@ -203,6 +248,7 @@ private:
void EndVrModeEx(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);
+ void PerformSystemButtonPressingIfInFocus(Kernel::HLERequestContext& ctx);
void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(Kernel::HLERequestContext& ctx);
std::shared_ptr<AppletMessageQueue> msg_queue;
@@ -304,6 +350,7 @@ private:
void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx);
void GetNotificationStorageChannelEvent(Kernel::HLERequestContext& ctx);
void GetHealthWarningDisappearedSystemEvent(Kernel::HLERequestContext& ctx);
+ void PrepareForJit(Kernel::HLERequestContext& ctx);
KernelHelpers::ServiceContext service_context;
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 0ec4fd4ca..d7719da35 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index f89f65649..2147976a6 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index b8859f4e6..00fc4202c 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 64b874ead..8fea249f1 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/am/applets/applet_controller.cpp b/src/core/hle/service/am/applets/applet_controller.cpp
index d073f2210..b418031de 100644
--- a/src/core/hle/service/am/applets/applet_controller.cpp
+++ b/src/core/hle/service/am/applets/applet_controller.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -21,9 +20,9 @@
namespace Service::AM::Applets {
// This error code (0x183ACA) is thrown when the applet fails to initialize.
-[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
+[[maybe_unused]] constexpr Result ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2.
-[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
+[[maybe_unused]] constexpr Result ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
@@ -174,12 +173,12 @@ bool Controller::TransactionComplete() const {
return complete;
}
-ResultCode Controller::GetStatus() const {
+Result Controller::GetStatus() const {
return status;
}
void Controller::ExecuteInteractive() {
- UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+ ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void Controller::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h
index 1a832505e..1f9adec65 100644
--- a/src/core/hle/service/am/applets/applet_controller.h
+++ b/src/core/hle/service/am/applets/applet_controller.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -127,7 +126,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -144,7 +143,7 @@ private:
ControllerUpdateFirmwareArg controller_update_arg;
ControllerKeyRemappingArg controller_key_remapping_arg;
bool complete{false};
- ResultCode status{ResultSuccess};
+ Result status{ResultSuccess};
bool is_single_mode{false};
std::vector<u8> out_data;
};
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index a06c2b872..fcf34bf7e 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <cstring>
@@ -26,15 +25,15 @@ struct ErrorCode {
};
}
- static constexpr ErrorCode FromResultCode(ResultCode result) {
+ static constexpr ErrorCode FromResult(Result result) {
return {
.error_category{2000 + static_cast<u32>(result.module.Value())},
.error_number{result.description.Value()},
};
}
- constexpr ResultCode ToResultCode() const {
- return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number};
+ constexpr Result ToResult() const {
+ return Result{static_cast<ErrorModule>(error_category - 2000), error_number};
}
};
static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");
@@ -98,8 +97,8 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) {
std::memcpy(&variable, data.data(), sizeof(T));
}
-ResultCode Decode64BitError(u64 error) {
- return ErrorCode::FromU64(error).ToResultCode();
+Result Decode64BitError(u64 error) {
+ return ErrorCode::FromU64(error).ToResult();
}
} // Anonymous namespace
@@ -128,16 +127,16 @@ void Error::Initialize() {
if (args->error.use_64bit_error_code) {
error_code = Decode64BitError(args->error.error_code_64);
} else {
- error_code = ResultCode(args->error.error_code_32);
+ error_code = Result(args->error.error_code_32);
}
break;
case ErrorAppletMode::ShowSystemError:
CopyArgumentData(data, args->system_error);
- error_code = ResultCode(Decode64BitError(args->system_error.error_code_64));
+ error_code = Result(Decode64BitError(args->system_error.error_code_64));
break;
case ErrorAppletMode::ShowApplicationError:
CopyArgumentData(data, args->application_error);
- error_code = ResultCode(args->application_error.error_code);
+ error_code = Result(args->application_error.error_code);
break;
case ErrorAppletMode::ShowErrorRecord:
CopyArgumentData(data, args->error_record);
@@ -152,12 +151,12 @@ bool Error::TransactionComplete() const {
return complete;
}
-ResultCode Error::GetStatus() const {
+Result Error::GetStatus() const {
return ResultSuccess;
}
void Error::ExecuteInteractive() {
- UNREACHABLE_MSG("Unexpected interactive applet data!");
+ ASSERT_MSG(false, "Unexpected interactive applet data!");
}
void Error::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_error.h b/src/core/hle/service/am/applets/applet_error.h
index 8aa9046a5..d78d6f1d1 100644
--- a/src/core/hle/service/am/applets/applet_error.h
+++ b/src/core/hle/service/am/applets/applet_error.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -32,7 +31,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -42,7 +41,7 @@ private:
union ErrorArguments;
const Core::Frontend::ErrorApplet& frontend;
- ResultCode error_code = ResultSuccess;
+ Result error_code = ResultSuccess;
ErrorAppletMode mode = ErrorAppletMode::ShowError;
std::unique_ptr<ErrorArguments> args;
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp
index 2c6e9d83c..c34ef08b3 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/applets/applet_general_backend.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/hex_util.h"
@@ -14,7 +13,7 @@
namespace Service::AM::Applets {
-constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
+constexpr Result ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
std::shared_ptr<IStorage> storage = broker.PopNormalDataToApplet();
@@ -72,12 +71,12 @@ bool Auth::TransactionComplete() const {
return complete;
}
-ResultCode Auth::GetStatus() const {
+Result Auth::GetStatus() const {
return successful ? ResultSuccess : ERROR_INVALID_PIN;
}
void Auth::ExecuteInteractive() {
- UNREACHABLE_MSG("Unexpected interactive applet data.");
+ ASSERT_MSG(false, "Unexpected interactive applet data.");
}
void Auth::Execute() {
@@ -137,7 +136,7 @@ void Auth::AuthFinished(bool is_successful) {
successful = is_successful;
struct Return {
- ResultCode result_code;
+ Result result_code;
};
static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size.");
@@ -171,12 +170,12 @@ bool PhotoViewer::TransactionComplete() const {
return complete;
}
-ResultCode PhotoViewer::GetStatus() const {
+Result PhotoViewer::GetStatus() const {
return ResultSuccess;
}
void PhotoViewer::ExecuteInteractive() {
- UNREACHABLE_MSG("Unexpected interactive applet data.");
+ ASSERT_MSG(false, "Unexpected interactive applet data.");
}
void PhotoViewer::Execute() {
@@ -224,7 +223,7 @@ bool StubApplet::TransactionComplete() const {
return true;
}
-ResultCode StubApplet::GetStatus() const {
+Result StubApplet::GetStatus() const {
LOG_WARNING(Service_AM, "called (STUBBED)");
return ResultSuccess;
}
diff --git a/src/core/hle/service/am/applets/applet_general_backend.h b/src/core/hle/service/am/applets/applet_general_backend.h
index 7496ded88..a9f2535a2 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.h
+++ b/src/core/hle/service/am/applets/applet_general_backend.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -26,7 +25,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -57,7 +56,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -78,7 +77,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
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..ae80ef506
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit.cpp
@@ -0,0 +1,138 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/frontend/applets/mii_edit.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/applet_mii_edit.h"
+#include "core/hle/service/mii/mii_manager.h"
+
+namespace Service::AM::Applets {
+
+MiiEdit::MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
+ const Core::Frontend::MiiEditApplet& frontend_)
+ : Applet{system_, applet_mode_}, frontend{frontend_}, system{system_} {}
+
+MiiEdit::~MiiEdit() = default;
+
+void MiiEdit::Initialize() {
+ // Note: MiiEdit is not initialized with common arguments.
+ // Instead, it is initialized by an AppletInput storage with size 0x100 bytes.
+ // Do NOT call Applet::Initialize() here.
+
+ const auto storage = broker.PopNormalDataToApplet();
+ ASSERT(storage != nullptr);
+
+ const auto applet_input_data = storage->GetData();
+ ASSERT(applet_input_data.size() >= sizeof(MiiEditAppletInputCommon));
+
+ std::memcpy(&applet_input_common, applet_input_data.data(), sizeof(MiiEditAppletInputCommon));
+
+ LOG_INFO(Service_AM,
+ "Initializing MiiEdit Applet with MiiEditAppletVersion={} and MiiEditAppletMode={}",
+ applet_input_common.version, applet_input_common.applet_mode);
+
+ switch (applet_input_common.version) {
+ case MiiEditAppletVersion::Version3:
+ ASSERT(applet_input_data.size() ==
+ sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV3));
+ std::memcpy(&applet_input_v3, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
+ sizeof(MiiEditAppletInputV3));
+ break;
+ case MiiEditAppletVersion::Version4:
+ ASSERT(applet_input_data.size() ==
+ sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
+ std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
+ sizeof(MiiEditAppletInputV4));
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown MiiEditAppletVersion={} with size={}",
+ applet_input_common.version, applet_input_data.size());
+ ASSERT(applet_input_data.size() >=
+ sizeof(MiiEditAppletInputCommon) + sizeof(MiiEditAppletInputV4));
+ std::memcpy(&applet_input_v4, applet_input_data.data() + sizeof(MiiEditAppletInputCommon),
+ sizeof(MiiEditAppletInputV4));
+ break;
+ }
+}
+
+bool MiiEdit::TransactionComplete() const {
+ return is_complete;
+}
+
+Result MiiEdit::GetStatus() const {
+ return ResultSuccess;
+}
+
+void MiiEdit::ExecuteInteractive() {
+ ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
+}
+
+void MiiEdit::Execute() {
+ if (is_complete) {
+ return;
+ }
+
+ // This is a default stub for each of the MiiEdit applet modes.
+ switch (applet_input_common.applet_mode) {
+ case MiiEditAppletMode::ShowMiiEdit:
+ case MiiEditAppletMode::AppendMii:
+ case MiiEditAppletMode::AppendMiiImage:
+ case MiiEditAppletMode::UpdateMiiImage:
+ MiiEditOutput(MiiEditResult::Success, 0);
+ break;
+ case MiiEditAppletMode::CreateMii:
+ case MiiEditAppletMode::EditMii: {
+ Service::Mii::MiiManager mii_manager;
+
+ const MiiEditCharInfo char_info{
+ .mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
+ ? applet_input_v4.char_info.mii_info
+ : mii_manager.BuildDefault(0)},
+ };
+
+ MiiEditOutputForCharInfoEditing(MiiEditResult::Success, char_info);
+ break;
+ }
+ default:
+ UNIMPLEMENTED_MSG("Unknown MiiEditAppletMode={}", applet_input_common.applet_mode);
+
+ MiiEditOutput(MiiEditResult::Success, 0);
+ break;
+ }
+}
+
+void MiiEdit::MiiEditOutput(MiiEditResult result, s32 index) {
+ const MiiEditAppletOutput applet_output{
+ .result{result},
+ .index{index},
+ };
+
+ std::vector<u8> out_data(sizeof(MiiEditAppletOutput));
+ std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutput));
+
+ is_complete = true;
+
+ broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
+ broker.SignalStateChanged();
+}
+
+void MiiEdit::MiiEditOutputForCharInfoEditing(MiiEditResult result,
+ const MiiEditCharInfo& char_info) {
+ const MiiEditAppletOutputForCharInfoEditing applet_output{
+ .result{result},
+ .char_info{char_info},
+ };
+
+ std::vector<u8> out_data(sizeof(MiiEditAppletOutputForCharInfoEditing));
+ std::memcpy(out_data.data(), &applet_output, sizeof(MiiEditAppletOutputForCharInfoEditing));
+
+ is_complete = true;
+
+ broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
+ broker.SignalStateChanged();
+}
+
+} // 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..d18dd3cf5
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit.h
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+#include "core/hle/service/am/applets/applet_mii_edit_types.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace Service::AM::Applets {
+
+class MiiEdit final : public Applet {
+public:
+ explicit MiiEdit(Core::System& system_, LibraryAppletMode applet_mode_,
+ const Core::Frontend::MiiEditApplet& frontend_);
+ ~MiiEdit() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ Result GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void MiiEditOutput(MiiEditResult result, s32 index);
+
+ void MiiEditOutputForCharInfoEditing(MiiEditResult result, const MiiEditCharInfo& char_info);
+
+private:
+ const Core::Frontend::MiiEditApplet& frontend;
+ Core::System& system;
+
+ MiiEditAppletInputCommon applet_input_common{};
+ MiiEditAppletInputV3 applet_input_v3{};
+ MiiEditAppletInputV4 applet_input_v4{};
+
+ bool is_complete{false};
+};
+
+} // 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..4705d019f
--- /dev/null
+++ b/src/core/hle/service/am/applets/applet_mii_edit_types.h
@@ -0,0 +1,82 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/service/mii/types.h"
+
+namespace Service::AM::Applets {
+
+enum class MiiEditAppletVersion : s32 {
+ Version3 = 0x3, // 1.0.0 - 10.1.1
+ Version4 = 0x4, // 10.2.0+
+};
+
+// This is nn::mii::AppletMode
+enum class MiiEditAppletMode : u32 {
+ ShowMiiEdit = 0,
+ AppendMii = 1,
+ AppendMiiImage = 2,
+ UpdateMiiImage = 3,
+ CreateMii = 4,
+ EditMii = 5,
+};
+
+enum class MiiEditResult : u32 {
+ Success,
+ Cancel,
+};
+
+struct MiiEditCharInfo {
+ Service::Mii::CharInfo mii_info{};
+};
+static_assert(sizeof(MiiEditCharInfo) == 0x58, "MiiEditCharInfo has incorrect size.");
+
+struct MiiEditAppletInputCommon {
+ MiiEditAppletVersion version{};
+ MiiEditAppletMode applet_mode{};
+};
+static_assert(sizeof(MiiEditAppletInputCommon) == 0x8,
+ "MiiEditAppletInputCommon has incorrect size.");
+
+struct MiiEditAppletInputV3 {
+ u32 special_mii_key_code{};
+ std::array<Common::UUID, 8> valid_uuids{};
+ Common::UUID used_uuid{};
+ INSERT_PADDING_BYTES(0x64);
+};
+static_assert(sizeof(MiiEditAppletInputV3) == 0x100 - sizeof(MiiEditAppletInputCommon),
+ "MiiEditAppletInputV3 has incorrect size.");
+
+struct MiiEditAppletInputV4 {
+ u32 special_mii_key_code{};
+ MiiEditCharInfo char_info{};
+ INSERT_PADDING_BYTES(0x28);
+ Common::UUID used_uuid{};
+ INSERT_PADDING_BYTES(0x64);
+};
+static_assert(sizeof(MiiEditAppletInputV4) == 0x100 - sizeof(MiiEditAppletInputCommon),
+ "MiiEditAppletInputV4 has incorrect size.");
+
+// This is nn::mii::AppletOutput
+struct MiiEditAppletOutput {
+ MiiEditResult result{};
+ s32 index{};
+ INSERT_PADDING_BYTES(0x18);
+};
+static_assert(sizeof(MiiEditAppletOutput) == 0x20, "MiiEditAppletOutput has incorrect size.");
+
+// This is nn::mii::AppletOutputForCharInfoEditing
+struct MiiEditAppletOutputForCharInfoEditing {
+ MiiEditResult result{};
+ MiiEditCharInfo char_info{};
+ INSERT_PADDING_BYTES(0x24);
+};
+static_assert(sizeof(MiiEditAppletOutputForCharInfoEditing) == 0x80,
+ "MiiEditAppletOutputForCharInfoEditing has incorrect size.");
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applet_profile_select.cpp b/src/core/hle/service/am/applets/applet_profile_select.cpp
index 82500e121..c738db028 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.cpp
+++ b/src/core/hle/service/am/applets/applet_profile_select.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
@@ -13,7 +12,7 @@
namespace Service::AM::Applets {
-constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
+constexpr Result ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_,
const Core::Frontend::ProfileSelectApplet& frontend_)
@@ -40,12 +39,12 @@ bool ProfileSelect::TransactionComplete() const {
return complete;
}
-ResultCode ProfileSelect::GetStatus() const {
+Result ProfileSelect::GetStatus() const {
return status;
}
void ProfileSelect::ExecuteInteractive() {
- UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+ ASSERT_MSG(false, "Attempted to call interactive execution on non-interactive applet.");
}
void ProfileSelect::Execute() {
diff --git a/src/core/hle/service/am/applets/applet_profile_select.h b/src/core/hle/service/am/applets/applet_profile_select.h
index 852e1e0c0..b77f1d205 100644
--- a/src/core/hle/service/am/applets/applet_profile_select.h
+++ b/src/core/hle/service/am/applets/applet_profile_select.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -40,7 +39,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -51,7 +50,7 @@ private:
UserSelectionConfig config;
bool complete = false;
- ResultCode status = ResultSuccess;
+ Result status = ResultSuccess;
std::vector<u8> final_data;
Core::System& system;
};
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..c18236045 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/string_util.h"
#include "core/core.h"
@@ -72,7 +71,7 @@ void SoftwareKeyboard::Initialize() {
InitializeBackground(applet_mode);
break;
default:
- UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode);
+ ASSERT_MSG(false, "Invalid LibraryAppletMode={}", applet_mode);
break;
}
}
@@ -81,7 +80,7 @@ bool SoftwareKeyboard::TransactionComplete() const {
return complete;
}
-ResultCode SoftwareKeyboard::GetStatus() const {
+Result SoftwareKeyboard::GetStatus() const {
return status;
}
@@ -226,7 +225,7 @@ void SoftwareKeyboard::InitializeForeground() {
ASSERT(work_buffer_storage != nullptr);
if (swkbd_config_common.initial_string_length == 0) {
- InitializeFrontendKeyboard();
+ InitializeFrontendNormalKeyboard();
return;
}
@@ -243,7 +242,7 @@ void SoftwareKeyboard::InitializeForeground() {
LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
- InitializeFrontendKeyboard();
+ InitializeFrontendNormalKeyboard();
}
void SoftwareKeyboard::InitializeBackground(LibraryAppletMode library_applet_mode) {
@@ -480,129 +479,179 @@ void SoftwareKeyboard::ChangeState(SwkbdState state) {
ReplyDefault();
}
-void SoftwareKeyboard::InitializeFrontendKeyboard() {
- if (is_background) {
- const auto& appear_arg = swkbd_calc_arg.appear_arg;
-
- std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- appear_arg.ok_text.data(), appear_arg.ok_text.size());
-
- const u32 max_text_length =
- appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
- ? appear_arg.max_text_length
- : DEFAULT_MAX_TEXT_LENGTH;
-
- const u32 min_text_length =
- appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
-
- const s32 initial_cursor_position =
- current_cursor_position > 0 ? current_cursor_position : 0;
-
- const auto text_draw_type =
- max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
-
- Core::Frontend::KeyboardInitializeParameters initialize_parameters{
- .ok_text{std::move(ok_text)},
- .header_text{},
- .sub_text{},
- .guide_text{},
- .initial_text{current_text},
- .max_text_length{max_text_length},
- .min_text_length{min_text_length},
- .initial_cursor_position{initial_cursor_position},
- .type{appear_arg.type},
- .password_mode{SwkbdPasswordMode::Disabled},
- .text_draw_type{text_draw_type},
- .key_disable_flags{appear_arg.key_disable_flags},
- .use_blur_background{false},
- .enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
- .enable_return_button{appear_arg.enable_return_button},
- .disable_cancel_button{appear_arg.disable_cancel_button},
- };
-
- frontend.InitializeKeyboard(
- true, std::move(initialize_parameters), {},
- [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
- SubmitTextInline(reply_type, submitted_text, cursor_position);
- });
- } else {
- std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
-
- std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
-
- std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
-
- std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
-
- const u32 max_text_length =
- swkbd_config_common.max_text_length > 0 &&
- swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
- ? swkbd_config_common.max_text_length
- : DEFAULT_MAX_TEXT_LENGTH;
-
- const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
- ? swkbd_config_common.min_text_length
- : 0;
-
- const s32 initial_cursor_position = [this] {
- switch (swkbd_config_common.initial_cursor_position) {
- case SwkbdInitialCursorPosition::Start:
- default:
- return 0;
- case SwkbdInitialCursorPosition::End:
- return static_cast<s32>(initial_text.size());
- }
- }();
-
- const auto text_draw_type = [this, max_text_length] {
- switch (swkbd_config_common.text_draw_type) {
- case SwkbdTextDrawType::Line:
- default:
- return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
- case SwkbdTextDrawType::Box:
- case SwkbdTextDrawType::DownloadCode:
- return swkbd_config_common.text_draw_type;
- }
- }();
-
- const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box
- ? swkbd_config_common.enable_return_button
- : false;
-
- const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
- ? swkbd_config_new.disable_cancel_button
- : false;
-
- Core::Frontend::KeyboardInitializeParameters initialize_parameters{
- .ok_text{std::move(ok_text)},
- .header_text{std::move(header_text)},
- .sub_text{std::move(sub_text)},
- .guide_text{std::move(guide_text)},
- .initial_text{initial_text},
- .max_text_length{max_text_length},
- .min_text_length{min_text_length},
- .initial_cursor_position{initial_cursor_position},
- .type{swkbd_config_common.type},
- .password_mode{swkbd_config_common.password_mode},
- .text_draw_type{text_draw_type},
- .key_disable_flags{swkbd_config_common.key_disable_flags},
- .use_blur_background{swkbd_config_common.use_blur_background},
- .enable_backspace_button{true},
- .enable_return_button{enable_return_button},
- .disable_cancel_button{disable_cancel_button},
- };
-
- frontend.InitializeKeyboard(
- false, std::move(initialize_parameters),
- [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
- SubmitTextNormal(result, submitted_text, confirmed);
- },
- {});
- }
+void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
+ std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
+
+ std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
+
+ std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
+
+ std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
+
+ const u32 max_text_length =
+ swkbd_config_common.max_text_length > 0 &&
+ swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+ ? swkbd_config_common.max_text_length
+ : DEFAULT_MAX_TEXT_LENGTH;
+
+ const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
+ ? swkbd_config_common.min_text_length
+ : 0;
+
+ const s32 initial_cursor_position = [this] {
+ switch (swkbd_config_common.initial_cursor_position) {
+ case SwkbdInitialCursorPosition::Start:
+ default:
+ return 0;
+ case SwkbdInitialCursorPosition::End:
+ return static_cast<s32>(initial_text.size());
+ }
+ }();
+
+ const auto text_draw_type = [this, max_text_length] {
+ switch (swkbd_config_common.text_draw_type) {
+ case SwkbdTextDrawType::Line:
+ default:
+ return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
+ case SwkbdTextDrawType::Box:
+ case SwkbdTextDrawType::DownloadCode:
+ return swkbd_config_common.text_draw_type;
+ }
+ }();
+
+ const auto enable_return_button =
+ text_draw_type == SwkbdTextDrawType::Box ? swkbd_config_common.enable_return_button : false;
+
+ const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
+ ? swkbd_config_new.disable_cancel_button
+ : false;
+
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters{
+ .ok_text{std::move(ok_text)},
+ .header_text{std::move(header_text)},
+ .sub_text{std::move(sub_text)},
+ .guide_text{std::move(guide_text)},
+ .initial_text{initial_text},
+ .left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key},
+ .right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key},
+ .max_text_length{max_text_length},
+ .min_text_length{min_text_length},
+ .initial_cursor_position{initial_cursor_position},
+ .type{swkbd_config_common.type},
+ .password_mode{swkbd_config_common.password_mode},
+ .text_draw_type{text_draw_type},
+ .key_disable_flags{swkbd_config_common.key_disable_flags},
+ .use_blur_background{swkbd_config_common.use_blur_background},
+ .enable_backspace_button{true},
+ .enable_return_button{enable_return_button},
+ .disable_cancel_button{disable_cancel_button},
+ };
+
+ frontend.InitializeKeyboard(
+ false, std::move(initialize_parameters),
+ [this](SwkbdResult result, std::u16string submitted_text, bool confirmed) {
+ SubmitTextNormal(result, submitted_text, confirmed);
+ },
+ {});
+}
+
+void SoftwareKeyboard::InitializeFrontendInlineKeyboard(
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
+ frontend.InitializeKeyboard(
+ true, std::move(initialize_parameters), {},
+ [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
+ SubmitTextInline(reply_type, submitted_text, cursor_position);
+ });
+}
+
+void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
+ const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
+
+ std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ appear_arg.ok_text.data(), appear_arg.ok_text.size());
+
+ const u32 max_text_length =
+ appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+ ? appear_arg.max_text_length
+ : DEFAULT_MAX_TEXT_LENGTH;
+
+ const u32 min_text_length =
+ appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
+
+ const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
+
+ const auto text_draw_type =
+ max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
+
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters{
+ .ok_text{std::move(ok_text)},
+ .header_text{},
+ .sub_text{},
+ .guide_text{},
+ .initial_text{current_text},
+ .left_optional_symbol_key{appear_arg.left_optional_symbol_key},
+ .right_optional_symbol_key{appear_arg.right_optional_symbol_key},
+ .max_text_length{max_text_length},
+ .min_text_length{min_text_length},
+ .initial_cursor_position{initial_cursor_position},
+ .type{appear_arg.type},
+ .password_mode{SwkbdPasswordMode::Disabled},
+ .text_draw_type{text_draw_type},
+ .key_disable_flags{appear_arg.key_disable_flags},
+ .use_blur_background{false},
+ .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
+ .enable_return_button{appear_arg.enable_return_button},
+ .disable_cancel_button{appear_arg.disable_cancel_button},
+ };
+
+ InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
+}
+
+void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
+ const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
+
+ std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ appear_arg.ok_text.data(), appear_arg.ok_text.size());
+
+ const u32 max_text_length =
+ appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+ ? appear_arg.max_text_length
+ : DEFAULT_MAX_TEXT_LENGTH;
+
+ const u32 min_text_length =
+ appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
+
+ const s32 initial_cursor_position = current_cursor_position > 0 ? current_cursor_position : 0;
+
+ const auto text_draw_type =
+ max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
+
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters{
+ .ok_text{std::move(ok_text)},
+ .header_text{},
+ .sub_text{},
+ .guide_text{},
+ .initial_text{current_text},
+ .left_optional_symbol_key{appear_arg.left_optional_symbol_key},
+ .right_optional_symbol_key{appear_arg.right_optional_symbol_key},
+ .max_text_length{max_text_length},
+ .min_text_length{min_text_length},
+ .initial_cursor_position{initial_cursor_position},
+ .type{appear_arg.type},
+ .password_mode{SwkbdPasswordMode::Disabled},
+ .text_draw_type{text_draw_type},
+ .key_disable_flags{appear_arg.key_disable_flags},
+ .use_blur_background{false},
+ .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
+ .enable_return_button{appear_arg.enable_return_button},
+ .disable_cancel_button{appear_arg.disable_cancel_button},
+ };
+
+ InitializeFrontendInlineKeyboard(std::move(initialize_parameters));
}
void SoftwareKeyboard::ShowNormalKeyboard() {
@@ -614,14 +663,21 @@ void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_resul
frontend.ShowTextCheckDialog(text_check_result, std::move(text_check_message));
}
-void SoftwareKeyboard::ShowInlineKeyboard() {
+void SoftwareKeyboard::ShowInlineKeyboard(
+ Core::Frontend::InlineAppearParameters appear_parameters) {
+ frontend.ShowInlineKeyboard(std::move(appear_parameters));
+
+ ChangeState(SwkbdState::InitializedIsShown);
+}
+
+void SoftwareKeyboard::ShowInlineKeyboardOld() {
if (swkbd_state != SwkbdState::InitializedIsHidden) {
return;
}
ChangeState(SwkbdState::InitializedIsAppearing);
- const auto& appear_arg = swkbd_calc_arg.appear_arg;
+ const auto& appear_arg = swkbd_calc_arg_old.appear_arg;
const u32 max_text_length =
appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
@@ -634,21 +690,54 @@ void SoftwareKeyboard::ShowInlineKeyboard() {
Core::Frontend::InlineAppearParameters appear_parameters{
.max_text_length{max_text_length},
.min_text_length{min_text_length},
- .key_top_scale_x{swkbd_calc_arg.key_top_scale_x},
- .key_top_scale_y{swkbd_calc_arg.key_top_scale_y},
- .key_top_translate_x{swkbd_calc_arg.key_top_translate_x},
- .key_top_translate_y{swkbd_calc_arg.key_top_translate_y},
+ .key_top_scale_x{swkbd_calc_arg_old.key_top_scale_x},
+ .key_top_scale_y{swkbd_calc_arg_old.key_top_scale_y},
+ .key_top_translate_x{swkbd_calc_arg_old.key_top_translate_x},
+ .key_top_translate_y{swkbd_calc_arg_old.key_top_translate_y},
.type{appear_arg.type},
.key_disable_flags{appear_arg.key_disable_flags},
- .key_top_as_floating{swkbd_calc_arg.key_top_as_floating},
- .enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
+ .key_top_as_floating{swkbd_calc_arg_old.key_top_as_floating},
+ .enable_backspace_button{swkbd_calc_arg_old.enable_backspace_button},
.enable_return_button{appear_arg.enable_return_button},
.disable_cancel_button{appear_arg.disable_cancel_button},
};
- frontend.ShowInlineKeyboard(std::move(appear_parameters));
+ ShowInlineKeyboard(std::move(appear_parameters));
+}
- ChangeState(SwkbdState::InitializedIsShown);
+void SoftwareKeyboard::ShowInlineKeyboardNew() {
+ if (swkbd_state != SwkbdState::InitializedIsHidden) {
+ return;
+ }
+
+ ChangeState(SwkbdState::InitializedIsAppearing);
+
+ const auto& appear_arg = swkbd_calc_arg_new.appear_arg;
+
+ const u32 max_text_length =
+ appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+ ? appear_arg.max_text_length
+ : DEFAULT_MAX_TEXT_LENGTH;
+
+ const u32 min_text_length =
+ appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
+
+ Core::Frontend::InlineAppearParameters appear_parameters{
+ .max_text_length{max_text_length},
+ .min_text_length{min_text_length},
+ .key_top_scale_x{swkbd_calc_arg_new.key_top_scale_x},
+ .key_top_scale_y{swkbd_calc_arg_new.key_top_scale_y},
+ .key_top_translate_x{swkbd_calc_arg_new.key_top_translate_x},
+ .key_top_translate_y{swkbd_calc_arg_new.key_top_translate_y},
+ .type{appear_arg.type},
+ .key_disable_flags{appear_arg.key_disable_flags},
+ .key_top_as_floating{swkbd_calc_arg_new.key_top_as_floating},
+ .enable_backspace_button{swkbd_calc_arg_new.enable_backspace_button},
+ .enable_return_button{appear_arg.enable_return_button},
+ .disable_cancel_button{appear_arg.disable_cancel_button},
+ };
+
+ ShowInlineKeyboard(std::move(appear_parameters));
}
void SoftwareKeyboard::HideInlineKeyboard() {
@@ -693,6 +782,8 @@ void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
+
+ ReplyReleasedUserWordInfo();
}
void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
@@ -702,53 +793,135 @@ void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_dat
void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
LOG_DEBUG(Service_AM, "Processing Request: Calc");
- ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg));
+ ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon));
+
+ std::memcpy(&swkbd_calc_arg_common, request_data.data() + sizeof(SwkbdRequestCommand),
+ sizeof(SwkbdCalcArgCommon));
+
+ switch (swkbd_calc_arg_common.calc_arg_size) {
+ case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld):
+ ASSERT(request_data.size() ==
+ sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgOld));
+ std::memcpy(&swkbd_calc_arg_old,
+ request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
+ sizeof(SwkbdCalcArgOld));
+ RequestCalcOld();
+ break;
+ case sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew):
+ ASSERT(request_data.size() ==
+ sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
+ std::memcpy(&swkbd_calc_arg_new,
+ request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
+ sizeof(SwkbdCalcArgNew));
+ RequestCalcNew();
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown SwkbdCalcArg size={}", swkbd_calc_arg_common.calc_arg_size);
+ ASSERT(request_data.size() >=
+ sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon) + sizeof(SwkbdCalcArgNew));
+ std::memcpy(&swkbd_calc_arg_new,
+ request_data.data() + sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArgCommon),
+ sizeof(SwkbdCalcArgNew));
+ RequestCalcNew();
+ break;
+ }
+}
+
+void SoftwareKeyboard::RequestCalcOld() {
+ if (swkbd_calc_arg_common.flags.set_input_text) {
+ current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ swkbd_calc_arg_old.input_text.data(), swkbd_calc_arg_old.input_text.size());
+ }
+
+ if (swkbd_calc_arg_common.flags.set_cursor_position) {
+ current_cursor_position = swkbd_calc_arg_old.cursor_position;
+ }
+
+ if (swkbd_calc_arg_common.flags.set_utf8_mode) {
+ inline_use_utf8 = swkbd_calc_arg_old.utf8_mode;
+ }
- std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand),
- sizeof(SwkbdCalcArg));
+ if (swkbd_state <= SwkbdState::InitializedIsHidden &&
+ swkbd_calc_arg_common.flags.unset_customize_dic) {
+ ReplyUnsetCustomizeDic();
+ }
+
+ if (swkbd_state <= SwkbdState::InitializedIsHidden &&
+ swkbd_calc_arg_common.flags.unset_user_word_info) {
+ ReplyReleasedUserWordInfo();
+ }
+
+ if (swkbd_state == SwkbdState::NotInitialized &&
+ swkbd_calc_arg_common.flags.set_initialize_arg) {
+ InitializeFrontendInlineKeyboardOld();
+
+ ChangeState(SwkbdState::InitializedIsHidden);
+
+ ReplyFinishedInitialize();
+ }
+
+ if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
+ (swkbd_calc_arg_common.flags.set_input_text ||
+ swkbd_calc_arg_common.flags.set_cursor_position)) {
+ InlineTextChanged();
+ }
+
+ if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
+ ShowInlineKeyboardOld();
+ return;
+ }
+
+ if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
+ HideInlineKeyboard();
+ return;
+ }
+}
- if (swkbd_calc_arg.flags.set_input_text) {
+void SoftwareKeyboard::RequestCalcNew() {
+ if (swkbd_calc_arg_common.flags.set_input_text) {
current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
- swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size());
+ swkbd_calc_arg_new.input_text.data(), swkbd_calc_arg_new.input_text.size());
}
- if (swkbd_calc_arg.flags.set_cursor_position) {
- current_cursor_position = swkbd_calc_arg.cursor_position;
+ if (swkbd_calc_arg_common.flags.set_cursor_position) {
+ current_cursor_position = swkbd_calc_arg_new.cursor_position;
}
- if (swkbd_calc_arg.flags.set_utf8_mode) {
- inline_use_utf8 = swkbd_calc_arg.utf8_mode;
+ if (swkbd_calc_arg_common.flags.set_utf8_mode) {
+ inline_use_utf8 = swkbd_calc_arg_new.utf8_mode;
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
- swkbd_calc_arg.flags.unset_customize_dic) {
+ swkbd_calc_arg_common.flags.unset_customize_dic) {
ReplyUnsetCustomizeDic();
}
if (swkbd_state <= SwkbdState::InitializedIsHidden &&
- swkbd_calc_arg.flags.unset_user_word_info) {
+ swkbd_calc_arg_common.flags.unset_user_word_info) {
ReplyReleasedUserWordInfo();
}
- if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) {
- InitializeFrontendKeyboard();
+ if (swkbd_state == SwkbdState::NotInitialized &&
+ swkbd_calc_arg_common.flags.set_initialize_arg) {
+ InitializeFrontendInlineKeyboardNew();
ChangeState(SwkbdState::InitializedIsHidden);
ReplyFinishedInitialize();
}
- if (!swkbd_calc_arg.flags.set_initialize_arg &&
- (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) {
+ if (!swkbd_calc_arg_common.flags.set_initialize_arg &&
+ (swkbd_calc_arg_common.flags.set_input_text ||
+ swkbd_calc_arg_common.flags.set_cursor_position)) {
InlineTextChanged();
}
- if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) {
- ShowInlineKeyboard();
+ if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg_common.flags.appear) {
+ ShowInlineKeyboardNew();
return;
}
- if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) {
+ if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg_common.flags.disappear) {
HideInlineKeyboard();
return;
}
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..b01b31c98 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.h
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -13,6 +12,11 @@ namespace Core {
class System;
}
+namespace Core::Frontend {
+struct KeyboardInitializeParameters;
+struct InlineAppearParameters;
+} // namespace Core::Frontend
+
namespace Service::AM::Applets {
class SoftwareKeyboard final : public Applet {
@@ -24,7 +28,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -78,13 +82,22 @@ private:
void ChangeState(SwkbdState state);
/**
- * Signals the frontend to initialize the software keyboard with common parameters.
- * This initializes either the normal software keyboard or the inline software keyboard
- * depending on the state of is_background.
+ * Signals the frontend to initialize the normal software keyboard with common parameters.
* Note that this does not cause the keyboard to appear.
- * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear.
+ * Use the ShowNormalKeyboard() functions to cause the keyboard to appear.
*/
- void InitializeFrontendKeyboard();
+ void InitializeFrontendNormalKeyboard();
+
+ /**
+ * Signals the frontend to initialize the inline software keyboard with common parameters.
+ * Note that this does not cause the keyboard to appear.
+ * Use the ShowInlineKeyboard() to cause the keyboard to appear.
+ */
+ void InitializeFrontendInlineKeyboard(
+ Core::Frontend::KeyboardInitializeParameters initialize_parameters);
+
+ void InitializeFrontendInlineKeyboardOld();
+ void InitializeFrontendInlineKeyboardNew();
/// Signals the frontend to show the normal software keyboard.
void ShowNormalKeyboard();
@@ -94,7 +107,10 @@ private:
std::u16string text_check_message);
/// Signals the frontend to show the inline software keyboard.
- void ShowInlineKeyboard();
+ void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters);
+
+ void ShowInlineKeyboardOld();
+ void ShowInlineKeyboardNew();
/// Signals the frontend to hide the inline software keyboard.
void HideInlineKeyboard();
@@ -111,6 +127,8 @@ private:
void RequestSetUserWordInfo(const std::vector<u8>& request_data);
void RequestSetCustomizeDic(const std::vector<u8>& request_data);
void RequestCalc(const std::vector<u8>& request_data);
+ void RequestCalcOld();
+ void RequestCalcNew();
void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
@@ -149,7 +167,9 @@ private:
SwkbdState swkbd_state{SwkbdState::NotInitialized};
SwkbdInitializeArg swkbd_initialize_arg;
- SwkbdCalcArg swkbd_calc_arg;
+ SwkbdCalcArgCommon swkbd_calc_arg_common;
+ SwkbdCalcArgOld swkbd_calc_arg_old;
+ SwkbdCalcArgNew swkbd_calc_arg_new;
bool use_changed_string_v2{false};
bool use_moved_cursor_v2{false};
bool inline_use_utf8{false};
@@ -160,7 +180,7 @@ private:
bool is_background{false};
bool complete{false};
- ResultCode status{ResultSuccess};
+ Result status{ResultSuccess};
};
} // namespace Service::AM::Applets
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..1f696900e 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
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,6 +9,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "common/uuid.h"
namespace Service::AM::Applets {
@@ -216,7 +216,7 @@ struct SwkbdInitializeArg {
};
static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
-struct SwkbdAppearArg {
+struct SwkbdAppearArgOld {
SwkbdType type{};
std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
char16_t left_optional_symbol_key{};
@@ -229,19 +229,76 @@ struct SwkbdAppearArg {
bool enable_return_button{};
INSERT_PADDING_BYTES(3);
u32 flags{};
- INSERT_PADDING_WORDS(6);
+ bool is_use_save_data{};
+ INSERT_PADDING_BYTES(7);
+ Common::UUID user_id{};
};
-static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size.");
+static_assert(sizeof(SwkbdAppearArgOld) == 0x48, "SwkbdAppearArg has incorrect size.");
-struct SwkbdCalcArg {
+struct SwkbdAppearArgNew {
+ SwkbdType type{};
+ std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
+ char16_t left_optional_symbol_key{};
+ char16_t right_optional_symbol_key{};
+ bool use_prediction{};
+ bool disable_cancel_button{};
+ SwkbdKeyDisableFlags key_disable_flags{};
+ u32 max_text_length{};
+ u32 min_text_length{};
+ bool enable_return_button{};
+ INSERT_PADDING_BYTES(3);
+ u32 flags{};
+ bool is_use_save_data{};
+ INSERT_PADDING_BYTES(7);
+ Common::UUID user_id{};
+ u64 start_sampling_number{};
+ INSERT_PADDING_WORDS(8);
+};
+static_assert(sizeof(SwkbdAppearArgNew) == 0x70, "SwkbdAppearArg has incorrect size.");
+
+struct SwkbdCalcArgCommon {
u32 unknown{};
u16 calc_arg_size{};
INSERT_PADDING_BYTES(2);
SwkbdCalcArgFlags flags{};
SwkbdInitializeArg initialize_arg{};
+};
+static_assert(sizeof(SwkbdCalcArgCommon) == 0x18, "SwkbdCalcArgCommon has incorrect size.");
+
+struct SwkbdCalcArgOld {
+ f32 volume{};
+ s32 cursor_position{};
+ SwkbdAppearArgOld appear_arg{};
+ std::array<char16_t, 0x1FA> input_text{};
+ bool utf8_mode{};
+ INSERT_PADDING_BYTES(1);
+ bool enable_backspace_button{};
+ INSERT_PADDING_BYTES(3);
+ bool key_top_as_floating{};
+ bool footer_scalable{};
+ bool alpha_enabled_in_input_mode{};
+ u8 input_mode_fade_type{};
+ bool disable_touch{};
+ bool disable_hardware_keyboard{};
+ INSERT_PADDING_BYTES(8);
+ f32 key_top_scale_x{};
+ f32 key_top_scale_y{};
+ f32 key_top_translate_x{};
+ f32 key_top_translate_y{};
+ f32 key_top_bg_alpha{};
+ f32 footer_bg_alpha{};
+ f32 balloon_scale{};
+ INSERT_PADDING_WORDS(4);
+ u8 se_group{};
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(SwkbdCalcArgOld) == 0x4A0 - sizeof(SwkbdCalcArgCommon),
+ "SwkbdCalcArgOld has incorrect size.");
+
+struct SwkbdCalcArgNew {
+ SwkbdAppearArgNew appear_arg{};
f32 volume{};
s32 cursor_position{};
- SwkbdAppearArg appear_arg{};
std::array<char16_t, 0x1FA> input_text{};
bool utf8_mode{};
INSERT_PADDING_BYTES(1);
@@ -264,8 +321,10 @@ struct SwkbdCalcArg {
INSERT_PADDING_WORDS(4);
u8 se_group{};
INSERT_PADDING_BYTES(3);
+ INSERT_PADDING_WORDS(8);
};
-static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size.");
+static_assert(sizeof(SwkbdCalcArgNew) == 0x4E8 - sizeof(SwkbdCalcArgCommon),
+ "SwkbdCalcArgNew has incorrect size.");
struct SwkbdChangedStringArg {
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..14aa6f69e 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/fs/file.h"
@@ -22,7 +21,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_web_browser.h"
#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/ns/pl_u.h"
+#include "core/hle/service/ns/iplatform_service_manager.h"
#include "core/loader/loader.h"
namespace Service::AM::Applets {
@@ -280,7 +279,7 @@ void WebBrowser::Initialize() {
InitializeLobby();
break;
default:
- UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind);
+ ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
break;
}
}
@@ -289,7 +288,7 @@ bool WebBrowser::TransactionComplete() const {
return complete;
}
-ResultCode WebBrowser::GetStatus() const {
+Result WebBrowser::GetStatus() const {
return status;
}
@@ -321,7 +320,7 @@ void WebBrowser::Execute() {
ExecuteLobby();
break;
default:
- UNREACHABLE_MSG("Invalid ShimKind={}", web_arg_header.shim_kind);
+ ASSERT_MSG(false, "Invalid ShimKind={}", web_arg_header.shim_kind);
WebBrowserExit(WebExitReason::EndButtonPressed);
break;
}
@@ -446,6 +445,14 @@ void WebBrowser::ExecuteLogin() {
}
void WebBrowser::ExecuteOffline() {
+ // TODO (Morph): This is a hack for WebSession foreground web applets such as those used by
+ // Super Mario 3D All-Stars.
+ // TODO (Morph): Implement WebSession.
+ if (applet_mode == LibraryAppletMode::AllForegroundInitiallyHidden) {
+ LOG_WARNING(Service_AM, "WebSession is not implemented");
+ return;
+ }
+
const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document));
if (!Common::FS::Exists(main_url)) {
diff --git a/src/core/hle/service/am/applets/applet_web_browser.h b/src/core/hle/service/am/applets/applet_web_browser.h
index b3364ee06..fd727fac8 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.h
+++ b/src/core/hle/service/am/applets/applet_web_browser.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -33,7 +32,7 @@ public:
void Initialize() override;
bool TransactionComplete() const override;
- ResultCode GetStatus() const override;
+ Result GetStatus() const override;
void ExecuteInteractive() override;
void Execute() override;
@@ -67,7 +66,7 @@ private:
const Core::Frontend::WebBrowserApplet& frontend;
bool complete{false};
- ResultCode status{ResultSuccess};
+ Result status{ResultSuccess};
WebAppletVersion web_applet_version{};
WebArgHeader web_arg_header{};
diff --git a/src/core/hle/service/am/applets/applet_web_browser_types.h b/src/core/hle/service/am/applets/applet_web_browser_types.h
index 419c2bf79..c522c5c1a 100644
--- a/src/core/hle/service/am/applets/applet_web_browser_types.h
+++ b/src/core/hle/service/am/applets/applet_web_browser_types.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 134ac1ee2..b5b8e4cad 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
@@ -9,6 +8,7 @@
#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
+#include "core/frontend/applets/mii_edit.h"
#include "core/frontend/applets/profile_select.h"
#include "core/frontend/applets/software_keyboard.h"
#include "core/frontend/applets/web_browser.h"
@@ -19,6 +19,7 @@
#include "core/hle/service/am/applets/applet_controller.h"
#include "core/hle/service/am/applets/applet_error.h"
#include "core/hle/service/am/applets/applet_general_backend.h"
+#include "core/hle/service/am/applets/applet_mii_edit.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/applets/applet_software_keyboard.h"
#include "core/hle/service/am/applets/applet_web_browser.h"
@@ -171,11 +172,12 @@ void Applet::Initialize() {
AppletFrontendSet::AppletFrontendSet() = default;
AppletFrontendSet::AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
+ MiiEdit mii_edit_,
ParentalControlsApplet parental_controls_applet,
PhotoViewer photo_viewer_, ProfileSelect profile_select_,
SoftwareKeyboard software_keyboard_, WebBrowser web_browser_)
: controller{std::move(controller_applet)}, error{std::move(error_applet)},
- parental_controls{std::move(parental_controls_applet)},
+ mii_edit{std::move(mii_edit_)}, parental_controls{std::move(parental_controls_applet)},
photo_viewer{std::move(photo_viewer_)}, profile_select{std::move(profile_select_)},
software_keyboard{std::move(software_keyboard_)}, web_browser{std::move(web_browser_)} {}
@@ -202,6 +204,10 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
frontend.error = std::move(set.error);
}
+ if (set.mii_edit != nullptr) {
+ frontend.mii_edit = std::move(set.mii_edit);
+ }
+
if (set.parental_controls != nullptr) {
frontend.parental_controls = std::move(set.parental_controls);
}
@@ -238,6 +244,10 @@ void AppletManager::SetDefaultAppletsIfMissing() {
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
}
+ if (frontend.mii_edit == nullptr) {
+ frontend.mii_edit = std::make_unique<Core::Frontend::DefaultMiiEditApplet>();
+ }
+
if (frontend.parental_controls == nullptr) {
frontend.parental_controls =
std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
@@ -277,6 +287,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode
return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select);
case AppletId::SoftwareKeyboard:
return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
+ case AppletId::MiiEdit:
+ return std::make_shared<MiiEdit>(system, mode, *frontend.mii_edit);
case AppletId::Web:
case AppletId::Shop:
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..e78a57657 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,7 +9,7 @@
#include "common/swap.h"
#include "core/hle/service/kernel_helpers.h"
-union ResultCode;
+union Result;
namespace Core {
class System;
@@ -20,6 +19,7 @@ namespace Core::Frontend {
class ControllerApplet;
class ECommerceApplet;
class ErrorApplet;
+class MiiEditApplet;
class ParentalControlsApplet;
class PhotoViewerApplet;
class ProfileSelectApplet;
@@ -138,7 +138,7 @@ public:
virtual void Initialize();
virtual bool TransactionComplete() const = 0;
- virtual ResultCode GetStatus() const = 0;
+ virtual Result GetStatus() const = 0;
virtual void ExecuteInteractive() = 0;
virtual void Execute() = 0;
@@ -178,6 +178,7 @@ protected:
struct AppletFrontendSet {
using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
+ using MiiEdit = std::unique_ptr<Core::Frontend::MiiEditApplet>;
using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
@@ -186,9 +187,9 @@ struct AppletFrontendSet {
AppletFrontendSet();
AppletFrontendSet(ControllerApplet controller_applet, ErrorApplet error_applet,
- ParentalControlsApplet parental_controls_applet, PhotoViewer photo_viewer_,
- ProfileSelect profile_select_, SoftwareKeyboard software_keyboard_,
- WebBrowser web_browser_);
+ MiiEdit mii_edit_, ParentalControlsApplet parental_controls_applet,
+ PhotoViewer photo_viewer_, ProfileSelect profile_select_,
+ SoftwareKeyboard software_keyboard_, WebBrowser web_browser_);
~AppletFrontendSet();
AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -199,6 +200,7 @@ struct AppletFrontendSet {
ControllerApplet controller;
ErrorApplet error;
+ MiiEdit mii_edit;
ParentalControlsApplet parental_controls;
PhotoViewer photo_viewer;
ProfileSelect profile_select;
diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp
index 6196773d5..603515284 100644
--- a/src/core/hle/service/am/idle.cpp
+++ b/src/core/hle/service/am/idle.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/idle.h"
diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h
index e290c30b1..15b31f67e 100644
--- a/src/core/hle/service/am/idle.h
+++ b/src/core/hle/service/am/idle.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp
index 6da9b9f58..66824e495 100644
--- a/src/core/hle/service/am/omm.cpp
+++ b/src/core/hle/service/am/omm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/omm.h"
diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h
index 3766150fe..73d0c82d5 100644
--- a/src/core/hle/service/am/omm.h
+++ b/src/core/hle/service/am/omm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp
index 95218d9ee..ec581e32b 100644
--- a/src/core/hle/service/am/spsm.cpp
+++ b/src/core/hle/service/am/spsm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/spsm.h"
diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h
index 04bbf9e68..922f8863e 100644
--- a/src/core/hle/service/am/spsm.h
+++ b/src/core/hle/service/am/spsm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/am/tcap.cpp b/src/core/hle/service/am/tcap.cpp
index 4d0971c03..818420e22 100644
--- a/src/core/hle/service/am/tcap.cpp
+++ b/src/core/hle/service/am/tcap.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/am/tcap.h"
diff --git a/src/core/hle/service/am/tcap.h b/src/core/hle/service/am/tcap.h
index e9578f16e..6b2148c29 100644
--- a/src/core/hle/service/am/tcap.h
+++ b/src/core/hle/service/am/tcap.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 3c83717b5..368ccd52f 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <numeric>
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 4b5f7c5f2..6c1ce601a 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp
index 243ea15b8..8a338d9b1 100644
--- a/src/core/hle/service/apm/apm.cpp
+++ b/src/core/hle/service/apm/apm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/apm/apm.h"
diff --git a/src/core/hle/service/apm/apm.h b/src/core/hle/service/apm/apm.h
index 691fe6c16..0fecc766a 100644
--- a/src/core/hle/service/apm/apm.h
+++ b/src/core/hle/service/apm/apm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp
index 98839fe97..d6de84066 100644
--- a/src/core/hle/service/apm/apm_controller.cpp
+++ b/src/core/hle/service/apm/apm_controller.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
@@ -17,8 +16,8 @@ constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Con
Controller::Controller(Core::Timing::CoreTiming& core_timing_)
: core_timing{core_timing_}, configs{
- {PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
- {PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
+ {PerformanceMode::Normal, DEFAULT_PERFORMANCE_CONFIGURATION},
+ {PerformanceMode::Boost, DEFAULT_PERFORMANCE_CONFIGURATION},
} {}
Controller::~Controller() = default;
@@ -63,13 +62,13 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
PerformanceConfiguration::Config15,
}};
- SetPerformanceConfiguration(PerformanceMode::Docked,
+ SetPerformanceConfiguration(PerformanceMode::Boost,
BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode)));
}
PerformanceMode Controller::GetCurrentPerformanceMode() const {
- return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
- : PerformanceMode::Handheld;
+ return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Boost
+ : PerformanceMode::Normal;
}
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
@@ -81,7 +80,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
}
void Controller::SetClockSpeed(u32 mhz) {
- LOG_INFO(Service_APM, "called, mhz={:08X}", mhz);
+ LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz);
// TODO(DarkLordZach): Actually signal core_timing to change clock speed.
// TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
}
diff --git a/src/core/hle/service/apm/apm_controller.h b/src/core/hle/service/apm/apm_controller.h
index 8d48e0104..3357b7762 100644
--- a/src/core/hle/service/apm/apm_controller.h
+++ b/src/core/hle/service/apm/apm_controller.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -32,15 +31,18 @@ enum class PerformanceConfiguration : u32 {
Config16 = 0x9222000C,
};
+// This is nn::oe::CpuBoostMode
enum class CpuBoostMode : u32 {
- Disabled = 0,
- Full = 1, // CPU + GPU -> Config 13, 14, 15, or 16
- Partial = 2, // GPU Only -> Config 15 or 16
+ Normal = 0, // Boost mode disabled
+ FastLoad = 1, // CPU + GPU -> Config 13, 14, 15, or 16
+ Partial = 2, // GPU Only -> Config 15 or 16
};
-enum class PerformanceMode : u8 {
- Handheld = 0,
- Docked = 1,
+// This is nn::oe::PerformanceMode
+enum class PerformanceMode : s32 {
+ Invalid = -1,
+ Normal = 0,
+ Boost = 1,
};
// Class to manage the state and change of the emulated system performance.
diff --git a/src/core/hle/service/apm/apm_interface.cpp b/src/core/hle/service/apm/apm_interface.cpp
index 6163e3294..041fc16bd 100644
--- a/src/core/hle/service/apm/apm_interface.cpp
+++ b/src/core/hle/service/apm/apm_interface.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/service/apm/apm_interface.h b/src/core/hle/service/apm/apm_interface.h
index 063ad5308..0740fd4ba 100644
--- a/src/core/hle/service/apm/apm_interface.h
+++ b/src/core/hle/service/apm/apm_interface.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp
index 260fd0e0e..4a2ae5f88 100644
--- a/src/core/hle/service/audio/audctl.cpp
+++ b/src/core/hle/service/audio/audctl.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h
index 15f6c77a0..a27ff6cfe 100644
--- a/src/core/hle/service/audio/audctl.h
+++ b/src/core/hle/service/audio/audctl.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp
index 6264e4bda..5541af300 100644
--- a/src/core/hle/service/audio/auddbg.cpp
+++ b/src/core/hle/service/audio/auddbg.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/auddbg.h"
diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h
index d1653eedd..8f26be5dc 100644
--- a/src/core/hle/service/audio/auddbg.h
+++ b/src/core/hle/service/audio/auddbg.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp
index 10acaad19..98f4a6048 100644
--- a/src/core/hle/service/audio/audin_a.cpp
+++ b/src/core/hle/service/audio/audin_a.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/audin_a.h"
diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h
index 15120a4b6..19a927de5 100644
--- a/src/core/hle/service/audio/audin_a.h
+++ b/src/core/hle/service/audio/audin_a.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp
index 34cc659ed..48a9a73a0 100644
--- a/src/core/hle/service/audio/audin_u.cpp
+++ b/src/core/hle/service/audio/audin_u.cpp
@@ -1,69 +1,211 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "audio_core/in/audio_in_system.h"
+#include "audio_core/renderer/audio_device.h"
+#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service::Audio {
+using namespace AudioCore::AudioIn;
-IAudioIn::IAudioIn(Core::System& system_)
- : ServiceFramework{system_, "IAudioIn"}, service_context{system_, "IAudioIn"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, nullptr, "GetAudioInState"},
- {1, &IAudioIn::Start, "Start"},
- {2, nullptr, "Stop"},
- {3, nullptr, "AppendAudioInBuffer"},
- {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
- {5, nullptr, "GetReleasedAudioInBuffer"},
- {6, nullptr, "ContainsAudioInBuffer"},
- {7, nullptr, "AppendUacInBuffer"},
- {8, &IAudioIn::AppendAudioInBufferAuto, "AppendAudioInBufferAuto"},
- {9, nullptr, "GetReleasedAudioInBuffersAuto"},
- {10, nullptr, "AppendUacInBufferAuto"},
- {11, nullptr, "GetAudioInBufferCount"},
- {12, nullptr, "SetDeviceGain"},
- {13, nullptr, "GetDeviceGain"},
- {14, nullptr, "FlushAudioInBuffers"},
- };
- // clang-format on
+class IAudioIn final : public ServiceFramework<IAudioIn> {
+public:
+ explicit IAudioIn(Core::System& system_, Manager& manager, size_t session_id,
+ std::string& device_name, const AudioInParameter& in_params, u32 handle,
+ u64 applet_resource_user_id)
+ : ServiceFramework{system_, "IAudioIn"},
+ service_context{system_, "IAudioIn"}, event{service_context.CreateEvent("AudioInEvent")},
+ impl{std::make_shared<In>(system_, manager, event, session_id)} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IAudioIn::GetAudioInState, "GetAudioInState"},
+ {1, &IAudioIn::Start, "Start"},
+ {2, &IAudioIn::Stop, "Stop"},
+ {3, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBuffer"},
+ {4, &IAudioIn::RegisterBufferEvent, "RegisterBufferEvent"},
+ {5, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffer"},
+ {6, &IAudioIn::ContainsAudioInBuffer, "ContainsAudioInBuffer"},
+ {7, &IAudioIn::AppendAudioInBuffer, "AppendUacInBuffer"},
+ {8, &IAudioIn::AppendAudioInBuffer, "AppendAudioInBufferAuto"},
+ {9, &IAudioIn::GetReleasedAudioInBuffer, "GetReleasedAudioInBuffersAuto"},
+ {10, &IAudioIn::AppendAudioInBuffer, "AppendUacInBufferAuto"},
+ {11, &IAudioIn::GetAudioInBufferCount, "GetAudioInBufferCount"},
+ {12, &IAudioIn::SetDeviceGain, "SetDeviceGain"},
+ {13, &IAudioIn::GetDeviceGain, "GetDeviceGain"},
+ {14, &IAudioIn::FlushAudioInBuffers, "FlushAudioInBuffers"},
+ };
+ // clang-format on
- RegisterHandlers(functions);
+ RegisterHandlers(functions);
- buffer_event = service_context.CreateEvent("IAudioIn:BufferEvent");
-}
+ if (impl->GetSystem()
+ .Initialize(device_name, in_params, handle, applet_resource_user_id)
+ .IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioIn System!");
+ }
+ }
-IAudioIn::~IAudioIn() {
- service_context.CloseEvent(buffer_event);
-}
+ ~IAudioIn() override {
+ impl->Free();
+ service_context.CloseEvent(event);
+ }
-void IAudioIn::Start(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ [[nodiscard]] std::shared_ptr<In> GetImpl() {
+ return impl;
+ }
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
+private:
+ void GetAudioInState(Kernel::HLERequestContext& ctx) {
+ const auto state = static_cast<u32>(impl->GetState());
-void IAudioIn::RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called. State={}", state);
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
-}
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(state);
+ }
-void IAudioIn::AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ void Start(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
-}
+ auto result = impl->StartSystem();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void Stop(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto result = impl->StopSystem();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void AppendAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ u64 tag = rp.PopRaw<u64>();
-AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
+ const auto in_buffer_size{ctx.GetReadBufferSize()};
+ if (in_buffer_size < sizeof(AudioInBuffer)) {
+ LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");
+ }
+
+ const auto& in_buffer = ctx.ReadBuffer();
+ AudioInBuffer buffer{};
+ std::memcpy(&buffer, in_buffer.data(), sizeof(AudioInBuffer));
+
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
+
+ auto result = impl->AppendBuffer(buffer, tag);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto& buffer_event = impl->GetBufferEvent();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(buffer_event);
+ }
+
+ void GetReleasedAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
+ std::vector<u64> released_buffers(write_buffer_size, 0);
+
+ auto count = impl->GetReleasedBuffers(released_buffers);
+
+ [[maybe_unused]] std::string tags{};
+ for (u32 i = 0; i < count; i++) {
+ tags += fmt::format("{:08X}, ", released_buffers[i]);
+ }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
+ tags);
+
+ ctx.WriteBuffer(released_buffers);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(count);
+ }
+
+ void ContainsAudioInBuffer(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const u64 tag{rp.Pop<u64>()};
+ const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
+
+ LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(buffer_queued);
+ }
+
+ void GetAudioInBufferCount(Kernel::HLERequestContext& ctx) {
+ const auto buffer_count = impl->GetBufferCount();
+
+ LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ rb.Push(ResultSuccess);
+ rb.Push(buffer_count);
+ }
+
+ void SetDeviceGain(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto volume{rp.Pop<f32>()};
+ LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
+
+ impl->SetVolume(volume);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetDeviceGain(Kernel::HLERequestContext& ctx) {
+ auto volume{impl->GetVolume()};
+
+ LOG_DEBUG(Service_Audio, "called. Gain {}", volume);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(volume);
+ }
+
+ void FlushAudioInBuffers(Kernel::HLERequestContext& ctx) {
+ bool flushed{impl->FlushAudioInBuffers()};
+
+ LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(flushed);
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* event;
+ std::shared_ptr<AudioCore::AudioIn::In> impl;
+};
+
+AudInU::AudInU(Core::System& system_)
+ : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew},
+ service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
+ system_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudInU::ListAudioIns, "ListAudioIns"},
@@ -81,59 +223,152 @@ AudInU::AudInU(Core::System& system_) : ServiceFramework{system_, "audin:u"} {
AudInU::~AudInU() = default;
void AudInU::ListAudioIns(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
+
LOG_DEBUG(Service_Audio, "called");
- const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioInDeviceName);
- const std::size_t device_count = std::min(count, audio_device_names.size());
- std::vector<AudioInDeviceName> device_names;
- device_names.reserve(device_count);
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
- for (std::size_t i = 0; i < device_count; i++) {
- const auto& device_name = audio_device_names[i];
- auto& entry = device_names.emplace_back();
- device_name.copy(entry.data(), device_name.size());
+ u32 out_count{0};
+ if (write_count > 0) {
+ out_count = impl->GetDeviceNames(device_names, write_count, false);
+ ctx.WriteBuffer(device_names);
}
- ctx.WriteBuffer(device_names);
-
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(device_names.size()));
+ rb.Push(out_count);
}
void AudInU::ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
+
LOG_DEBUG(Service_Audio, "called");
- constexpr u32 device_count = 0;
- // Since we don't actually use any other audio input devices, we return 0 devices. Filtered
- // device listing just omits the default input device
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
+
+ u32 out_count{0};
+ if (write_count > 0) {
+ out_count = impl->GetDeviceNames(device_names, write_count, true);
+ ctx.WriteBuffer(device_names);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(device_count));
+ rb.Push(out_count);
}
-void AudInU::OpenInOutImpl(Kernel::HLERequestContext& ctx) {
- AudInOutParams params{};
- params.channel_count = 2;
- params.sample_format = SampleFormat::PCM16;
- params.sample_rate = 48000;
- params.state = State::Started;
+void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto in_params{rp.PopRaw<AudioInParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
+ const auto device_name_data{ctx.ReadBuffer()};
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
+
+ std::scoped_lock l{impl->mutex};
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
+ }
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+ impl->sessions[new_session_id] = audio_in->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushRaw<AudInOutParams>(params);
- rb.PushIpcInterface<IAudioIn>(system);
-}
-void AudInU::OpenAudioIn(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
- OpenInOutImpl(ctx);
+ std::string out_name{out_system.GetName()};
+ ctx.WriteBuffer(out_name);
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioInParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioIn>(audio_in);
}
void AudInU::OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
- OpenInOutImpl(ctx);
+ IPC::RequestParser rp{ctx};
+ auto protocol_specified{rp.PopRaw<u64>()};
+ auto in_params{rp.PopRaw<AudioInParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
+ const auto device_name_data{ctx.ReadBuffer()};
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
+
+ std::scoped_lock l{impl->mutex};
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio In to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
+ }
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ LOG_DEBUG(Service_Audio, "Opening new AudioIn, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_in = std::make_shared<IAudioIn>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+ impl->sessions[new_session_id] = audio_in->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioInParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
+
+ IPC::ResponseBuilder rb{ctx, 6, 0, 1};
+
+ std::string out_name{out_system.GetName()};
+ if (protocol_specified == 0) {
+ if (out_system.IsUac()) {
+ out_name = "UacIn";
+ } else {
+ out_name = "DeviceIn";
+ }
+ }
+
+ ctx.WriteBuffer(out_name);
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioInParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioIn>(audio_in);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audin_u.h b/src/core/hle/service/audio/audin_u.h
index bf3418613..b45fda78a 100644
--- a/src/core/hle/service/audio/audin_u.h
+++ b/src/core/hle/service/audio/audin_u.h
@@ -1,9 +1,10 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include "audio_core/audio_in_manager.h"
+#include "audio_core/in/audio_in.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -15,22 +16,12 @@ namespace Kernel {
class HLERequestContext;
}
-namespace Service::Audio {
-
-class IAudioIn final : public ServiceFramework<IAudioIn> {
-public:
- explicit IAudioIn(Core::System& system_);
- ~IAudioIn() override;
-
-private:
- void Start(Kernel::HLERequestContext& ctx);
- void RegisterBufferEvent(Kernel::HLERequestContext& ctx);
- void AppendAudioInBufferAuto(Kernel::HLERequestContext& ctx);
-
- KernelHelpers::ServiceContext service_context;
+namespace AudioCore::AudioOut {
+class Manager;
+class In;
+} // namespace AudioCore::AudioOut
- Kernel::KEvent* buffer_event;
-};
+namespace Service::Audio {
class AudInU final : public ServiceFramework<AudInU> {
public:
@@ -38,33 +29,14 @@ public:
~AudInU() override;
private:
- enum class SampleFormat : u32_le {
- PCM16 = 2,
- };
-
- enum class State : u32_le {
- Started = 0,
- Stopped = 1,
- };
-
- struct AudInOutParams {
- u32_le sample_rate{};
- u32_le channel_count{};
- SampleFormat sample_format{};
- State state{};
- };
- static_assert(sizeof(AudInOutParams) == 0x10, "AudInOutParams is an invalid size");
-
- using AudioInDeviceName = std::array<char, 256>;
- static constexpr std::array<std::string_view, 1> audio_device_names{{
- "BuiltInHeadset",
- }};
-
void ListAudioIns(Kernel::HLERequestContext& ctx);
void ListAudioInsAutoFiltered(Kernel::HLERequestContext& ctx);
void OpenInOutImpl(Kernel::HLERequestContext& ctx);
void OpenAudioIn(Kernel::HLERequestContext& ctx);
void OpenAudioInProtocolSpecified(Kernel::HLERequestContext& ctx);
+
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioCore::AudioIn::Manager> impl;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp
index b3f24f9bb..97da71dfa 100644
--- a/src/core/hle/service/audio/audio.cpp
+++ b/src/core/hle/service/audio/audio.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/audctl.h"
#include "core/hle/service/audio/auddbg.h"
diff --git a/src/core/hle/service/audio/audio.h b/src/core/hle/service/audio/audio.h
index b6d13912e..bbb2214e4 100644
--- a/src/core/hle/service/audio/audio.h
+++ b/src/core/hle/service/audio/audio.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp
index 3ee522b50..5ecb99236 100644
--- a/src/core/hle/service/audio/audout_a.cpp
+++ b/src/core/hle/service/audio/audout_a.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/audout_a.h"
diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h
index 2043dfb77..f641cffeb 100644
--- a/src/core/hle/service/audio/audout_a.h
+++ b/src/core/hle/service/audio/audout_a.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index affa7971c..49c092301 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -1,60 +1,47 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <cstring>
#include <vector>
-#include "audio_core/audio_out.h"
-#include "audio_core/codec.h"
+#include "audio_core/out/audio_out_system.h"
+#include "audio_core/renderer/audio_device.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
+#include "common/string_util.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/errors.h"
-#include "core/hle/service/kernel_helpers.h"
#include "core/memory.h"
namespace Service::Audio {
-
-constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}};
-constexpr int DefaultSampleRate{48000};
-
-struct AudoutParams {
- s32_le sample_rate;
- u16_le channel_count;
- INSERT_PADDING_BYTES_NOINIT(2);
-};
-static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size");
-
-enum class AudioState : u32 {
- Started,
- Stopped,
-};
+using namespace AudioCore::AudioOut;
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
- explicit IAudioOut(Core::System& system_, AudoutParams audio_params_,
- AudioCore::AudioOut& audio_core_, std::string&& device_name_,
- std::string&& unique_name)
- : ServiceFramework{system_, "IAudioOut"}, audio_core{audio_core_},
- device_name{std::move(device_name_)}, audio_params{audio_params_},
- main_memory{system.Memory()}, service_context{system_, "IAudioOut"} {
+ explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager,
+ size_t session_id, std::string& device_name,
+ const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id)
+ : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew},
+ service_context{system_, "IAudioOut"}, event{service_context.CreateEvent(
+ "AudioOutEvent")},
+ impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} {
+
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
- {1, &IAudioOut::StartAudioOut, "Start"},
- {2, &IAudioOut::StopAudioOut, "Stop"},
- {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"},
+ {1, &IAudioOut::Start, "Start"},
+ {2, &IAudioOut::Stop, "Stop"},
+ {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"},
{4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
- {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffers"},
+ {5, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffers"},
{6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"},
- {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"},
- {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"},
+ {7, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBufferAuto"},
+ {8, &IAudioOut::GetReleasedAudioOutBuffers, "GetReleasedAudioOutBuffersAuto"},
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
{10, &IAudioOut::GetAudioOutPlayedSampleCount, "GetAudioOutPlayedSampleCount"},
{11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"},
@@ -64,241 +51,262 @@ public:
// clang-format on
RegisterHandlers(functions);
- // This is the event handle used to check if the audio buffer was released
- buffer_event = service_context.CreateEvent("IAudioOutBufferReleased");
-
- stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate,
- audio_params.channel_count, std::move(unique_name), [this] {
- const auto guard = LockService();
- buffer_event->GetWritableEvent().Signal();
- });
+ if (impl->GetSystem()
+ .Initialize(device_name, in_params, handle, applet_resource_user_id)
+ .IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to initialize the AudioOut System!");
+ }
}
~IAudioOut() override {
- service_context.CloseEvent(buffer_event);
+ impl->Free();
+ service_context.CloseEvent(event);
}
-private:
- struct AudioBuffer {
- u64_le next;
- u64_le buffer;
- u64_le buffer_capacity;
- u64_le buffer_size;
- u64_le offset;
- };
- static_assert(sizeof(AudioBuffer) == 0x28, "AudioBuffer is an invalid size");
+ [[nodiscard]] std::shared_ptr<AudioCore::AudioOut::Out> GetImpl() {
+ return impl;
+ }
+private:
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto state = static_cast<u32>(impl->GetState());
+
+ LOG_DEBUG(Service_Audio, "called. State={}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
+ rb.Push(state);
}
- void StartAudioOut(Kernel::HLERequestContext& ctx) {
+ void Start(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- if (stream->IsPlaying()) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_OPERATION_FAILED);
- return;
- }
-
- audio_core.StartStream(stream);
+ auto result = impl->StartSystem();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
- void StopAudioOut(Kernel::HLERequestContext& ctx) {
+ void Stop(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- if (stream->IsPlaying()) {
- audio_core.StopStream(stream);
- }
+ auto result = impl->StopSystem();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
- void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
- }
-
- void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description());
+ void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ u64 tag = rp.PopRaw<u64>();
- const auto& input_buffer{ctx.ReadBuffer()};
- ASSERT_MSG(input_buffer.size() == sizeof(AudioBuffer),
- "AudioBuffer input is an invalid size!");
- AudioBuffer audio_buffer{};
- std::memcpy(&audio_buffer, input_buffer.data(), sizeof(AudioBuffer));
- const u64 tag{rp.Pop<u64>()};
+ const auto in_buffer_size{ctx.GetReadBufferSize()};
+ if (in_buffer_size < sizeof(AudioOutBuffer)) {
+ LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
+ }
- std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16));
- main_memory.ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size);
+ const auto& in_buffer = ctx.ReadBuffer();
+ AudioOutBuffer buffer{};
+ std::memcpy(&buffer, in_buffer.data(), sizeof(AudioOutBuffer));
- if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_BUFFER_COUNT_EXCEEDED);
- return;
- }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} Appending buffer {:08X}", sessionid, tag);
+
+ auto result = impl->AppendBuffer(buffer, tag);
IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ auto& buffer_event = impl->GetBufferEvent();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
+ rb.PushCopyObjects(buffer_event);
}
- void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
+ void GetReleasedAudioOutBuffers(Kernel::HLERequestContext& ctx) {
+ auto write_buffer_size = ctx.GetWriteBufferSize() / sizeof(u64);
+ std::vector<u64> released_buffers(write_buffer_size, 0);
- const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
- const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
+ auto count = impl->GetReleasedBuffers(released_buffers);
- std::vector<u64> tags{released_buffers};
- tags.resize(max_count);
- ctx.WriteBuffer(tags);
+ [[maybe_unused]] std::string tags{};
+ for (u32 i = 0; i < count; i++) {
+ tags += fmt::format("{:08X}, ", released_buffers[i]);
+ }
+ [[maybe_unused]] auto sessionid{impl->GetSystem().GetSessionId()};
+ LOG_TRACE(Service_Audio, "called. Session {} released {} buffers: {}", sessionid, count,
+ tags);
+ ctx.WriteBuffer(released_buffers);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(released_buffers.size()));
+ rb.Push(count);
}
void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
IPC::RequestParser rp{ctx};
+
const u64 tag{rp.Pop<u64>()};
+ const auto buffer_queued{impl->ContainsAudioBuffer(tag)};
+
+ LOG_DEBUG(Service_Audio, "called. Is buffer {:08X} registered? {}", tag, buffer_queued);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->ContainsBuffer(tag));
+ rb.Push(buffer_queued);
}
void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto buffer_count = impl->GetBufferCount();
+
+ LOG_DEBUG(Service_Audio, "called. Buffer count={}", buffer_count);
IPC::ResponseBuilder rb{ctx, 3};
+
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(stream->GetQueueSize()));
+ rb.Push(buffer_count);
}
void GetAudioOutPlayedSampleCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto samples_played = impl->GetPlayedSampleCount();
+
+ LOG_DEBUG(Service_Audio, "called. Played samples={}", samples_played);
IPC::ResponseBuilder rb{ctx, 4};
+
rb.Push(ResultSuccess);
- rb.Push(stream->GetPlayedSampleCount());
+ rb.Push(samples_played);
}
void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ bool flushed{impl->FlushAudioOutBuffers()};
+
+ LOG_DEBUG(Service_Audio, "called. Were any buffers flushed? {}", flushed);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->Flush());
+ rb.Push(flushed);
}
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const float volume = rp.Pop<float>();
- LOG_DEBUG(Service_Audio, "called, volume={}", volume);
+ const auto volume = rp.Pop<f32>();
+
+ LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
- stream->SetVolume(volume);
+ impl->SetVolume(volume);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto volume = impl->GetVolume();
+
+ LOG_DEBUG(Service_Audio, "called. Volume={}", volume);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(stream->GetVolume());
+ rb.Push(volume);
}
- AudioCore::AudioOut& audio_core;
- AudioCore::StreamPtr stream;
- std::string device_name;
-
- [[maybe_unused]] AudoutParams audio_params{};
-
- Core::Memory::Memory& main_memory;
-
KernelHelpers::ServiceContext service_context;
-
- /// This is the event handle used to check if the audio buffer was released
- Kernel::KEvent* buffer_event;
+ Kernel::KEvent* event;
+ std::shared_ptr<AudioCore::AudioOut::Out> impl;
};
-AudOutU::AudOutU(Core::System& system_) : ServiceFramework{system_, "audout:u"} {
+AudOutU::AudOutU(Core::System& system_)
+ : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew},
+ service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(
+ system_)} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"},
- {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"},
- {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"},
- {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"},
+ {0, &AudOutU::ListAudioOuts, "ListAudioOuts"},
+ {1, &AudOutU::OpenAudioOut, "OpenAudioOut"},
+ {2, &AudOutU::ListAudioOuts, "ListAudioOutsAuto"},
+ {3, &AudOutU::OpenAudioOut, "OpenAudioOutAuto"},
};
// clang-format on
RegisterHandlers(functions);
- audio_core = std::make_unique<AudioCore::AudioOut>();
}
AudOutU::~AudOutU() = default;
-void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
+ using namespace AudioCore::AudioRenderer;
- ctx.WriteBuffer(DefaultDevice);
+ std::scoped_lock l{impl->mutex};
+
+ const auto write_count =
+ static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
+ std::vector<AudioDevice::AudioDeviceName> device_names{};
+ if (write_count > 0) {
+ device_names.emplace_back("DeviceOut");
+ LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
+ } else {
+ LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
+ }
+
+ ctx.WriteBuffer(device_names);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(1); // Amount of audio devices
+ rb.Push<u32>(static_cast<u32>(device_names.size()));
}
-void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
+void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ auto in_params{rp.PopRaw<AudioOutParameter>()};
+ auto applet_resource_user_id{rp.PopRaw<u64>()};
const auto device_name_data{ctx.ReadBuffer()};
- std::string device_name;
- if (device_name_data[0] != '\0') {
- device_name.assign(device_name_data.begin(), device_name_data.end());
- } else {
- device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
- }
- ctx.WriteBuffer(device_name);
+ auto device_name = Common::StringFromBuffer(device_name_data);
+ auto handle{ctx.GetCopyHandle(0)};
- IPC::RequestParser rp{ctx};
- auto params{rp.PopRaw<AudoutParams>()};
- if (params.channel_count <= 2) {
- // Mono does not exist for audout
- params.channel_count = 2;
- } else {
- params.channel_count = 6;
+ auto link{impl->LinkToManager()};
+ if (link.IsError()) {
+ LOG_ERROR(Service_Audio, "Failed to link Audio Out to Audio Manager");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(link);
+ return;
}
- if (!params.sample_rate) {
- params.sample_rate = DefaultSampleRate;
+
+ size_t new_session_id{};
+ auto result{impl->AcquireSessionId(new_session_id)};
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
}
- std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
- auto audio_out_interface = std::make_shared<IAudioOut>(
- system, params, *audio_core, std::move(device_name), std::move(unique_name));
+ LOG_DEBUG(Service_Audio, "Opening new AudioOut, sessionid={}, free sessions={}", new_session_id,
+ impl->num_free_sessions);
+
+ auto audio_out = std::make_shared<IAudioOut>(system, *impl, new_session_id, device_name,
+ in_params, handle, applet_resource_user_id);
+
+ impl->sessions[new_session_id] = audio_out->GetImpl();
+ impl->applet_resource_user_ids[new_session_id] = applet_resource_user_id;
+
+ auto& out_system = impl->sessions[new_session_id]->GetSystem();
+ AudioOutParameterInternal out_params{.sample_rate = out_system.GetSampleRate(),
+ .channel_count = out_system.GetChannelCount(),
+ .sample_format =
+ static_cast<u32>(out_system.GetSampleFormat()),
+ .state = static_cast<u32>(out_system.GetState())};
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- rb.Push(ResultSuccess);
- rb.Push<u32>(DefaultSampleRate);
- rb.Push<u32>(params.channel_count);
- rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
- rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
- rb.PushIpcInterface<IAudioOut>(audio_out_interface);
- audio_out_interfaces.push_back(std::move(audio_out_interface));
+ ctx.WriteBuffer(out_system.GetName());
+
+ rb.Push(ResultSuccess);
+ rb.PushRaw<AudioOutParameterInternal>(out_params);
+ rb.PushIpcInterface<IAudioOut>(audio_out);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index f7ae2f2bf..fdc0ee754 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -1,16 +1,13 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <vector>
+#include "audio_core/audio_out_manager.h"
+#include "audio_core/out/audio_out.h"
+#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
-namespace AudioCore {
-class AudioOut;
-}
-
namespace Core {
class System;
}
@@ -19,6 +16,11 @@ namespace Kernel {
class HLERequestContext;
}
+namespace AudioCore::AudioOut {
+class Manager;
+class Out;
+} // namespace AudioCore::AudioOut
+
namespace Service::Audio {
class IAudioOut;
@@ -29,11 +31,11 @@ public:
~AudOutU() override;
private:
- void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
- void OpenAudioOutImpl(Kernel::HLERequestContext& ctx);
+ void ListAudioOuts(Kernel::HLERequestContext& ctx);
+ void OpenAudioOut(Kernel::HLERequestContext& ctx);
- std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
- std::unique_ptr<AudioCore::AudioOut> audio_core;
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioCore::AudioOut::Manager> impl;
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp
index 70fc17ae2..fa82e9ac7 100644
--- a/src/core/hle/service/audio/audrec_a.cpp
+++ b/src/core/hle/service/audio/audrec_a.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/audrec_a.h"
diff --git a/src/core/hle/service/audio/audrec_a.h b/src/core/hle/service/audio/audrec_a.h
index 2cce90b1d..9edf89f6c 100644
--- a/src/core/hle/service/audio/audrec_a.h
+++ b/src/core/hle/service/audio/audrec_a.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/audrec_u.cpp b/src/core/hle/service/audio/audrec_u.cpp
index 74a65ccff..bc55cec17 100644
--- a/src/core/hle/service/audio/audrec_u.cpp
+++ b/src/core/hle/service/audio/audrec_u.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/audrec_u.h"
diff --git a/src/core/hle/service/audio/audrec_u.h b/src/core/hle/service/audio/audrec_u.h
index f79d49e5c..8b4817884 100644
--- a/src/core/hle/service/audio/audrec_u.h
+++ b/src/core/hle/service/audio/audrec_u.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp
index cf8c34a15..e775ac3bf 100644
--- a/src/core/hle/service/audio/audren_a.cpp
+++ b/src/core/hle/service/audio/audren_a.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/audren_a.h"
diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h
index 5d0a626ad..9e08b4245 100644
--- a/src/core/hle/service/audio/audren_a.h
+++ b/src/core/hle/service/audio/audren_a.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index f45e5cecc..6fb07c37d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -1,11 +1,15 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <memory>
-#include "audio_core/audio_renderer.h"
+#include "audio_core/audio_core.h"
+#include "audio_core/common/audio_renderer_parameter.h"
+#include "audio_core/common/feature_support.h"
+#include "audio_core/renderer/audio_device.h"
+#include "audio_core/renderer/audio_renderer.h"
+#include "audio_core/renderer/voice/voice_info.h"
#include "common/alignment.h"
#include "common/bit_util.h"
#include "common/common_funcs.h"
@@ -14,90 +18,127 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/service/audio/audren_u.h"
#include "core/hle/service/audio/errors.h"
+#include "core/memory.h"
+
+using namespace AudioCore::AudioRenderer;
namespace Service::Audio {
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
- explicit IAudioRenderer(Core::System& system_,
- const AudioCommon::AudioRendererParameter& audren_params,
- const std::size_t instance_number)
- : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"} {
+ explicit IAudioRenderer(Core::System& system_, Manager& manager_,
+ AudioCore::AudioRendererParameterInternal& params,
+ Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
+ u32 process_handle, u64 applet_resource_user_id, s32 session_id)
+ : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew},
+ service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent(
+ "IAudioRendererEvent")},
+ manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IAudioRenderer::GetSampleRate, "GetSampleRate"},
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
{3, &IAudioRenderer::GetState, "GetState"},
- {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
+ {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
{5, &IAudioRenderer::Start, "Start"},
{6, &IAudioRenderer::Stop, "Stop"},
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
- {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
- {11, &IAudioRenderer::ExecuteAudioRendererRendering, "ExecuteAudioRendererRendering"},
+ {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
+ {11, nullptr, "ExecuteAudioRendererRendering"},
};
// clang-format on
RegisterHandlers(functions);
- system_event = service_context.CreateEvent("IAudioRenderer:SystemEvent");
- renderer = std::make_unique<AudioCore::AudioRenderer>(
- system.CoreTiming(), system.Memory(), audren_params,
- [this]() {
- const auto guard = LockService();
- system_event->GetWritableEvent().Signal();
- },
- instance_number);
+ impl->Initialize(params, transfer_memory, transfer_memory_size, process_handle,
+ applet_resource_user_id, session_id);
}
~IAudioRenderer() override {
- service_context.CloseEvent(system_event);
+ impl->Finalize();
+ service_context.CloseEvent(rendered_event);
}
private:
void GetSampleRate(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto sample_rate{impl->GetSystem().GetSampleRate()};
+
+ LOG_DEBUG(Service_Audio, "called. Sample rate {}", sample_rate);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetSampleRate());
+ rb.Push(sample_rate);
}
void GetSampleCount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const auto sample_count{impl->GetSystem().GetSampleCount()};
+
+ LOG_DEBUG(Service_Audio, "called. Sample count {}", sample_count);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetSampleCount());
+ rb.Push(sample_count);
}
void GetState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const u32 state{!impl->GetSystem().IsActive()};
+
+ LOG_DEBUG(Service_Audio, "called, state {}", state);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
+ rb.Push(state);
}
void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+ const auto buffer_count{impl->GetSystem().GetMixBufferCount()};
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push<u32>(renderer->GetMixBufferCount());
+ rb.Push(buffer_count);
}
- void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "(STUBBED) called");
+ void RequestUpdate(Kernel::HLERequestContext& ctx) {
+ LOG_TRACE(Service_Audio, "called");
+
+ std::vector<u8> input{ctx.ReadBuffer(0)};
+
+ // These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
+ // checking size 0. Performance size is 0 for most games.
+
+ std::vector<u8> output{};
+ std::vector<u8> performance{};
+ auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};
+ if (is_buffer_b) {
+ const auto buffersB{ctx.BufferDescriptorB()};
+ output.resize(buffersB[0].Size(), 0);
+ performance.resize(buffersB[1].Size(), 0);
+ } else {
+ const auto buffersC{ctx.BufferDescriptorC()};
+ output.resize(buffersC[0].Size(), 0);
+ performance.resize(buffersC[1].Size(), 0);
+ }
- std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0);
- auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
+ auto result = impl->RequestUpdate(input, performance, output);
if (result.IsSuccess()) {
- ctx.WriteBuffer(output_params);
+ if (is_buffer_b) {
+ ctx.WriteBufferB(output.data(), output.size(), 0);
+ ctx.WriteBufferB(performance.data(), performance.size(), 1);
+ } else {
+ ctx.WriteBufferC(output.data(), output.size(), 0);
+ ctx.WriteBufferC(performance.data(), performance.size(), 1);
+ }
+ } else {
+ LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
}
IPC::ResponseBuilder rb{ctx, 2};
@@ -105,38 +146,45 @@ private:
}
void Start(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- const auto result = renderer->Start();
+ impl->Start();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void Stop(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
- const auto result = renderer->Stop();
+ impl->Stop();
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ rb.Push(ResultSuccess);
}
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "called");
+
+ if (impl->GetSystem().GetExecutionMode() == AudioCore::ExecutionMode::Manual) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_NOT_SUPPORTED);
+ return;
+ }
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(system_event->GetReadableEvent());
+ rb.PushCopyObjects(rendered_event->GetReadableEvent());
}
void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::RequestParser rp{ctx};
- rendering_time_limit_percent = rp.Pop<u32>();
- LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
- rendering_time_limit_percent);
+ auto limit = rp.PopRaw<u32>();
- ASSERT(rendering_time_limit_percent <= 100);
+ auto& system_ = impl->GetSystem();
+ system_.SetRenderingTimeLimit(limit);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -145,34 +193,34 @@ private:
void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+ auto& system_ = impl->GetSystem();
+ auto time = system_.GetRenderingTimeLimit();
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(rendering_time_limit_percent);
+ rb.Push(time);
}
void ExecuteAudioRendererRendering(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
-
- // This service command currently only reports an unsupported operation
- // error code, or aborts. Given that, we just always return an error
- // code in this case.
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_SUPPORTED);
}
KernelHelpers::ServiceContext service_context;
-
- Kernel::KEvent* system_event;
- std::unique_ptr<AudioCore::AudioRenderer> renderer;
- u32 rendering_time_limit_percent = 100;
+ Kernel::KEvent* rendered_event;
+ Manager& manager;
+ std::unique_ptr<Renderer> impl;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
+
public:
- explicit IAudioDevice(Core::System& system_, Kernel::KEvent* buffer_event_, u32_le revision_)
- : ServiceFramework{system_, "IAudioDevice"}, buffer_event{buffer_event_}, revision{
- revision_} {
+ explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision,
+ u32 device_num)
+ : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew},
+ service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>(
+ system_, applet_resource_user_id,
+ revision)},
+ event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} {
static const FunctionInfo functions[] = {
{0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
{1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
@@ -186,54 +234,45 @@ public:
{10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
{11, &IAudioDevice::QueryAudioDeviceInputEvent, "QueryAudioDeviceInputEvent"},
{12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
- {13, nullptr, "GetActiveAudioOutputDeviceName"},
- {14, nullptr, "ListAudioOutputDeviceName"},
+ {13, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioOutputDeviceName"},
+ {14, &IAudioDevice::ListAudioOutputDeviceName, "ListAudioOutputDeviceName"},
};
RegisterHandlers(functions);
+
+ event->GetWritableEvent().Signal();
}
-private:
- using AudioDeviceName = std::array<char, 256>;
- static constexpr std::array<std::string_view, 4> audio_device_names{{
- "AudioStereoJackOutput",
- "AudioBuiltInSpeakerOutput",
- "AudioTvOutput",
- "AudioUsbDeviceOutput",
- }};
- enum class DeviceType {
- AHUBHeadphones,
- AHUBSpeakers,
- HDA,
- USBOutput,
- };
+ ~IAudioDevice() override {
+ service_context.CloseEvent(event);
+ }
+private:
void ListAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
+ const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
- const bool usb_output_supported =
- IsFeatureSupported(AudioFeatures::AudioUSBDeviceOutput, revision);
- const std::size_t count = ctx.GetWriteBufferSize() / sizeof(AudioDeviceName);
+ std::vector<AudioDevice::AudioDeviceName> out_names{};
- std::vector<AudioDeviceName> name_buffer;
- name_buffer.reserve(audio_device_names.size());
+ const u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
- for (std::size_t i = 0; i < count && i < audio_device_names.size(); i++) {
- const auto type = static_cast<DeviceType>(i);
-
- if (!usb_output_supported && type == DeviceType::USBOutput) {
- continue;
+ std::string out{};
+ for (u32 i = 0; i < out_count; i++) {
+ std::string a{};
+ u32 j = 0;
+ while (out_names[i].name[j] != '\0') {
+ a += out_names[i].name[j];
+ j++;
}
-
- const auto& device_name = audio_device_names[i];
- auto& entry = name_buffer.emplace_back();
- device_name.copy(entry.data(), device_name.size());
+ out += "\n\t" + a;
}
- ctx.WriteBuffer(name_buffer);
+ LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
IPC::ResponseBuilder rb{ctx, 3};
+
+ ctx.WriteBuffer(out_names);
+
rb.Push(ResultSuccess);
- rb.Push(static_cast<u32>(name_buffer.size()));
+ rb.Push(out_count);
}
void SetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
@@ -243,7 +282,11 @@ private:
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
- LOG_WARNING(Service_Audio, "(STUBBED) called. name={}, volume={}", name, volume);
+ LOG_DEBUG(Service_Audio, "called. name={}, volume={}", name, volume);
+
+ if (name == "AudioTvOutput") {
+ impl->SetDeviceVolumes(volume);
+ }
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -253,53 +296,60 @@ private:
const auto device_name_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(device_name_buffer);
- LOG_WARNING(Service_Audio, "(STUBBED) called. name={}", name);
+ LOG_DEBUG(Service_Audio, "called. Name={}", name);
+
+ f32 volume{1.0f};
+ if (name == "AudioTvOutput") {
+ volume = impl->GetDeviceVolume(name);
+ }
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(1.0f);
+ rb.Push(volume);
}
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ const auto write_size = ctx.GetWriteBufferSize() / sizeof(char);
+ std::string out_name{"AudioTvOutput"};
- // Currently set to always be TV audio output.
- const auto& device_name = audio_device_names[2];
+ LOG_DEBUG(Service_Audio, "(STUBBED) called. Name={}", out_name);
- AudioDeviceName out_device_name{};
- device_name.copy(out_device_name.data(), device_name.size());
+ out_name.resize(write_size);
- ctx.WriteBuffer(out_device_name);
+ ctx.WriteBuffer(out_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
- buffer_event->GetWritableEvent().Signal();
+ event->GetWritableEvent().Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ const auto& sink{system.AudioCore().GetOutputSink()};
+ u32 channel_count{sink.GetDeviceChannels()};
+
+ LOG_DEBUG(Service_Audio, "(STUBBED) called. Channels={}", channel_count);
IPC::ResponseBuilder rb{ctx, 3};
+
rb.Push(ResultSuccess);
- rb.Push<u32>(2);
+ rb.Push<u32>(channel_count);
}
- // Should be similar to QueryAudioDeviceOutputEvent
void QueryAudioDeviceInputEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ LOG_DEBUG(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
@@ -307,402 +357,167 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(buffer_event->GetReadableEvent());
+ rb.PushCopyObjects(event->GetReadableEvent());
}
- Kernel::KEvent* buffer_event;
- u32_le revision = 0;
+ void ListAudioOutputDeviceName(Kernel::HLERequestContext& ctx) {
+ const size_t in_count = ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName);
+
+ std::vector<AudioDevice::AudioDeviceName> out_names{};
+
+ const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
+
+ std::string out{};
+ for (u32 i = 0; i < out_count; i++) {
+ std::string a{};
+ u32 j = 0;
+ while (out_names[i].name[j] != '\0') {
+ a += out_names[i].name[j];
+ j++;
+ }
+ out += "\n\t" + a;
+ }
+
+ LOG_DEBUG(Service_Audio, "called.\nNames={}", out);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+
+ ctx.WriteBuffer(out_names);
+
+ rb.Push(ResultSuccess);
+ rb.Push(out_count);
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ std::unique_ptr<AudioDevice> impl;
+ Kernel::KEvent* event;
};
AudRenU::AudRenU(Core::System& system_)
- : ServiceFramework{system_, "audren:u"}, service_context{system_, "audren:u"} {
+ : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew},
+ service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &AudRenU::OpenAudioRenderer, "OpenAudioRenderer"},
- {1, &AudRenU::GetAudioRendererWorkBufferSize, "GetWorkBufferSize"},
+ {1, &AudRenU::GetWorkBufferSize, "GetWorkBufferSize"},
{2, &AudRenU::GetAudioDeviceService, "GetAudioDeviceService"},
- {3, &AudRenU::OpenAudioRendererForManualExecution, "OpenAudioRendererForManualExecution"},
+ {3, nullptr, "OpenAudioRendererForManualExecution"},
{4, &AudRenU::GetAudioDeviceServiceWithRevisionInfo, "GetAudioDeviceServiceWithRevisionInfo"},
};
// clang-format on
RegisterHandlers(functions);
-
- buffer_event = service_context.CreateEvent("IAudioOutBufferReleasedEvent");
}
-AudRenU::~AudRenU() {
- service_context.CloseEvent(buffer_event);
-}
+AudRenU::~AudRenU() = default;
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- OpenAudioRendererImpl(ctx);
-}
-
-static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
- // +1 represents the final mix.
- return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
- 1;
-}
-
-void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_Audio, "called");
-
- // Several calculations below align the sizes being calculated
- // onto a 64 byte boundary.
- static constexpr u64 buffer_alignment_size = 64;
-
- // Some calculations that calculate portions of the buffer
- // that will contain information, on the other hand, align
- // the result of some of their calcularions on a 16 byte boundary.
- static constexpr u64 info_field_alignment_size = 16;
-
- // Maximum detail entries that may exist at one time for performance
- // frame statistics.
- static constexpr u64 max_perf_detail_entries = 100;
-
- // Size of the data structure representing the bulk of the voice-related state.
- static constexpr u64 voice_state_size_bytes = 0x100;
-
- // Size of the upsampler manager data structure
- constexpr u64 upsampler_manager_size = 0x48;
-
- // Calculates the part of the size that relates to mix buffers.
- const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
- // As of 8.0.0 this is the maximum on voice channels.
- constexpr u64 max_voice_channels = 6;
-
- // The service expects the sample_count member of the parameters to either be
- // a value of 160 or 240, so the maximum sample count is assumed in order
- // to adequately handle all values at runtime.
- constexpr u64 default_max_sample_count = 240;
-
- const u64 total_mix_buffers = params.mix_buffer_count + max_voice_channels;
-
- u64 size = 0;
- size += total_mix_buffers * (sizeof(s32) * params.sample_count);
- size += total_mix_buffers * (sizeof(s32) * default_max_sample_count);
- size += u64{params.submix_count} + params.sink_count;
- size = Common::AlignUp(size, buffer_alignment_size);
- size += Common::AlignUp(params.unknown_30, buffer_alignment_size);
- size += Common::AlignUp(sizeof(s32) * params.mix_buffer_count, buffer_alignment_size);
- return size;
- };
-
- // Calculates the portion of the size related to the mix data (and the sorting thereof).
- const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- // The size of the mixing info data structure.
- constexpr u64 mix_info_size = 0x940;
-
- // Consists of total submixes with the final mix included.
- const u64 total_mix_count = u64{params.submix_count} + 1;
-
- // The total number of effects that may be available to the audio renderer at any time.
- constexpr u64 max_effects = 256;
-
- // Calculates the part of the size related to the audio node state.
- // This will only be used if the audio revision supports the splitter.
- const auto calculate_node_state_size = [](std::size_t num_nodes) {
- // Internally within a nodestate, it appears to use a data structure
- // similar to a std::bitset<64> twice.
- constexpr u64 bit_size = Common::BitSize<u64>();
- constexpr u64 num_bitsets = 2;
-
- // Node state instances have three states internally for performing
- // depth-first searches of nodes. Initialized, Found, and Done Sorting.
- constexpr u64 num_states = 3;
-
- u64 size = 0;
- size += (num_nodes * num_nodes) * sizeof(s32);
- size += num_states * (num_nodes * sizeof(s32));
- size += num_bitsets * (Common::AlignUp(num_nodes, bit_size) / Common::BitSize<u8>());
- return size;
- };
-
- // Calculates the part of the size related to the adjacency (aka edge) matrix.
- const auto calculate_edge_matrix_size = [](std::size_t num_nodes) {
- return (num_nodes * num_nodes) * sizeof(s32);
- };
-
- u64 size = 0;
- size += Common::AlignUp(sizeof(void*) * total_mix_count, info_field_alignment_size);
- size += Common::AlignUp(mix_info_size * total_mix_count, info_field_alignment_size);
- size += Common::AlignUp(sizeof(s32) * max_effects * params.submix_count,
- info_field_alignment_size);
-
- if (IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- size += Common::AlignUp(calculate_node_state_size(total_mix_count) +
- calculate_edge_matrix_size(total_mix_count),
- info_field_alignment_size);
- }
-
- return size;
- };
-
- // Calculates the part of the size related to voice channel info.
- const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 voice_info_size = 0x220;
- constexpr u64 voice_resource_size = 0xD0;
-
- u64 size = 0;
- size += Common::AlignUp(sizeof(void*) * params.voice_count, info_field_alignment_size);
- size += Common::AlignUp(voice_info_size * params.voice_count, info_field_alignment_size);
- size +=
- Common::AlignUp(voice_resource_size * params.voice_count, info_field_alignment_size);
- size +=
- Common::AlignUp(voice_state_size_bytes * params.voice_count, info_field_alignment_size);
- return size;
- };
-
- // Calculates the part of the size related to memory pools.
- const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
- const u64 memory_pool_info_size = 0x20;
- return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to the splitter context.
- const auto calculate_splitter_context_size =
- [](const AudioCommon::AudioRendererParameter& params) -> u64 {
- if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
- return 0;
- }
-
- constexpr u64 splitter_info_size = 0x20;
- constexpr u64 splitter_destination_data_size = 0xE0;
-
- u64 size = 0;
- size += params.num_splitter_send_channels;
- size +=
- Common::AlignUp(splitter_info_size * params.splitter_count, info_field_alignment_size);
- size += Common::AlignUp(splitter_destination_data_size * params.num_splitter_send_channels,
- info_field_alignment_size);
-
- return size;
- };
-
- // Calculates the part of the size related to the upsampler info.
- const auto calculate_upsampler_info_size =
- [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 upsampler_info_size = 0x280;
- // Yes, using the buffer size over info alignment size is intentional here.
- return Common::AlignUp(upsampler_info_size *
- (u64{params.submix_count} + params.sink_count),
- buffer_alignment_size);
- };
-
- // Calculates the part of the size related to effect info.
- const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 effect_info_size = 0x2B0;
- return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to audio sink info.
- const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 sink_info_size = 0x170;
- return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
- };
-
- // Calculates the part of the size related to voice state info.
- const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
- const u64 voice_state_size = 0x100;
- const u64 additional_size = buffer_alignment_size - 1;
- return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
- info_field_alignment_size);
- };
-
- // Calculates the part of the size related to performance statistics.
- const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
- // Extra size value appended to the end of the calculation.
- constexpr u64 appended = 128;
-
- // Whether or not we assume the newer version of performance metrics data structures.
- const bool is_v2 =
- IsFeatureSupported(AudioFeatures::PerformanceMetricsVersion2, params.revision);
-
- // Data structure sizes
- constexpr u64 perf_statistics_size = 0x0C;
- const u64 header_size = is_v2 ? 0x30 : 0x18;
- const u64 entry_size = is_v2 ? 0x18 : 0x10;
- const u64 detail_size = is_v2 ? 0x18 : 0x10;
-
- const u64 entry_count = CalculateNumPerformanceEntries(params);
- const u64 size_per_frame =
- header_size + (entry_size * entry_count) + (detail_size * max_perf_detail_entries);
-
- u64 size = 0;
- size += Common::AlignUp(size_per_frame * params.performance_frame_count + 1,
- buffer_alignment_size);
- size += Common::AlignUp(perf_statistics_size, buffer_alignment_size);
- size += appended;
- return size;
- };
-
- // Calculates the part of the size that relates to the audio command buffer.
- const auto calculate_command_buffer_size =
- [](const AudioCommon::AudioRendererParameter& params) {
- constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
-
- if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
- constexpr u64 command_buffer_size = 0x18000;
-
- return command_buffer_size + alignment;
- }
-
- // When the variadic command buffer is supported, this means
- // the command generator for the audio renderer can issue commands
- // that are (as one would expect), variable in size. So what we need to do
- // is determine the maximum possible size for a few command data structures
- // then multiply them by the amount of present commands indicated by the given
- // respective audio parameters.
-
- constexpr u64 max_biquad_filters = 2;
- constexpr u64 max_mix_buffers = 24;
-
- constexpr u64 biquad_filter_command_size = 0x2C;
-
- constexpr u64 depop_mix_command_size = 0x24;
- constexpr u64 depop_setup_command_size = 0x50;
-
- constexpr u64 effect_command_max_size = 0x540;
-
- constexpr u64 mix_command_size = 0x1C;
- constexpr u64 mix_ramp_command_size = 0x24;
- constexpr u64 mix_ramp_grouped_command_size = 0x13C;
-
- constexpr u64 perf_command_size = 0x28;
-
- constexpr u64 sink_command_size = 0x130;
-
- constexpr u64 submix_command_max_size =
- depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
-
- constexpr u64 volume_command_size = 0x1C;
- constexpr u64 volume_ramp_command_size = 0x20;
-
- constexpr u64 voice_biquad_filter_command_size =
- biquad_filter_command_size * max_biquad_filters;
- constexpr u64 voice_data_command_size = 0x9C;
- const u64 voice_command_max_size =
- (params.splitter_count * depop_setup_command_size) +
- (voice_data_command_size + voice_biquad_filter_command_size +
- volume_ramp_command_size + mix_ramp_grouped_command_size);
+ IPC::RequestParser rp{ctx};
- // Now calculate the individual elements that comprise the size and add them together.
- const u64 effect_commands_size = params.effect_count * effect_command_max_size;
+ AudioCore::AudioRendererParameterInternal params;
+ rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
+ auto transfer_memory_handle = ctx.GetCopyHandle(0);
+ auto process_handle = ctx.GetCopyHandle(1);
+ auto transfer_memory_size = rp.Pop<u64>();
+ auto applet_resource_user_id = rp.Pop<u64>();
- const u64 final_mix_commands_size =
- depop_mix_command_size + volume_command_size * max_mix_buffers;
+ if (impl->GetSessionCount() + 1 > AudioCore::MaxRendererSessions) {
+ LOG_ERROR(Service_Audio, "Too many AudioRenderer sessions open!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
+ return;
+ }
- const u64 perf_commands_size =
- perf_command_size *
- (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
+ const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
+ auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
+ auto transfer_memory{
+ process->GetHandleTable().GetObject<Kernel::KTransferMemory>(transfer_memory_handle)};
- const u64 sink_commands_size = params.sink_count * sink_command_size;
+ const auto session_id{impl->GetSessionId()};
+ if (session_id == -1) {
+ LOG_ERROR(Service_Audio, "Tried to open a session that's already in use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_MAXIMUM_SESSIONS_REACHED);
+ return;
+ }
- const u64 splitter_commands_size =
- params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
+ LOG_DEBUG(Service_Audio, "Opened new AudioRenderer session {} sessions open {}", session_id,
+ impl->GetSessionCount());
- const u64 submix_commands_size = params.submix_count * submix_command_max_size;
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IAudioRenderer>(system, *impl, params, transfer_memory.GetPointerUnsafe(),
+ transfer_memory_size, process_handle,
+ applet_resource_user_id, session_id);
+}
- const u64 voice_commands_size = params.voice_count * voice_command_max_size;
-
- return effect_commands_size + final_mix_commands_size + perf_commands_size +
- sink_commands_size + splitter_commands_size + submix_commands_size +
- voice_commands_size + alignment;
- };
+void AudRenU::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
+ AudioCore::AudioRendererParameterInternal params;
IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
-
- u64 size = 0;
- size += calculate_mix_buffer_sizes(params);
- size += calculate_mix_info_size(params);
- size += calculate_voice_info_size(params);
- size += upsampler_manager_size;
- size += calculate_memory_pools_size(params);
- size += calculate_splitter_context_size(params);
-
- size = Common::AlignUp(size, buffer_alignment_size);
-
- size += calculate_upsampler_info_size(params);
- size += calculate_effect_info_size(params);
- size += calculate_sink_info_size(params);
- size += calculate_voice_state_size(params);
- size += calculate_perf_size(params);
- size += calculate_command_buffer_size(params);
-
- // finally, 4KB page align the size, and we're done.
- size = Common::AlignUp(size, 4096);
+ rp.PopRaw<AudioCore::AudioRendererParameterInternal>(params);
+
+ u64 size{0};
+ auto result = impl->GetWorkBufferSize(params, size);
+
+ std::string output_info{};
+ output_info += fmt::format("\tRevision {}", AudioCore::GetRevisionNum(params.revision));
+ output_info +=
+ fmt::format("\n\tSample Rate {}, Sample Count {}", params.sample_rate, params.sample_count);
+ output_info += fmt::format("\n\tExecution Mode {}, Voice Drop Enabled {}",
+ static_cast<u32>(params.execution_mode), params.voice_drop_enabled);
+ output_info += fmt::format(
+ "\n\tSizes: Effects {:04X}, Mixes {:04X}, Sinks {:04X}, Submixes {:04X}, Splitter Infos "
+ "{:04X}, Splitter Destinations {:04X}, Voices {:04X}, Performance Frames {:04X} External "
+ "Context {:04X}",
+ params.effects, params.mixes, params.sinks, params.sub_mixes, params.splitter_infos,
+ params.splitter_destinations, params.voices, params.perf_frames,
+ params.external_context_size);
+
+ LOG_DEBUG(Service_Audio, "called.\nInput params:\n{}\nOutput params:\n\tWorkbuffer size {:08X}",
+ output_info, size);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
+ rb.Push(result);
rb.Push<u64>(size);
-
- LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", size);
}
void AudRenU::GetAudioDeviceService(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const u64 aruid = rp.Pop<u64>();
- LOG_DEBUG(Service_Audio, "called. aruid={:016X}", aruid);
+ const auto applet_resource_user_id = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_Audio, "called. Applet resource id {}", applet_resource_user_id);
- // Revisionless variant of GetAudioDeviceServiceWithRevisionInfo that
- // always assumes the initial release revision (REV1).
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioDevice>(system, buffer_event, Common::MakeMagic('R', 'E', 'V', '1'));
+ rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id,
+ ::Common::MakeMagic('R', 'E', 'V', '1'), num_audio_devices++);
}
void AudRenU::OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
-
- OpenAudioRendererImpl(ctx);
}
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
struct Parameters {
u32 revision;
- u64 aruid;
+ u64 applet_resource_user_id;
};
IPC::RequestParser rp{ctx};
- const auto [revision, aruid] = rp.PopRaw<Parameters>();
- LOG_DEBUG(Service_Audio, "called. revision={:08X}, aruid={:016X}", revision, aruid);
+ const auto [revision, applet_resource_user_id] = rp.PopRaw<Parameters>();
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioDevice>(system, buffer_event, revision);
-}
+ LOG_DEBUG(Service_Audio, "called. Revision {} Applet resource id {}",
+ AudioCore::GetRevisionNum(revision), applet_resource_user_id);
-void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IAudioRenderer>(system, params, audren_instance_count++);
-}
-
-bool IsFeatureSupported(AudioFeatures feature, u32_le revision) {
- // Byte swap
- const u32_be version_num = revision - Common::MakeMagic('R', 'E', 'V', '0');
-
- switch (feature) {
- case AudioFeatures::AudioUSBDeviceOutput:
- return version_num >= 4U;
- case AudioFeatures::Splitter:
- return version_num >= 2U;
- case AudioFeatures::PerformanceMetricsVersion2:
- case AudioFeatures::VariadicCommandBuffer:
- return version_num >= 5U;
- default:
- return false;
- }
+ rb.PushIpcInterface<IAudioDevice>(system, applet_resource_user_id, revision,
+ num_audio_devices++);
}
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h
index 5922b4b27..4384a9b3c 100644
--- a/src/core/hle/service/audio/audren_u.h
+++ b/src/core/hle/service/audio/audren_u.h
@@ -1,9 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include "audio_core/audio_render_manager.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
@@ -16,6 +16,7 @@ class HLERequestContext;
}
namespace Service::Audio {
+class IAudioRenderer;
class AudRenU final : public ServiceFramework<AudRenU> {
public:
@@ -24,28 +25,14 @@ public:
private:
void OpenAudioRenderer(Kernel::HLERequestContext& ctx);
- void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
+ void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetAudioDeviceService(Kernel::HLERequestContext& ctx);
void OpenAudioRendererForManualExecution(Kernel::HLERequestContext& ctx);
void GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx);
- void OpenAudioRendererImpl(Kernel::HLERequestContext& ctx);
-
KernelHelpers::ServiceContext service_context;
-
- std::size_t audren_instance_count = 0;
- Kernel::KEvent* buffer_event;
+ std::unique_ptr<AudioCore::AudioRenderer::Manager> impl;
+ u32 num_audio_devices{0};
};
-// Describes a particular audio feature that may be supported in a particular revision.
-enum class AudioFeatures : u32 {
- AudioUSBDeviceOutput,
- Splitter,
- PerformanceMetricsVersion2,
- VariadicCommandBuffer,
-};
-
-// Tests if a particular audio feature is supported with a given audio revision.
-bool IsFeatureSupported(AudioFeatures feature, u32_le revision);
-
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/codecctl.cpp b/src/core/hle/service/audio/codecctl.cpp
index 42961d908..81b956d7e 100644
--- a/src/core/hle/service/audio/codecctl.cpp
+++ b/src/core/hle/service/audio/codecctl.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/audio/codecctl.h"
diff --git a/src/core/hle/service/audio/codecctl.h b/src/core/hle/service/audio/codecctl.h
index 58e53259e..34da98212 100644
--- a/src/core/hle/service/audio/codecctl.h
+++ b/src/core/hle/service/audio/codecctl.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h
index 6f8c09bcf..d706978cb 100644
--- a/src/core/hle/service/audio/errors.h
+++ b/src/core/hle/service/audio/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,8 +7,17 @@
namespace Service::Audio {
-constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
-constexpr ResultCode ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
-constexpr ResultCode ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
+constexpr Result ERR_INVALID_DEVICE_NAME{ErrorModule::Audio, 1};
+constexpr Result ERR_OPERATION_FAILED{ErrorModule::Audio, 2};
+constexpr Result ERR_INVALID_SAMPLE_RATE{ErrorModule::Audio, 3};
+constexpr Result ERR_INSUFFICIENT_BUFFER_SIZE{ErrorModule::Audio, 4};
+constexpr Result ERR_MAXIMUM_SESSIONS_REACHED{ErrorModule::Audio, 5};
+constexpr Result ERR_BUFFER_COUNT_EXCEEDED{ErrorModule::Audio, 8};
+constexpr Result ERR_INVALID_CHANNEL_COUNT{ErrorModule::Audio, 10};
+constexpr Result ERR_INVALID_UPDATE_DATA{ErrorModule::Audio, 41};
+constexpr Result ERR_POOL_MAPPING_FAILED{ErrorModule::Audio, 42};
+constexpr Result ERR_NOT_SUPPORTED{ErrorModule::Audio, 513};
+constexpr Result ERR_INVALID_PROCESS_HANDLE{ErrorModule::Audio, 1536};
+constexpr Result ERR_INVALID_REVISION{ErrorModule::Audio, 1537};
} // namespace Service::Audio
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 981b6c996..8bafc3a98 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <cstring>
@@ -256,6 +255,32 @@ void HwOpus::GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx) {
GetWorkBufferSize(ctx);
}
+void HwOpus::GetWorkBufferSizeForMultiStreamEx(Kernel::HLERequestContext& ctx) {
+ OpusMultiStreamParametersEx param;
+ std::memcpy(&param, ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
+
+ const auto sample_rate = param.sample_rate;
+ const auto channel_count = param.channel_count;
+ const auto number_streams = param.number_streams;
+ const auto number_stereo_streams = param.number_stereo_streams;
+
+ LOG_DEBUG(
+ Audio,
+ "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}",
+ sample_rate, channel_count, number_streams, number_stereo_streams);
+
+ ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
+ sample_rate == 12000 || sample_rate == 8000,
+ "Invalid sample rate");
+
+ const u32 worker_buffer_sz =
+ static_cast<u32>(opus_multistream_decoder_get_size(number_streams, number_stereo_streams));
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(worker_buffer_sz);
+}
+
void HwOpus::OpenHardwareOpusDecoder(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto sample_rate = rp.Pop<u32>();
@@ -299,7 +324,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) {
const auto sample_rate = rp.Pop<u32>();
const auto channel_count = rp.Pop<u32>();
- LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
+ LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
@@ -336,7 +361,7 @@ HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} {
{4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"},
{5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"},
{6, nullptr, "OpenHardwareOpusDecoderForMultiStreamEx"},
- {7, nullptr, "GetWorkBufferSizeForMultiStreamEx"},
+ {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h
index b74824ff3..e6092e290 100644
--- a/src/core/hle/service/audio/hwopus.h
+++ b/src/core/hle/service/audio/hwopus.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -12,6 +11,16 @@ class System;
namespace Service::Audio {
+struct OpusMultiStreamParametersEx {
+ u32 sample_rate;
+ u32 channel_count;
+ u32 number_streams;
+ u32 number_stereo_streams;
+ u32 use_large_frame_size;
+ u32 padding;
+ std::array<u32, 64> channel_mappings;
+};
+
class HwOpus final : public ServiceFramework<HwOpus> {
public:
explicit HwOpus(Core::System& system_);
@@ -22,6 +31,7 @@ private:
void OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx);
void GetWorkBufferSize(Kernel::HLERequestContext& ctx);
void GetWorkBufferSizeEx(Kernel::HLERequestContext& ctx);
+ void GetWorkBufferSizeForMultiStreamEx(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Audio
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index ee49edbb9..cd0b405ff 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/hex_util.h"
#include "common/logging/log.h"
@@ -75,7 +74,7 @@ void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
SignalUpdate();
}
-void ProgressServiceBackend::FinishDownload(ResultCode result) {
+void ProgressServiceBackend::FinishDownload(Result result) {
impl.total_downloaded_bytes = impl.total_bytes;
impl.status = DeliveryCacheProgressImpl::Status::Done;
impl.result = result;
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
index 63833c927..205ed0702 100644
--- a/src/core/hle/service/bcat/backend/backend.h
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -50,7 +49,7 @@ struct DeliveryCacheProgressImpl {
};
Status status;
- ResultCode result = ResultSuccess;
+ Result result = ResultSuccess;
DirectoryName current_directory;
FileName current_file;
s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
@@ -91,7 +90,7 @@ public:
void CommitDirectory(std::string_view dir_name);
// Notifies the application that the operation completed with result code result.
- void FinishDownload(ResultCode result);
+ void FinishDownload(Result result);
private:
explicit ProgressServiceBackend(Core::System& system, std::string_view event_name);
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index 5a95707de..d0ac17324 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/bcat/bcat.h"
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h
index 1eba477da..db9d3c8c5 100644
--- a/src/core/hle/service/bcat/bcat.h
+++ b/src/core/hle/service/bcat/bcat.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/bcat/bcat_module.cpp b/src/core/hle/service/bcat/bcat_module.cpp
index 500e7e52d..bc08ac487 100644
--- a/src/core/hle/service/bcat/bcat_module.cpp
+++ b/src/core/hle/service/bcat/bcat_module.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cctype>
#include <mbedtls/md5.h>
@@ -19,15 +18,15 @@
namespace Service::BCAT {
-constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
-constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
-constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
-constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
+constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
+constexpr Result ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
+constexpr Result ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
+constexpr Result ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
// and if any of them have a non-zero result it just forwards that result. This is the FS error code
// for permission denied, which is the closest approximation of this scenario.
-constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
+constexpr Result ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
using BCATDigest = std::array<u8, 0x10>;
@@ -141,8 +140,8 @@ public:
{20401, nullptr, "UnregisterSystemApplicationDeliveryTask"},
{20410, nullptr, "SetSystemApplicationDeliveryTaskTimer"},
{30100, &IBcatService::SetPassphrase, "SetPassphrase"},
- {30101, nullptr, "Unknown"},
- {30102, nullptr, "Unknown2"},
+ {30101, nullptr, "Unknown30101"},
+ {30102, nullptr, "Unknown30102"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
diff --git a/src/core/hle/service/bcat/bcat_module.h b/src/core/hle/service/bcat/bcat_module.h
index 738731c06..b2fcf9bfb 100644
--- a/src/core/hle/service/bcat/bcat_module.h
+++ b/src/core/hle/service/bcat/bcat_module.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp
index 78e01d8d8..466163538 100644
--- a/src/core/hle/service/bpc/bpc.cpp
+++ b/src/core/hle/service/bpc/bpc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/bpc/bpc.h b/src/core/hle/service/bpc/bpc.h
index 6ec25aa9b..8adc2f962 100644
--- a/src/core/hle/service/bpc/bpc.h
+++ b/src/core/hle/service/bpc/bpc.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 0787f43f4..ec7e5320c 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
@@ -182,6 +181,11 @@ public:
{147, nullptr, "RegisterAudioControlNotification"},
{148, nullptr, "SendAudioControlPassthroughCommand"},
{149, nullptr, "SendAudioControlSetAbsoluteVolumeCommand"},
+ {150, nullptr, "AcquireAudioSinkVolumeLocallyChangedEvent"},
+ {151, nullptr, "AcquireAudioSinkVolumeUpdateRequestCompletedEvent"},
+ {152, nullptr, "GetAudioSinkVolume"},
+ {153, nullptr, "RequestUpdateAudioSinkVolume"},
+ {154, nullptr, "IsAudioSinkVolumeSupported"},
{256, nullptr, "IsManufacturingMode"},
{257, nullptr, "EmulateBluetoothCrash"},
{258, nullptr, "GetBleChannelMap"},
diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h
index 191410dbc..9cbe2926f 100644
--- a/src/core/hle/service/btdrv/btdrv.h
+++ b/src/core/hle/service/btdrv/btdrv.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index cc268d877..eebf85e03 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
@@ -215,8 +214,12 @@ public:
{76, nullptr, "Unknown76"},
{100, nullptr, "Unknown100"},
{101, nullptr, "Unknown101"},
- {110, nullptr, "Unknown102"},
- {111, nullptr, "Unknown103"},
+ {110, nullptr, "Unknown110"},
+ {111, nullptr, "Unknown111"},
+ {112, nullptr, "Unknown112"},
+ {113, nullptr, "Unknown113"},
+ {114, nullptr, "Unknown114"},
+ {115, nullptr, "Unknown115"},
};
// clang-format on
diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h
index c6b878043..9dcda1848 100644
--- a/src/core/hle/service/btm/btm.h
+++ b/src/core/hle/service/btm/btm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp
index 5b7fe8e9b..13940a8c9 100644
--- a/src/core/hle/service/caps/caps.cpp
+++ b/src/core/hle/service/caps/caps.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/caps/caps.h"
#include "core/hle/service/caps/caps_a.h"
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h
index 7254055e6..3e89c82cb 100644
--- a/src/core/hle/service/caps/caps.h
+++ b/src/core/hle/service/caps/caps.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp
index 6220e9f77..44267b284 100644
--- a/src/core/hle/service/caps/caps_a.cpp
+++ b/src/core/hle/service/caps/caps_a.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/caps/caps_a.h"
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h
index 389cc6dbe..319c173d8 100644
--- a/src/core/hle/service/caps/caps_a.h
+++ b/src/core/hle/service/caps/caps_a.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp
index a9ad5c8b1..725a2e3a7 100644
--- a/src/core/hle/service/caps/caps_c.cpp
+++ b/src/core/hle/service/caps/caps_c.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h
index c6d1dfdce..983a4212d 100644
--- a/src/core/hle/service/caps/caps_c.h
+++ b/src/core/hle/service/caps/caps_c.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp
index d91e18e80..395b13da7 100644
--- a/src/core/hle/service/caps/caps_sc.cpp
+++ b/src/core/hle/service/caps/caps_sc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/caps/caps_sc.h"
diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h
index e79a33ee5..e5600f6d7 100644
--- a/src/core/hle/service/caps/caps_sc.h
+++ b/src/core/hle/service/caps/caps_sc.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp
index 33a976ddf..62b9edd41 100644
--- a/src/core/hle/service/caps/caps_ss.cpp
+++ b/src/core/hle/service/caps/caps_ss.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/caps/caps_ss.h"
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h
index 1816f7885..718ade485 100644
--- a/src/core/hle/service/caps/caps_ss.h
+++ b/src/core/hle/service/caps/caps_ss.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp
index 45b705950..fcb496756 100644
--- a/src/core/hle/service/caps/caps_su.cpp
+++ b/src/core/hle/service/caps/caps_su.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h
index b366fdb13..c9a1d507b 100644
--- a/src/core/hle/service/caps/caps_su.h
+++ b/src/core/hle/service/caps/caps_su.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp
index 8f8ee2bb4..5fbba8673 100644
--- a/src/core/hle/service/caps/caps_u.cpp
+++ b/src/core/hle/service/caps/caps_u.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h
index e7e0d8775..c3d4b9cea 100644
--- a/src/core/hle/service/caps/caps_u.h
+++ b/src/core/hle/service/caps/caps_u.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp
index c767926a4..923c0022a 100644
--- a/src/core/hle/service/erpt/erpt.cpp
+++ b/src/core/hle/service/erpt/erpt.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/erpt/erpt.h b/src/core/hle/service/erpt/erpt.h
index 8cd5c081f..507d626ec 100644
--- a/src/core/hle/service/erpt/erpt.h
+++ b/src/core/hle/service/erpt/erpt.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index f6184acc9..ff9b0427c 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/crypto/key_manager.h"
#include "core/hle/ipc_helpers.h"
@@ -9,8 +8,8 @@
namespace Service::ES {
-constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2};
-constexpr ResultCode ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3};
+constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2};
+constexpr Result ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3};
class ETicket final : public ServiceFramework<ETicket> {
public:
diff --git a/src/core/hle/service/es/es.h b/src/core/hle/service/es/es.h
index 2a7b27d12..530563550 100644
--- a/src/core/hle/service/es/es.h
+++ b/src/core/hle/service/es/es.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/eupld/eupld.cpp b/src/core/hle/service/eupld/eupld.cpp
index 2d650b1b7..d1553ace0 100644
--- a/src/core/hle/service/eupld/eupld.cpp
+++ b/src/core/hle/service/eupld/eupld.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/eupld/eupld.h b/src/core/hle/service/eupld/eupld.h
index 539993a9d..5de8219be 100644
--- a/src/core/hle/service/eupld/eupld.h
+++ b/src/core/hle/service/eupld/eupld.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index f84506af0..27675615b 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <cstring>
@@ -63,8 +62,7 @@ enum class FatalType : u32 {
ErrorScreen = 2,
};
-static void GenerateErrorReport(Core::System& system, ResultCode error_code,
- const FatalInfo& info) {
+static void GenerateErrorReport(Core::System& system, Result error_code, const FatalInfo& info) {
const auto title_id = system.GetCurrentProcessProgramID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
@@ -107,7 +105,7 @@ static void GenerateErrorReport(Core::System& system, ResultCode error_code,
info.backtrace_size, info.ArchAsString(), info.unk10);
}
-static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type,
+static void ThrowFatalError(Core::System& system, Result error_code, FatalType fatal_type,
const FatalInfo& info) {
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", fatal_type,
error_code.raw);
@@ -130,7 +128,7 @@ static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalTy
void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp{ctx};
- const auto error_code = rp.Pop<ResultCode>();
+ const auto error_code = rp.Pop<Result>();
ThrowFatalError(system, error_code, FatalType::ErrorScreen, {});
IPC::ResponseBuilder rb{ctx, 2};
@@ -140,7 +138,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
- const auto error_code = rp.Pop<ResultCode>();
+ const auto error_code = rp.Pop<Result>();
const auto fatal_type = rp.PopEnum<FatalType>();
ThrowFatalError(system, error_code, fatal_type,
@@ -152,7 +150,7 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
LOG_ERROR(Service_Fatal, "called");
IPC::RequestParser rp(ctx);
- const auto error_code = rp.Pop<ResultCode>();
+ const auto error_code = rp.Pop<Result>();
const auto fatal_type = rp.PopEnum<FatalType>();
const auto fatal_info = ctx.ReadBuffer();
FatalInfo info{};
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h
index 2095bf89f..a7a310f7b 100644
--- a/src/core/hle/service/fatal/fatal.h
+++ b/src/core/hle/service/fatal/fatal.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp
index 8672b85dc..4a81bb5e2 100644
--- a/src/core/hle/service/fatal/fatal_p.cpp
+++ b/src/core/hle/service/fatal/fatal_p.cpp
@@ -1,13 +1,18 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/fatal/fatal_p.h"
namespace Service::Fatal {
Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_)
- : Interface(std::move(module_), system_, "fatal:p") {}
+ : Interface(std::move(module_), system_, "fatal:p") {
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetFatalEvent"},
+ {10, nullptr, "GetFatalContext"},
+ };
+ RegisterHandlers(functions);
+}
Fatal_P::~Fatal_P() = default;
diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h
index ffa5b7b98..f74336835 100644
--- a/src/core/hle/service/fatal/fatal_p.h
+++ b/src/core/hle/service/fatal/fatal_p.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp
index 82993938a..3739711fc 100644
--- a/src/core/hle/service/fatal/fatal_u.cpp
+++ b/src/core/hle/service/fatal/fatal_u.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/fatal/fatal_u.h"
diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h
index 0b58c9112..65fbe2696 100644
--- a/src/core/hle/service/fatal/fatal_u.h
+++ b/src/core/hle/service/fatal/fatal_u.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp
index d7a638f96..7e9fb0385 100644
--- a/src/core/hle/service/fgm/fgm.cpp
+++ b/src/core/hle/service/fgm/fgm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/fgm/fgm.h b/src/core/hle/service/fgm/fgm.h
index 75978f2ed..077e48812 100644
--- a/src/core/hle/service/fgm/fgm.h
+++ b/src/core/hle/service/fgm/fgm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 3703ca4c6..11c604a0f 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
@@ -50,7 +49,7 @@ std::string VfsDirectoryServiceWrapper::GetName() const {
return backing->GetName();
}
-ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
+Result VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (dir == nullptr) {
@@ -74,7 +73,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
+Result VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
if (path.empty()) {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
@@ -93,7 +92,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
+Result VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
// NOTE: This is inaccurate behavior. CreateDirectory is not recursive.
@@ -117,7 +116,7 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_)
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
+Result VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) {
@@ -127,7 +126,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_)
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
+Result VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) {
@@ -137,7 +136,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
+Result VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
const std::string sanitized_path(Common::FS::SanitizePath(path));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path));
@@ -149,8 +148,8 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
- const std::string& dest_path_) const {
+Result VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
+ const std::string& dest_path_) const {
std::string src_path(Common::FS::SanitizePath(src_path_));
std::string dest_path(Common::FS::SanitizePath(dest_path_));
auto src = backing->GetFileRelative(src_path);
@@ -174,7 +173,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found.");
ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(),
- "Could not write all of the bytes but everything else has succeded.");
+ "Could not write all of the bytes but everything else has succeeded.");
if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) {
// TODO(DarkLordZach): Find a better error code for this
@@ -184,8 +183,8 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
return ResultSuccess;
}
-ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
- const std::string& dest_path_) const {
+Result VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_,
+ const std::string& dest_path_) const {
std::string src_path(Common::FS::SanitizePath(src_path_));
std::string dest_path(Common::FS::SanitizePath(dest_path_));
auto src = GetDirectoryRelativeWrapped(backing, src_path);
@@ -274,28 +273,27 @@ FileSystemController::FileSystemController(Core::System& system_) : system{syste
FileSystemController::~FileSystemController() = default;
-ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
+Result FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
romfs_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered RomFS");
return ResultSuccess;
}
-ResultCode FileSystemController::RegisterSaveData(
- std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
+Result FileSystemController::RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
save_data_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered save data");
return ResultSuccess;
}
-ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
+Result FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
sdmc_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered SDMC");
return ResultSuccess;
}
-ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
+Result FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
bis_factory = std::move(factory);
LOG_DEBUG(Service_FS, "Registered BIS");
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index b155e0811..5b27de9fa 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -59,10 +58,10 @@ public:
explicit FileSystemController(Core::System& system_);
~FileSystemController();
- ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
- ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
- ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
- ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
+ Result RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
+ Result RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
+ Result RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
+ Result RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
void SetPackedUpdate(FileSys::VirtualFile update_raw);
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const;
@@ -142,7 +141,7 @@ private:
void InstallInterfaces(Core::System& system);
-// A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
+// A class that wraps a VfsDirectory with methods that return ResultVal and Result instead of
// pointers and booleans. This makes using a VfsDirectory with switch services much easier and
// avoids repetitive code.
class VfsDirectoryServiceWrapper {
@@ -161,35 +160,35 @@ public:
* @param size The size of the new file, filled with zeroes
* @return Result of the operation
*/
- ResultCode CreateFile(const std::string& path, u64 size) const;
+ Result CreateFile(const std::string& path, u64 size) const;
/**
* Delete a file specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
- ResultCode DeleteFile(const std::string& path) const;
+ Result DeleteFile(const std::string& path) const;
/**
* Create a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
- ResultCode CreateDirectory(const std::string& path) const;
+ Result CreateDirectory(const std::string& path) const;
/**
* Delete a directory specified by its path
* @param path Path relative to the archive
* @return Result of the operation
*/
- ResultCode DeleteDirectory(const std::string& path) const;
+ Result DeleteDirectory(const std::string& path) const;
/**
* Delete a directory specified by its path and anything under it
* @param path Path relative to the archive
* @return Result of the operation
*/
- ResultCode DeleteDirectoryRecursively(const std::string& path) const;
+ Result DeleteDirectoryRecursively(const std::string& path) const;
/**
* Cleans the specified directory. This is similar to DeleteDirectoryRecursively,
@@ -201,7 +200,7 @@ public:
*
* @return Result of the operation.
*/
- ResultCode CleanDirectoryRecursively(const std::string& path) const;
+ Result CleanDirectoryRecursively(const std::string& path) const;
/**
* Rename a File specified by its path
@@ -209,7 +208,7 @@ public:
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
- ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const;
+ Result RenameFile(const std::string& src_path, const std::string& dest_path) const;
/**
* Rename a Directory specified by its path
@@ -217,7 +216,7 @@ public:
* @param dest_path Destination path relative to the archive
* @return Result of the operation
*/
- ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
+ Result RenameDirectory(const std::string& src_path, const std::string& dest_path) const;
/**
* Open a file specified by its path, using the specified mode
diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp
index f112ae9d0..1e3366e71 100644
--- a/src/core/hle/service/filesystem/fsp_ldr.cpp
+++ b/src/core/hle/service/filesystem/fsp_ldr.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/filesystem/fsp_ldr.h"
diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp_ldr.h
index d6432a0e1..358739a87 100644
--- a/src/core/hle/service/filesystem/fsp_ldr.h
+++ b/src/core/hle/service/filesystem/fsp_ldr.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp
index 9b7f7d861..4ffc31977 100644
--- a/src/core/hle/service/filesystem/fsp_pr.cpp
+++ b/src/core/hle/service/filesystem/fsp_pr.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/filesystem/fsp_pr.h"
diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp_pr.h
index 9e622518c..bd4e0a730 100644
--- a/src/core/hle/service/filesystem/fsp_pr.h
+++ b/src/core/hle/service/filesystem/fsp_pr.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index b087e7bba..e23eae36a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <cstring>
@@ -58,7 +57,8 @@ enum class FileSystemType : u8 {
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(Core::System& system_, FileSys::VirtualFile backend_)
- : ServiceFramework{system_, "IStorage"}, backend(std::move(backend_)) {
+ : ServiceFramework{system_, "IStorage", ServiceThreadType::CreateNew},
+ backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IStorage::Read, "Read"},
{1, nullptr, "Write"},
@@ -116,7 +116,8 @@ private:
class IFile final : public ServiceFramework<IFile> {
public:
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_)
- : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
+ : ServiceFramework{system_, "IFile", ServiceThreadType::CreateNew},
+ backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"},
{1, &IFile::Write, "Write"},
@@ -245,14 +246,16 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
- entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize());
+ entries.emplace_back(new_entry->GetName(), type,
+ type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize());
}
}
class IDirectory final : public ServiceFramework<IDirectory> {
public:
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_)
- : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
+ : ServiceFramework{system_, "IDirectory", ServiceThreadType::CreateNew},
+ backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
@@ -308,8 +311,8 @@ private:
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
- : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
- size_)} {
+ : ServiceFramework{system_, "IFileSystem", ServiceThreadType::CreateNew},
+ backend{std::move(backend_)}, size{std::move(size_)} {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
{1, &IFileSystem::DeleteFile, "DeleteFile"},
@@ -897,7 +900,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
case FileSys::SaveDataSpaceId::TemporaryStorage:
case FileSys::SaveDataSpaceId::ProperSystem:
case FileSys::SaveDataSpaceId::SafeMode:
- UNREACHABLE();
+ ASSERT(false);
}
auto filesystem = std::make_shared<IFileSystem>(system, std::move(dir.Unwrap()),
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 556708284..36f552e05 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h
index b3996e275..ff525d865 100644
--- a/src/core/hle/service/friend/errors.h
+++ b/src/core/hle/service/friend/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,5 +7,5 @@
namespace Service::Friend {
-constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15};
+constexpr Result ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15};
}
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 79cd3acbb..e0db787fc 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <queue>
#include "common/logging/log.h"
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h
index 8be3321db..444da8b35 100644
--- a/src/core/hle/service/friend/friend.h
+++ b/src/core/hle/service/friend/friend.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/friend/friend_interface.cpp b/src/core/hle/service/friend/friend_interface.cpp
index 9b18b2a32..c8b98b1f0 100644
--- a/src/core/hle/service/friend/friend_interface.cpp
+++ b/src/core/hle/service/friend/friend_interface.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/friend/friend_interface.h"
diff --git a/src/core/hle/service/friend/friend_interface.h b/src/core/hle/service/friend/friend_interface.h
index 43d914b32..3a2184c34 100644
--- a/src/core/hle/service/friend/friend_interface.h
+++ b/src/core/hle/service/friend/friend_interface.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
index 2feead2aa..49b6d45fe 100644
--- a/src/core/hle/service/glue/arp.cpp
+++ b/src/core/hle/service/glue/arp.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
@@ -154,7 +153,7 @@ class IRegistrar final : public ServiceFramework<IRegistrar> {
friend class ARP_W;
public:
- using IssuerFn = std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)>;
+ using IssuerFn = std::function<Result(u64, ApplicationLaunchProperty, std::vector<u8>)>;
explicit IRegistrar(Core::System& system_, IssuerFn&& issuer)
: ServiceFramework{system_, "IRegistrar"}, issue_process_id{std::move(issuer)} {
diff --git a/src/core/hle/service/glue/arp.h b/src/core/hle/service/glue/arp.h
index 0df3c5e1f..06c992e88 100644
--- a/src/core/hle/service/glue/arp.h
+++ b/src/core/hle/service/glue/arp.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/glue/bgtc.cpp b/src/core/hle/service/glue/bgtc.cpp
index 564c3b750..3248091c3 100644
--- a/src/core/hle/service/glue/bgtc.cpp
+++ b/src/core/hle/service/glue/bgtc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
diff --git a/src/core/hle/service/glue/bgtc.h b/src/core/hle/service/glue/bgtc.h
index 4c0142fd5..d6e2baec1 100644
--- a/src/core/hle/service/glue/bgtc.h
+++ b/src/core/hle/service/glue/bgtc.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/glue/ectx.cpp b/src/core/hle/service/glue/ectx.cpp
index 249c6f003..1bd9314ae 100644
--- a/src/core/hle/service/glue/ectx.cpp
+++ b/src/core/hle/service/glue/ectx.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/glue/ectx.h"
diff --git a/src/core/hle/service/glue/ectx.h b/src/core/hle/service/glue/ectx.h
index b275e808a..a608de053 100644
--- a/src/core/hle/service/glue/ectx.h
+++ b/src/core/hle/service/glue/ectx.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h
index f6647f724..d4ce7f44e 100644
--- a/src/core/hle/service/glue/errors.h
+++ b/src/core/hle/service/glue/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,9 +7,9 @@
namespace Service::Glue {
-constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 30};
-constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31};
-constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 42};
-constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 102};
+constexpr Result ERR_INVALID_RESOURCE{ErrorModule::ARP, 30};
+constexpr Result ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 31};
+constexpr Result ERR_INVALID_ACCESS{ErrorModule::ARP, 42};
+constexpr Result ERR_NOT_REGISTERED{ErrorModule::ARP, 102};
} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index b24d469cf..717f2562b 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include "core/core.h"
diff --git a/src/core/hle/service/glue/glue.h b/src/core/hle/service/glue/glue.h
index 112cd238b..ae7c6d235 100644
--- a/src/core/hle/service/glue/glue.h
+++ b/src/core/hle/service/glue/glue.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/glue/glue_manager.cpp b/src/core/hle/service/glue/glue_manager.cpp
index 48e133b48..8a654cdca 100644
--- a/src/core/hle/service/glue/glue_manager.cpp
+++ b/src/core/hle/service/glue/glue_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/glue/errors.h"
#include "core/hle/service/glue/glue_manager.h"
@@ -42,8 +41,8 @@ ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const {
return iter->second.control;
}
-ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
- std::vector<u8> control) {
+Result ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
+ std::vector<u8> control) {
if (title_id == 0) {
return ERR_INVALID_PROCESS_ID;
}
@@ -57,7 +56,7 @@ ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
return ResultSuccess;
}
-ResultCode ARPManager::Unregister(u64 title_id) {
+Result ARPManager::Unregister(u64 title_id) {
if (title_id == 0) {
return ERR_INVALID_PROCESS_ID;
}
diff --git a/src/core/hle/service/glue/glue_manager.h b/src/core/hle/service/glue/glue_manager.h
index 4bc5297c6..cd0b092ac 100644
--- a/src/core/hle/service/glue/glue_manager.h
+++ b/src/core/hle/service/glue/glue_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -43,12 +42,12 @@ public:
// Adds a new entry to the internal database with the provided parameters, returning
// ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister
// step, and ERR_INVALID_PROCESS_ID if the title ID is 0.
- ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);
+ Result Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);
// Removes the registration for the provided title ID from the database, returning
// ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the
// title ID is 0.
- ResultCode Unregister(u64 title_id);
+ Result Unregister(u64 title_id);
// Removes all entries from the database, always succeeds. Should only be used when resetting
// system state.
diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp
index c559ec9df..3ace2dabd 100644
--- a/src/core/hle/service/glue/notif.cpp
+++ b/src/core/hle/service/glue/notif.cpp
@@ -1,7 +1,11 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <algorithm>
+#include <cstring>
+
+#include "common/assert.h"
+#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/glue/notif.h"
@@ -10,11 +14,11 @@ namespace Service::Glue {
NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
// clang-format off
static const FunctionInfo functions[] = {
- {500, nullptr, "RegisterAlarmSetting"},
- {510, nullptr, "UpdateAlarmSetting"},
+ {500, &NOTIF_A::RegisterAlarmSetting, "RegisterAlarmSetting"},
+ {510, &NOTIF_A::UpdateAlarmSetting, "UpdateAlarmSetting"},
{520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
- {530, nullptr, "LoadApplicationParameter"},
- {540, nullptr, "DeleteAlarmSetting"},
+ {530, &NOTIF_A::LoadApplicationParameter, "LoadApplicationParameter"},
+ {540, &NOTIF_A::DeleteAlarmSetting, "DeleteAlarmSetting"},
{1000, &NOTIF_A::Initialize, "Initialize"},
};
// clang-format on
@@ -24,21 +28,132 @@ NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
NOTIF_A::~NOTIF_A() = default;
+void NOTIF_A::RegisterAlarmSetting(Kernel::HLERequestContext& ctx) {
+ const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0);
+ const auto application_parameter_size = ctx.GetReadBufferSize(1);
+
+ ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting),
+ "alarm_setting_buffer_size is not 0x40 bytes");
+ ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter),
+ "application_parameter_size is bigger than 0x400 bytes");
+
+ AlarmSetting new_alarm{};
+ memcpy(&new_alarm, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting));
+
+ // TODO: Count alarms per game id
+ if (alarms.size() >= max_alarms) {
+ LOG_ERROR(Service_NOTIF, "Alarm limit reached");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ new_alarm.alarm_setting_id = last_alarm_setting_id++;
+ alarms.push_back(new_alarm);
+
+ // TODO: Save application parameter data
+
+ LOG_WARNING(Service_NOTIF,
+ "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}",
+ application_parameter_size, new_alarm.alarm_setting_id, new_alarm.kind,
+ new_alarm.muted);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ rb.Push(new_alarm.alarm_setting_id);
+}
+
+void NOTIF_A::UpdateAlarmSetting(Kernel::HLERequestContext& ctx) {
+ const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0);
+ const auto application_parameter_size = ctx.GetReadBufferSize(1);
+
+ ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting),
+ "alarm_setting_buffer_size is not 0x40 bytes");
+ ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter),
+ "application_parameter_size is bigger than 0x400 bytes");
+
+ AlarmSetting alarm_setting{};
+ memcpy(&alarm_setting, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting));
+
+ const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id);
+ if (alarm_it != alarms.end()) {
+ LOG_DEBUG(Service_NOTIF, "Alarm updated");
+ *alarm_it = alarm_setting;
+ // TODO: Save application parameter data
+ }
+
+ LOG_WARNING(Service_NOTIF,
+ "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}",
+ application_parameter_size, alarm_setting.alarm_setting_id, alarm_setting.kind,
+ alarm_setting.muted);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
- // Returns an array of AlarmSetting
- constexpr s32 alarm_count = 0;
+ LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size());
- LOG_WARNING(Service_NOTIF, "(STUBBED) called");
+ // TODO: Only return alarms of this game id
+ ctx.WriteBuffer(alarms);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(alarm_count);
+ rb.Push(static_cast<u32>(alarms.size()));
+}
+
+void NOTIF_A::LoadApplicationParameter(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto alarm_setting_id{rp.Pop<AlarmSettingId>()};
+
+ const auto alarm_it = GetAlarmFromId(alarm_setting_id);
+ if (alarm_it == alarms.end()) {
+ LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ // TODO: Read application parameter related to this setting id
+ ApplicationParameter application_parameter{};
+
+ LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id);
+
+ ctx.WriteBuffer(application_parameter);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ rb.Push(static_cast<u32>(application_parameter.size()));
+}
+
+void NOTIF_A::DeleteAlarmSetting(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto alarm_setting_id{rp.Pop<AlarmSettingId>()};
+
+ std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) {
+ return alarm.alarm_setting_id == alarm_setting_id;
+ });
+
+ LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
}
void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) {
+ // TODO: Load previous alarms from config
+
LOG_WARNING(Service_NOTIF, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
+std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId(
+ AlarmSettingId alarm_setting_id) {
+ return std::find_if(alarms.begin(), alarms.end(),
+ [alarm_setting_id](const AlarmSetting& alarm) {
+ return alarm.alarm_setting_id == alarm_setting_id;
+ });
+}
+
} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h
index 6ecf2015c..4467e1f35 100644
--- a/src/core/hle/service/glue/notif.h
+++ b/src/core/hle/service/glue/notif.h
@@ -1,9 +1,12 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <array>
+#include <vector>
+
+#include "common/uuid.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -18,8 +21,52 @@ public:
~NOTIF_A() override;
private:
+ static constexpr std::size_t max_alarms = 8;
+
+ // This is nn::notification::AlarmSettingId
+ using AlarmSettingId = u16;
+ static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size");
+
+ using ApplicationParameter = std::array<u8, 0x400>;
+ static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size");
+
+ struct DailyAlarmSetting {
+ s8 hour;
+ s8 minute;
+ };
+ static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size");
+
+ struct WeeklyScheduleAlarmSetting {
+ INSERT_PADDING_BYTES(0xA);
+ std::array<DailyAlarmSetting, 0x7> day_of_week;
+ };
+ static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18,
+ "WeeklyScheduleAlarmSetting is an invalid size");
+
+ // This is nn::notification::AlarmSetting
+ struct AlarmSetting {
+ AlarmSettingId alarm_setting_id;
+ u8 kind;
+ u8 muted;
+ INSERT_PADDING_BYTES(0x4);
+ Common::UUID account_id;
+ u64 application_id;
+ INSERT_PADDING_BYTES(0x8);
+ WeeklyScheduleAlarmSetting schedule;
+ };
+ static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size");
+
+ void RegisterAlarmSetting(Kernel::HLERequestContext& ctx);
+ void UpdateAlarmSetting(Kernel::HLERequestContext& ctx);
void ListAlarmSettings(Kernel::HLERequestContext& ctx);
+ void LoadApplicationParameter(Kernel::HLERequestContext& ctx);
+ void DeleteAlarmSetting(Kernel::HLERequestContext& ctx);
void Initialize(Kernel::HLERequestContext& ctx);
+
+ std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id);
+
+ std::vector<AlarmSetting> alarms{};
+ AlarmSettingId last_alarm_setting_id{};
};
} // namespace Service::Glue
diff --git a/src/core/hle/service/grc/grc.cpp b/src/core/hle/service/grc/grc.cpp
index f918bdf03..4b684f6d0 100644
--- a/src/core/hle/service/grc/grc.cpp
+++ b/src/core/hle/service/grc/grc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/grc/grc.h b/src/core/hle/service/grc/grc.h
index 9069fe756..f8c2f8dab 100644
--- a/src/core/hle/service/grc/grc.h
+++ b/src/core/hle/service/grc/grc.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.cpp b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
index a727b3582..bb3cba910 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.cpp
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.cpp
@@ -1,8 +1,6 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
-#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hid/emulated_console.h"
#include "core/hid/hid_core.h"
@@ -11,9 +9,14 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C200;
-Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_)
+Controller_ConsoleSixAxis::Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_,
+ u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
console = hid_core.GetEmulatedConsole();
+ static_assert(SHARED_MEMORY_OFFSET + sizeof(ConsoleSharedMemory) < shared_memory_size,
+ "ConsoleSharedMemory is bigger than the shared memory");
+ shared_memory = std::construct_at(
+ reinterpret_cast<ConsoleSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
}
Controller_ConsoleSixAxis::~Controller_ConsoleSixAxis() = default;
@@ -22,8 +25,7 @@ void Controller_ConsoleSixAxis::OnInit() {}
void Controller_ConsoleSixAxis::OnRelease() {}
-void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {
+void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated() || !is_transfer_memory_set) {
seven_sixaxis_lifo.buffer_count = 0;
seven_sixaxis_lifo.buffer_tail = 0;
@@ -50,13 +52,11 @@ void Controller_ConsoleSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_ti
-motion_status.quaternion.xyz.z,
};
- console_six_axis.sampling_number++;
- console_six_axis.is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
- console_six_axis.verticalization_error = motion_status.verticalization_error;
- console_six_axis.gyro_bias = motion_status.gyro_bias;
+ shared_memory->sampling_number++;
+ shared_memory->is_seven_six_axis_sensor_at_rest = motion_status.is_at_rest;
+ shared_memory->verticalization_error = motion_status.verticalization_error;
+ shared_memory->gyro_bias = motion_status.gyro_bias;
- // Update console six axis shared memory
- std::memcpy(data + SHARED_MEMORY_OFFSET, &console_six_axis, sizeof(console_six_axis));
// Update seven six axis transfer memory
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
std::memcpy(transfer_memory, &seven_sixaxis_lifo, sizeof(seven_sixaxis_lifo));
diff --git a/src/core/hle/service/hid/controllers/console_sixaxis.h b/src/core/hle/service/hid/controllers/console_sixaxis.h
index 26d153f0c..2fd11538f 100644
--- a/src/core/hle/service/hid/controllers/console_sixaxis.h
+++ b/src/core/hle/service/hid/controllers/console_sixaxis.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,7 +7,6 @@
#include "common/common_types.h"
#include "common/quaternion.h"
-#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
@@ -19,7 +17,7 @@ class EmulatedConsole;
namespace Service::HID {
class Controller_ConsoleSixAxis final : public ControllerBase {
public:
- explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_);
+ explicit Controller_ConsoleSixAxis(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_ConsoleSixAxis() override;
// Called when the controller is initialized
@@ -29,7 +27,7 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// Called on InitializeSevenSixAxisSensor
void SetTransferMemoryPointer(u8* t_mem);
@@ -63,12 +61,13 @@ private:
Lifo<SevenSixAxisState, 0x21> seven_sixaxis_lifo{};
static_assert(sizeof(seven_sixaxis_lifo) == 0xA70, "SevenSixAxisState is an invalid size");
- Core::HID::EmulatedConsole* console;
+ SevenSixAxisState next_seven_sixaxis_state{};
u8* transfer_memory = nullptr;
+ ConsoleSharedMemory* shared_memory = nullptr;
+ Core::HID::EmulatedConsole* console = nullptr;
+
bool is_transfer_memory_set = false;
u64 last_saved_timestamp{};
u64 last_global_timestamp{};
- ConsoleSharedMemory console_six_axis{};
- SevenSixAxisState next_seven_sixaxis_state{};
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 788ae9ae7..c58d67d7d 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/hid/controllers/controller_base.h"
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 7450eb20a..d6f7a5073 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -1,11 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
-#include "common/swap.h"
namespace Core::Timing {
class CoreTiming;
@@ -28,12 +26,10 @@ public:
virtual void OnRelease() = 0;
// When the controller is requesting an update for the shared memory
- virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) = 0;
+ virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0;
// When the controller is requesting a motion update for the shared memory
- virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {}
+ virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}
void ActivateController();
@@ -42,6 +38,7 @@ public:
bool IsControllerActivated() const;
static const std::size_t hid_entry_count = 17;
+ static const std::size_t shared_memory_size = 0x40000;
protected:
bool is_activated{false};
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 6a6fb9cab..8ec9f4a95 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
@@ -14,8 +13,12 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x00000;
-Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_)
+Controller_DebugPad::Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
+ static_assert(SHARED_MEMORY_OFFSET + sizeof(DebugPadSharedMemory) < shared_memory_size,
+ "DebugPadSharedMemory is bigger than the shared memory");
+ shared_memory = std::construct_at(
+ reinterpret_cast<DebugPadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
}
@@ -25,16 +28,14 @@ void Controller_DebugPad::OnInit() {}
void Controller_DebugPad::OnRelease() {}
-void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {
+void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
- debug_pad_lifo.buffer_count = 0;
- debug_pad_lifo.buffer_tail = 0;
- std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
+ shared_memory->debug_pad_lifo.buffer_count = 0;
+ shared_memory->debug_pad_lifo.buffer_tail = 0;
return;
}
- const auto& last_entry = debug_pad_lifo.ReadCurrentEntry().state;
+ const auto& last_entry = shared_memory->debug_pad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.debug_pad_enabled) {
@@ -48,8 +49,7 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing,
next_state.r_stick = stick_state.right;
}
- debug_pad_lifo.WriteNextEntry(next_state);
- std::memcpy(data + SHARED_MEMORY_OFFSET, &debug_pad_lifo, sizeof(debug_pad_lifo));
+ shared_memory->debug_pad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index afe374fc2..68ff0ea79 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -1,14 +1,10 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <array>
#include "common/bit_field.h"
-#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
@@ -21,7 +17,7 @@ struct AnalogStickState;
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
public:
- explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_);
+ explicit Controller_DebugPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_DebugPad() override;
// Called when the controller is initialized
@@ -31,7 +27,7 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::DebugPadAttribute
@@ -45,19 +41,24 @@ private:
// This is nn::hid::DebugPadState
struct DebugPadState {
- s64 sampling_number;
- DebugPadAttribute attribute;
- Core::HID::DebugPadButton pad_state;
- Core::HID::AnalogStickState r_stick;
- Core::HID::AnalogStickState l_stick;
+ s64 sampling_number{};
+ DebugPadAttribute attribute{};
+ Core::HID::DebugPadButton pad_state{};
+ Core::HID::AnalogStickState r_stick{};
+ Core::HID::AnalogStickState l_stick{};
};
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");
- // This is nn::hid::detail::DebugPadLifo
- Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
- static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
- DebugPadState next_state{};
+ struct DebugPadSharedMemory {
+ // This is nn::hid::detail::DebugPadLifo
+ Lifo<DebugPadState, hid_entry_count> debug_pad_lifo{};
+ static_assert(sizeof(debug_pad_lifo) == 0x2C8, "debug_pad_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0x4E);
+ };
+ static_assert(sizeof(DebugPadSharedMemory) == 0x400, "DebugPadSharedMemory is an invalid size");
- Core::HID::EmulatedController* controller;
+ DebugPadState next_state{};
+ DebugPadSharedMemory* shared_memory = nullptr;
+ Core::HID::EmulatedController* controller = nullptr;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index fe895c4f6..32e0708ba 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "common/math_util.h"
@@ -24,25 +23,28 @@ constexpr f32 Square(s32 num) {
return static_cast<f32>(num * num);
}
-Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) {
+Controller_Gesture::Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
+ : ControllerBase(hid_core_) {
+ static_assert(SHARED_MEMORY_OFFSET + sizeof(GestureSharedMemory) < shared_memory_size,
+ "GestureSharedMemory is bigger than the shared memory");
+ shared_memory = std::construct_at(
+ reinterpret_cast<GestureSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {
- gesture_lifo.buffer_count = 0;
- gesture_lifo.buffer_tail = 0;
+ shared_memory->gesture_lifo.buffer_count = 0;
+ shared_memory->gesture_lifo.buffer_tail = 0;
force_update = true;
}
void Controller_Gesture::OnRelease() {}
-void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {
+void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
- gesture_lifo.buffer_count = 0;
- gesture_lifo.buffer_tail = 0;
- std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
+ shared_memory->gesture_lifo.buffer_count = 0;
+ shared_memory->gesture_lifo.buffer_tail = 0;
return;
}
@@ -50,15 +52,16 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
GestureProperties gesture = GetGestureProperties();
f32 time_difference =
- static_cast<f32>(gesture_lifo.timestamp - last_update_timestamp) / (1000 * 1000 * 1000);
+ static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) /
+ (1000 * 1000 * 1000);
// Only update if necesary
if (!ShouldUpdateGesture(gesture, time_difference)) {
return;
}
- last_update_timestamp = gesture_lifo.timestamp;
- UpdateGestureSharedMemory(data, size, gesture, time_difference);
+ last_update_timestamp = shared_memory->gesture_lifo.timestamp;
+ UpdateGestureSharedMemory(gesture, time_difference);
}
void Controller_Gesture::ReadTouchInput() {
@@ -92,13 +95,12 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture,
return false;
}
-void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
- GestureProperties& gesture,
+void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture,
f32 time_difference) {
GestureType type = GestureType::Idle;
GestureAttribute attributes{};
- const auto& last_entry = gesture_lifo.ReadCurrentEntry().state;
+ const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state;
// Reset next state to default
next_state.sampling_number = last_entry.sampling_number + 1;
@@ -128,8 +130,7 @@ void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size,
next_state.points = gesture.points;
last_gesture = gesture;
- gesture_lifo.WriteNextEntry(next_state);
- std::memcpy(data + SHARED_MEMORY_OFFSET, &gesture_lifo, sizeof(gesture_lifo));
+ shared_memory->gesture_lifo.WriteNextEntry(next_state);
}
void Controller_Gesture::NewGesture(GestureProperties& gesture, GestureType& type,
@@ -306,7 +307,7 @@ void Controller_Gesture::SetSwipeEvent(GestureProperties& gesture,
}
const Controller_Gesture::GestureState& Controller_Gesture::GetLastGestureEntry() const {
- return gesture_lifo.ReadCurrentEntry().state;
+ return shared_memory->gesture_lifo.ReadCurrentEntry().state;
}
Controller_Gesture::GestureProperties Controller_Gesture::GetGestureProperties() {
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index 0936a3fa3..0d6099ea0 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -15,7 +14,7 @@
namespace Service::HID {
class Controller_Gesture final : public ControllerBase {
public:
- explicit Controller_Gesture(Core::HID::HIDCore& hid_core_);
+ explicit Controller_Gesture(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Gesture() override;
// Called when the controller is initialized
@@ -25,7 +24,7 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr size_t MAX_FINGERS = 16;
@@ -67,19 +66,19 @@ private:
// This is nn::hid::GestureState
struct GestureState {
- s64 sampling_number;
- s64 detection_count;
- GestureType type;
- GestureDirection direction;
- Common::Point<s32> pos;
- Common::Point<s32> delta;
- f32 vel_x;
- f32 vel_y;
- GestureAttribute attributes;
- f32 scale;
- f32 rotation_angle;
- s32 point_count;
- std::array<Common::Point<s32>, 4> points;
+ s64 sampling_number{};
+ s64 detection_count{};
+ GestureType type{GestureType::Idle};
+ GestureDirection direction{GestureDirection::None};
+ Common::Point<s32> pos{};
+ Common::Point<s32> delta{};
+ f32 vel_x{};
+ f32 vel_y{};
+ GestureAttribute attributes{};
+ f32 scale{};
+ f32 rotation_angle{};
+ s32 point_count{};
+ std::array<Common::Point<s32>, 4> points{};
};
static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size");
@@ -93,6 +92,14 @@ private:
f32 angle{};
};
+ struct GestureSharedMemory {
+ // This is nn::hid::detail::GestureLifo
+ Lifo<GestureState, hid_entry_count> gesture_lifo{};
+ static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0x3E);
+ };
+ static_assert(sizeof(GestureSharedMemory) == 0x800, "GestureSharedMemory is an invalid size");
+
// Reads input from all available input engines
void ReadTouchInput();
@@ -100,8 +107,7 @@ private:
bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference);
// Updates the shared memory to the next state
- void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture,
- f32 time_difference);
+ void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference);
// Initializes new gesture
void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes);
@@ -135,12 +141,9 @@ private:
// Returns the average distance, angle and middle point of the active fingers
GestureProperties GetGestureProperties();
- // This is nn::hid::detail::GestureLifo
- Lifo<GestureState, hid_entry_count> gesture_lifo{};
- static_assert(sizeof(gesture_lifo) == 0x708, "gesture_lifo is an invalid size");
GestureState next_state{};
-
- Core::HID::EmulatedConsole* console;
+ GestureSharedMemory* shared_memory = nullptr;
+ Core::HID::EmulatedConsole* console = nullptr;
std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{};
GestureProperties last_gesture{};
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 9588a6910..117d87433 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
@@ -13,8 +12,12 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
-Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_)
+Controller_Keyboard::Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
+ static_assert(SHARED_MEMORY_OFFSET + sizeof(KeyboardSharedMemory) < shared_memory_size,
+ "KeyboardSharedMemory is bigger than the shared memory");
+ shared_memory = std::construct_at(
+ reinterpret_cast<KeyboardSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
emulated_devices = hid_core.GetEmulatedDevices();
}
@@ -24,16 +27,14 @@ void Controller_Keyboard::OnInit() {}
void Controller_Keyboard::OnRelease() {}
-void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {
+void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
- keyboard_lifo.buffer_count = 0;
- keyboard_lifo.buffer_tail = 0;
- std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
+ shared_memory->keyboard_lifo.buffer_count = 0;
+ shared_memory->keyboard_lifo.buffer_tail = 0;
return;
}
- const auto& last_entry = keyboard_lifo.ReadCurrentEntry().state;
+ const auto& last_entry = shared_memory->keyboard_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
if (Settings::values.keyboard_enabled) {
@@ -45,8 +46,7 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing,
next_state.attribute.is_connected.Assign(1);
}
- keyboard_lifo.WriteNextEntry(next_state);
- std::memcpy(data + SHARED_MEMORY_OFFSET, &keyboard_lifo, sizeof(keyboard_lifo));
+ shared_memory->keyboard_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index cf62d3896..7532f53c6 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -1,14 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <array>
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
@@ -21,7 +16,7 @@ struct KeyboardKey;
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
public:
- explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_);
+ explicit Controller_Keyboard(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Keyboard() override;
// Called when the controller is initialized
@@ -31,23 +26,28 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::detail::KeyboardState
struct KeyboardState {
- s64 sampling_number;
- Core::HID::KeyboardModifier modifier;
- Core::HID::KeyboardAttribute attribute;
- Core::HID::KeyboardKey key;
+ s64 sampling_number{};
+ Core::HID::KeyboardModifier modifier{};
+ Core::HID::KeyboardAttribute attribute{};
+ Core::HID::KeyboardKey key{};
};
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");
- // This is nn::hid::detail::KeyboardLifo
- Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
- static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
- KeyboardState next_state{};
+ struct KeyboardSharedMemory {
+ // This is nn::hid::detail::KeyboardLifo
+ Lifo<KeyboardState, hid_entry_count> keyboard_lifo{};
+ static_assert(sizeof(keyboard_lifo) == 0x3D8, "keyboard_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0xA);
+ };
+ static_assert(sizeof(KeyboardSharedMemory) == 0x400, "KeyboardSharedMemory is an invalid size");
- Core::HID::EmulatedDevices* emulated_devices;
+ KeyboardState next_state{};
+ KeyboardSharedMemory* shared_memory = nullptr;
+ Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index ba79888ae..b11cb438d 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
@@ -13,7 +12,12 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
-Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
+Controller_Mouse::Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
+ : ControllerBase{hid_core_} {
+ static_assert(SHARED_MEMORY_OFFSET + sizeof(MouseSharedMemory) < shared_memory_size,
+ "MouseSharedMemory is bigger than the shared memory");
+ shared_memory = std::construct_at(
+ reinterpret_cast<MouseSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
emulated_devices = hid_core.GetEmulatedDevices();
}
@@ -22,16 +26,14 @@ Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
void Controller_Mouse::OnRelease() {}
-void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {
+void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
- mouse_lifo.buffer_count = 0;
- mouse_lifo.buffer_tail = 0;
- std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
+ shared_memory->mouse_lifo.buffer_count = 0;
+ shared_memory->mouse_lifo.buffer_tail = 0;
return;
}
- const auto& last_entry = mouse_lifo.ReadCurrentEntry().state;
+ const auto& last_entry = shared_memory->mouse_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.attribute.raw = 0;
@@ -51,8 +53,7 @@ void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
next_state.button = mouse_button_state;
}
- mouse_lifo.WriteNextEntry(next_state);
- std::memcpy(data + SHARED_MEMORY_OFFSET, &mouse_lifo, sizeof(mouse_lifo));
+ shared_memory->mouse_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 7559fc78d..733d00577 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -1,13 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <array>
-#include "common/bit_field.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
@@ -20,7 +16,7 @@ struct AnalogStickState;
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
public:
- explicit Controller_Mouse(Core::HID::HIDCore& hid_core_);
+ explicit Controller_Mouse(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Mouse() override;
// Called when the controller is initialized
@@ -30,15 +26,20 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
- // This is nn::hid::detail::MouseLifo
- Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
- static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
- Core::HID::MouseState next_state{};
+ struct MouseSharedMemory {
+ // This is nn::hid::detail::MouseLifo
+ Lifo<Core::HID::MouseState, hid_entry_count> mouse_lifo{};
+ static_assert(sizeof(mouse_lifo) == 0x350, "mouse_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0x2C);
+ };
+ static_assert(sizeof(MouseSharedMemory) == 0x400, "MouseSharedMemory is an invalid size");
- Core::HID::AnalogStickState last_mouse_wheel_state;
- Core::HID::EmulatedDevices* emulated_devices;
+ Core::HID::MouseState next_state{};
+ Core::HID::AnalogStickState last_mouse_wheel_state{};
+ MouseSharedMemory* shared_memory = nullptr;
+ Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e5c951e06..f8972ec7a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -1,10 +1,11 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
+#include <chrono>
#include <cstring>
+
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
@@ -17,6 +18,7 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/errors.h"
#include "core/hle/service/kernel_helpers.h"
namespace Service::HID {
@@ -47,23 +49,52 @@ bool Controller_NPad::IsNpadIdValid(Core::HID::NpadIdType npad_id) {
}
}
-bool Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
- return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
- device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
- device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+Result Controller_NPad::IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle) {
+ const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
+ const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
+ const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+
+ if (!npad_type) {
+ return VibrationInvalidStyleIndex;
+ }
+ if (!npad_id) {
+ return VibrationInvalidNpadId;
+ }
+ if (!device_index) {
+ return VibrationDeviceIndexOutOfRange;
+ }
+
+ return ResultSuccess;
}
-bool Controller_NPad::IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle) {
- return IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id)) &&
- device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType &&
- device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+Result Controller_NPad::VerifyValidSixAxisSensorHandle(
+ const Core::HID::SixAxisSensorHandle& device_handle) {
+ const auto npad_id = IsNpadIdValid(static_cast<Core::HID::NpadIdType>(device_handle.npad_id));
+ const bool device_index = device_handle.device_index < Core::HID::DeviceIndex::MaxDeviceIndex;
+ const bool npad_type = device_handle.npad_type < Core::HID::NpadStyleIndex::MaxNpadType;
+
+ if (!npad_id) {
+ return InvalidNpadId;
+ }
+ if (!device_index) {
+ return NpadDeviceIndexOutOfRange;
+ }
+ // This doesn't get validated on nnsdk
+ if (!npad_type) {
+ return NpadInvalidHandle;
+ }
+
+ return ResultSuccess;
}
-Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_,
+Controller_NPad::Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_)
: ControllerBase{hid_core_}, service_context{service_context_} {
+ static_assert(NPAD_OFFSET + (NPAD_COUNT * sizeof(NpadInternalState)) < shared_memory_size);
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
+ controller.shared_memory = std::construct_at(reinterpret_cast<NpadInternalState*>(
+ raw_shared_memory_ + NPAD_OFFSET + (i * sizeof(NpadInternalState))));
controller.device = hid_core.GetEmulatedControllerByIndex(i);
controller.vibration[Core::HID::EmulatedDeviceIndex::LeftIndex].latest_vibration_value =
Core::HID::DEFAULT_VIBRATION_VALUE;
@@ -113,11 +144,11 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
if (!controller.device->IsConnected()) {
return;
}
- auto& shared_memory = controller.shared_memory_entry;
+ auto* shared_memory = controller.shared_memory;
const auto& battery_level = controller.device->GetBattery();
- shared_memory.battery_level_dual = battery_level.dual.battery_level;
- shared_memory.battery_level_left = battery_level.left.battery_level;
- shared_memory.battery_level_right = battery_level.right.battery_level;
+ shared_memory->battery_level_dual = battery_level.dual.battery_level;
+ shared_memory->battery_level_left = battery_level.left.battery_level;
+ shared_memory->battery_level_right = battery_level.right.battery_level;
break;
}
default:
@@ -132,123 +163,178 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
}
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
- auto& shared_memory = controller.shared_memory_entry;
+ const auto& body_colors = controller.device->GetColors();
+ const auto& battery_level = controller.device->GetBattery();
+ auto* shared_memory = controller.shared_memory;
if (controller_type == Core::HID::NpadStyleIndex::None) {
controller.styleset_changed_event->GetWritableEvent().Signal();
return;
}
- shared_memory.style_tag.raw = Core::HID::NpadStyleSet::None;
- shared_memory.device_type.raw = 0;
- shared_memory.system_properties.raw = 0;
+
+ // Reset memory values
+ shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None;
+ shared_memory->device_type.raw = 0;
+ shared_memory->system_properties.raw = 0;
+ shared_memory->joycon_color.attribute = ColorAttribute::NoController;
+ shared_memory->joycon_color.attribute = ColorAttribute::NoController;
+ shared_memory->fullkey_color = {};
+ shared_memory->joycon_color.left = {};
+ shared_memory->joycon_color.right = {};
+ shared_memory->battery_level_dual = {};
+ shared_memory->battery_level_left = {};
+ shared_memory->battery_level_right = {};
+
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
- UNREACHABLE();
+ ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
- shared_memory.style_tag.fullkey.Assign(1);
- shared_memory.device_type.fullkey.Assign(1);
- shared_memory.system_properties.is_vertical.Assign(1);
- shared_memory.system_properties.use_plus.Assign(1);
- shared_memory.system_properties.use_minus.Assign(1);
- shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->fullkey_color.fullkey = body_colors.fullkey;
+ shared_memory->battery_level_dual = battery_level.dual.battery_level;
+ shared_memory->style_tag.fullkey.Assign(1);
+ shared_memory->device_type.fullkey.Assign(1);
+ shared_memory->system_properties.is_vertical.Assign(1);
+ shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.dual.is_charging);
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
+ shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::Handheld:
- shared_memory.style_tag.handheld.Assign(1);
- shared_memory.device_type.handheld_left.Assign(1);
- shared_memory.device_type.handheld_right.Assign(1);
- shared_memory.system_properties.is_vertical.Assign(1);
- shared_memory.system_properties.use_plus.Assign(1);
- shared_memory.system_properties.use_minus.Assign(1);
- shared_memory.system_properties.use_directional_buttons.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
- shared_memory.applet_footer.type = AppletFooterUiType::HandheldJoyConLeftJoyConRight;
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->fullkey_color.fullkey = body_colors.fullkey;
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->joycon_color.right = body_colors.right;
+ shared_memory->style_tag.handheld.Assign(1);
+ shared_memory->device_type.handheld_left.Assign(1);
+ shared_memory->device_type.handheld_right.Assign(1);
+ shared_memory->system_properties.is_vertical.Assign(1);
+ shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.use_directional_buttons.Assign(1);
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
+ shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
+ shared_memory->applet_nfc_xcd.applet_footer.type =
+ AppletFooterUiType::HandheldJoyConLeftJoyConRight;
+ shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
- shared_memory.style_tag.joycon_dual.Assign(1);
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->style_tag.joycon_dual.Assign(1);
if (controller.is_dual_left_connected) {
- shared_memory.device_type.joycon_left.Assign(1);
- shared_memory.system_properties.use_minus.Assign(1);
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->battery_level_left = battery_level.left.battery_level;
+ shared_memory->device_type.joycon_left.Assign(1);
+ shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
+ shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
}
if (controller.is_dual_right_connected) {
- shared_memory.device_type.joycon_right.Assign(1);
- shared_memory.system_properties.use_plus.Assign(1);
+ shared_memory->joycon_color.right = body_colors.right;
+ shared_memory->battery_level_right = battery_level.right.battery_level;
+ shared_memory->device_type.joycon_right.Assign(1);
+ shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
+ shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
}
- shared_memory.system_properties.use_directional_buttons.Assign(1);
- shared_memory.system_properties.is_vertical.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
+ shared_memory->system_properties.use_directional_buttons.Assign(1);
+ shared_memory->system_properties.is_vertical.Assign(1);
+ shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
+
if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
- shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual;
+ shared_memory->fullkey_color.fullkey = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
} else if (controller.is_dual_left_connected) {
- shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
+ shared_memory->fullkey_color.fullkey = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
} else {
- shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
+ shared_memory->fullkey_color.fullkey = body_colors.right;
+ shared_memory->battery_level_dual = battery_level.right.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.right.is_charging);
}
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
- shared_memory.style_tag.joycon_left.Assign(1);
- shared_memory.device_type.joycon_left.Assign(1);
- shared_memory.system_properties.is_horizontal.Assign(1);
- shared_memory.system_properties.use_minus.Assign(1);
- shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
+ shared_memory->style_tag.joycon_left.Assign(1);
+ shared_memory->device_type.joycon_left.Assign(1);
+ shared_memory->system_properties.is_horizontal.Assign(1);
+ shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
+ shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
- shared_memory.style_tag.joycon_right.Assign(1);
- shared_memory.device_type.joycon_right.Assign(1);
- shared_memory.system_properties.is_horizontal.Assign(1);
- shared_memory.system_properties.use_plus.Assign(1);
- shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.right = body_colors.right;
+ shared_memory->battery_level_right = battery_level.right.battery_level;
+ shared_memory->style_tag.joycon_right.Assign(1);
+ shared_memory->device_type.joycon_right.Assign(1);
+ shared_memory->system_properties.is_horizontal.Assign(1);
+ shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
+ shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::GameCube:
- shared_memory.style_tag.gamecube.Assign(1);
- shared_memory.device_type.fullkey.Assign(1);
- shared_memory.system_properties.is_vertical.Assign(1);
- shared_memory.system_properties.use_plus.Assign(1);
+ shared_memory->style_tag.gamecube.Assign(1);
+ shared_memory->device_type.fullkey.Assign(1);
+ shared_memory->system_properties.is_vertical.Assign(1);
+ shared_memory->system_properties.use_plus.Assign(1);
break;
case Core::HID::NpadStyleIndex::Pokeball:
- shared_memory.style_tag.palma.Assign(1);
- shared_memory.device_type.palma.Assign(1);
+ shared_memory->style_tag.palma.Assign(1);
+ shared_memory->device_type.palma.Assign(1);
+ shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::NES:
- shared_memory.style_tag.lark.Assign(1);
- shared_memory.device_type.fullkey.Assign(1);
+ shared_memory->style_tag.lark.Assign(1);
+ shared_memory->device_type.fullkey.Assign(1);
break;
case Core::HID::NpadStyleIndex::SNES:
- shared_memory.style_tag.lucia.Assign(1);
- shared_memory.device_type.fullkey.Assign(1);
- shared_memory.applet_footer.type = AppletFooterUiType::Lucia;
+ shared_memory->style_tag.lucia.Assign(1);
+ shared_memory->device_type.fullkey.Assign(1);
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lucia;
break;
case Core::HID::NpadStyleIndex::N64:
- shared_memory.style_tag.lagoon.Assign(1);
- shared_memory.device_type.fullkey.Assign(1);
- shared_memory.applet_footer.type = AppletFooterUiType::Lagon;
+ shared_memory->style_tag.lagoon.Assign(1);
+ shared_memory->device_type.fullkey.Assign(1);
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::Lagon;
break;
case Core::HID::NpadStyleIndex::SegaGenesis:
- shared_memory.style_tag.lager.Assign(1);
- shared_memory.device_type.fullkey.Assign(1);
+ shared_memory->style_tag.lager.Assign(1);
+ shared_memory->device_type.fullkey.Assign(1);
break;
default:
break;
}
- const auto& body_colors = controller.device->GetColors();
-
- shared_memory.fullkey_color.attribute = ColorAttribute::Ok;
- shared_memory.fullkey_color.fullkey = body_colors.fullkey;
-
- shared_memory.joycon_color.attribute = ColorAttribute::Ok;
- shared_memory.joycon_color.left = body_colors.left;
- shared_memory.joycon_color.right = body_colors.right;
-
- // TODO: Investigate when we should report all batery types
- const auto& battery_level = controller.device->GetBattery();
- shared_memory.battery_level_dual = battery_level.dual.battery_level;
- shared_memory.battery_level_left = battery_level.left.battery_level;
- shared_memory.battery_level_right = battery_level.right.battery_level;
-
controller.is_connected = true;
controller.device->Connect();
SignalStyleSetChangedEvent(npad_id);
- WriteEmptyEntry(controller.shared_memory_entry);
+ WriteEmptyEntry(controller.shared_memory);
}
void Controller_NPad::OnInit() {
@@ -262,23 +348,18 @@ void Controller_NPad::OnInit() {
service_context.CreateEvent(fmt::format("npad:NpadStyleSetChanged_{}", i));
}
- if (hid_core.GetSupportedStyleTag().raw == Core::HID::NpadStyleSet::None) {
- // We want to support all controllers
- hid_core.SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
- }
-
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(Core::HID::NpadIdType));
// Prefill controller buffers
for (auto& controller : controller_data) {
- auto& npad = controller.shared_memory_entry;
- npad.fullkey_color = {
+ auto* npad = controller.shared_memory;
+ npad->fullkey_color = {
.attribute = ColorAttribute::NoController,
.fullkey = {},
};
- npad.joycon_color = {
+ npad->joycon_color = {
.attribute = ColorAttribute::NoController,
.left = {},
.right = {},
@@ -288,38 +369,31 @@ void Controller_NPad::OnInit() {
WriteEmptyEntry(npad);
}
}
-
- // Connect controllers
- for (auto& controller : controller_data) {
- const auto& device = controller.device;
- if (device->IsConnected()) {
- AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
- }
- }
}
-void Controller_NPad::WriteEmptyEntry(NpadInternalState& npad) {
+void Controller_NPad::WriteEmptyEntry(NpadInternalState* npad) {
NPadGenericState dummy_pad_state{};
NpadGcTriggerState dummy_gc_state{};
- dummy_pad_state.sampling_number = npad.fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
- npad.fullkey_lifo.WriteNextEntry(dummy_pad_state);
- dummy_pad_state.sampling_number = npad.handheld_lifo.ReadCurrentEntry().sampling_number + 1;
- npad.handheld_lifo.WriteNextEntry(dummy_pad_state);
- dummy_pad_state.sampling_number = npad.joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
- npad.joy_dual_lifo.WriteNextEntry(dummy_pad_state);
- dummy_pad_state.sampling_number = npad.joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
- npad.joy_left_lifo.WriteNextEntry(dummy_pad_state);
- dummy_pad_state.sampling_number = npad.joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
- npad.joy_right_lifo.WriteNextEntry(dummy_pad_state);
- dummy_pad_state.sampling_number = npad.palma_lifo.ReadCurrentEntry().sampling_number + 1;
- npad.palma_lifo.WriteNextEntry(dummy_pad_state);
- dummy_pad_state.sampling_number = npad.system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
- npad.system_ext_lifo.WriteNextEntry(dummy_pad_state);
- dummy_gc_state.sampling_number = npad.gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
- npad.gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
+ dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad->fullkey_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad->handheld_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad->joy_left_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad->joy_right_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad->palma_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad->system_ext_lifo.WriteNextEntry(dummy_pad_state);
+ dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
+ npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
}
void Controller_NPad::OnRelease() {
+ is_controller_initialized = false;
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
service_context.CloseEvent(controller.styleset_changed_event);
@@ -330,7 +404,7 @@ void Controller_NPad::OnRelease() {
}
void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
- std::lock_guard lock{mutex};
+ std::scoped_lock lock{mutex};
auto& controller = GetControllerFromNpadIdType(npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
if (!controller.device->IsConnected()) {
@@ -381,23 +455,19 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) {
}
}
-void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t data_len) {
+void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < controller_data.size(); ++i) {
auto& controller = controller_data[i];
- auto& npad = controller.shared_memory_entry;
+ auto* npad = controller.shared_memory;
const auto& controller_type = controller.device->GetNpadStyleIndex();
if (controller_type == Core::HID::NpadStyleIndex::None ||
!controller.device->IsConnected()) {
- // Refresh shared memory
- std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
- &controller.shared_memory_entry, sizeof(NpadInternalState));
continue;
}
@@ -412,7 +482,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_state.connection_status.is_connected.Assign(1);
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
- UNREACHABLE();
+ ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
case Core::HID::NpadStyleIndex::NES:
@@ -425,8 +495,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_state.connection_status.is_wired.Assign(1);
pad_state.sampling_number =
- npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad.fullkey_lifo.WriteNextEntry(pad_state);
+ npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->fullkey_lifo.WriteNextEntry(pad_state);
break;
case Core::HID::NpadStyleIndex::Handheld:
pad_state.connection_status.raw = 0;
@@ -443,8 +513,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_state.connection_status.is_left_wired.Assign(1);
libnx_state.connection_status.is_right_wired.Assign(1);
pad_state.sampling_number =
- npad.handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad.handheld_lifo.WriteNextEntry(pad_state);
+ npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->handheld_lifo.WriteNextEntry(pad_state);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
pad_state.connection_status.raw = 0;
@@ -459,8 +529,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
}
pad_state.sampling_number =
- npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad.joy_dual_lifo.WriteNextEntry(pad_state);
+ npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->joy_dual_lifo.WriteNextEntry(pad_state);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
pad_state.connection_status.raw = 0;
@@ -469,8 +539,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_state.connection_status.is_left_connected.Assign(1);
pad_state.sampling_number =
- npad.joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad.joy_left_lifo.WriteNextEntry(pad_state);
+ npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->joy_left_lifo.WriteNextEntry(pad_state);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
pad_state.connection_status.raw = 0;
@@ -479,8 +549,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_state.connection_status.is_right_connected.Assign(1);
pad_state.sampling_number =
- npad.joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad.joy_right_lifo.WriteNextEntry(pad_state);
+ npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->joy_right_lifo.WriteNextEntry(pad_state);
break;
case Core::HID::NpadStyleIndex::GameCube:
pad_state.connection_status.raw = 0;
@@ -489,18 +559,18 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_state.connection_status.is_wired.Assign(1);
pad_state.sampling_number =
- npad.fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
trigger_state.sampling_number =
- npad.gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad.fullkey_lifo.WriteNextEntry(pad_state);
- npad.gc_trigger_lifo.WriteNextEntry(trigger_state);
+ npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->fullkey_lifo.WriteNextEntry(pad_state);
+ npad->gc_trigger_lifo.WriteNextEntry(trigger_state);
break;
case Core::HID::NpadStyleIndex::Pokeball:
pad_state.connection_status.raw = 0;
pad_state.connection_status.is_connected.Assign(1);
pad_state.sampling_number =
- npad.palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
- npad.palma_lifo.WriteNextEntry(pad_state);
+ npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->palma_lifo.WriteNextEntry(pad_state);
break;
default:
break;
@@ -509,17 +579,13 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
libnx_state.l_stick = pad_state.l_stick;
libnx_state.r_stick = pad_state.r_stick;
- npad.system_ext_lifo.WriteNextEntry(pad_state);
+ npad->system_ext_lifo.WriteNextEntry(pad_state);
press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
-
- std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
- &controller.shared_memory_entry, sizeof(NpadInternalState));
}
}
-void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t data_len) {
+void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
return;
}
@@ -534,7 +600,7 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
continue;
}
- auto& npad = controller.shared_memory_entry;
+ auto* npad = controller.shared_memory;
const auto& motion_state = controller.device->GetMotions();
auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state;
auto& sixaxis_handheld_state = controller.sixaxis_handheld_state;
@@ -543,6 +609,14 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state;
auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state;
+ // Clear previous state
+ sixaxis_fullkey_state = {};
+ sixaxis_handheld_state = {};
+ sixaxis_dual_left_state = {};
+ sixaxis_dual_right_state = {};
+ sixaxis_left_lifo_state = {};
+ sixaxis_right_lifo_state = {};
+
if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) {
controller.sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_state.size(); ++e) {
@@ -551,109 +625,116 @@ void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing
}
}
+ const auto set_motion_state = [&](SixAxisSensorState& state,
+ const Core::HID::ControllerMotion& hid_state) {
+ using namespace std::literals::chrono_literals;
+ static constexpr SixAxisSensorState default_motion_state = {
+ .delta_time = std::chrono::nanoseconds(5ms).count(),
+ .accel = {0, 0, -1.0f},
+ .orientation =
+ {
+ Common::Vec3f{1.0f, 0, 0},
+ Common::Vec3f{0, 1.0f, 0},
+ Common::Vec3f{0, 0, 1.0f},
+ },
+ .attribute = {1},
+ };
+ if (!controller.sixaxis_sensor_enabled) {
+ state = default_motion_state;
+ return;
+ }
+ if (!Settings::values.motion_enabled.GetValue()) {
+ state = default_motion_state;
+ return;
+ }
+ state.attribute.is_connected.Assign(1);
+ state.delta_time = std::chrono::nanoseconds(5ms).count();
+ state.accel = hid_state.accel;
+ state.gyro = hid_state.gyro;
+ state.rotation = hid_state.rotation;
+ state.orientation = hid_state.orientation;
+ };
+
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
- UNREACHABLE();
+ ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
- sixaxis_fullkey_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- sixaxis_fullkey_state.attribute.is_connected.Assign(1);
- sixaxis_fullkey_state.accel = motion_state[0].accel;
- sixaxis_fullkey_state.gyro = motion_state[0].gyro;
- sixaxis_fullkey_state.rotation = motion_state[0].rotation;
- sixaxis_fullkey_state.orientation = motion_state[0].orientation;
- }
+ set_motion_state(sixaxis_fullkey_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::Handheld:
- sixaxis_handheld_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- sixaxis_handheld_state.attribute.is_connected.Assign(1);
- sixaxis_handheld_state.accel = motion_state[0].accel;
- sixaxis_handheld_state.gyro = motion_state[0].gyro;
- sixaxis_handheld_state.rotation = motion_state[0].rotation;
- sixaxis_handheld_state.orientation = motion_state[0].orientation;
- }
+ set_motion_state(sixaxis_handheld_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
- sixaxis_dual_left_state.attribute.raw = 0;
- sixaxis_dual_right_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- // Set motion for the left joycon
- sixaxis_dual_left_state.attribute.is_connected.Assign(1);
- sixaxis_dual_left_state.accel = motion_state[0].accel;
- sixaxis_dual_left_state.gyro = motion_state[0].gyro;
- sixaxis_dual_left_state.rotation = motion_state[0].rotation;
- sixaxis_dual_left_state.orientation = motion_state[0].orientation;
- }
- if (controller.sixaxis_sensor_enabled) {
- // Set motion for the right joycon
- sixaxis_dual_right_state.attribute.is_connected.Assign(1);
- sixaxis_dual_right_state.accel = motion_state[1].accel;
- sixaxis_dual_right_state.gyro = motion_state[1].gyro;
- sixaxis_dual_right_state.rotation = motion_state[1].rotation;
- sixaxis_dual_right_state.orientation = motion_state[1].orientation;
- }
+ set_motion_state(sixaxis_dual_left_state, motion_state[0]);
+ set_motion_state(sixaxis_dual_right_state, motion_state[1]);
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
- sixaxis_left_lifo_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- sixaxis_left_lifo_state.attribute.is_connected.Assign(1);
- sixaxis_left_lifo_state.accel = motion_state[0].accel;
- sixaxis_left_lifo_state.gyro = motion_state[0].gyro;
- sixaxis_left_lifo_state.rotation = motion_state[0].rotation;
- sixaxis_left_lifo_state.orientation = motion_state[0].orientation;
- }
+ set_motion_state(sixaxis_left_lifo_state, motion_state[0]);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
- sixaxis_right_lifo_state.attribute.raw = 0;
- if (controller.sixaxis_sensor_enabled) {
- sixaxis_right_lifo_state.attribute.is_connected.Assign(1);
- sixaxis_right_lifo_state.accel = motion_state[1].accel;
- sixaxis_right_lifo_state.gyro = motion_state[1].gyro;
- sixaxis_right_lifo_state.rotation = motion_state[1].rotation;
- sixaxis_right_lifo_state.orientation = motion_state[1].orientation;
- }
+ set_motion_state(sixaxis_right_lifo_state, motion_state[1]);
+ break;
+ case Core::HID::NpadStyleIndex::Pokeball:
+ using namespace std::literals::chrono_literals;
+ set_motion_state(sixaxis_fullkey_state, motion_state[0]);
+ sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count();
break;
default:
break;
}
sixaxis_fullkey_state.sampling_number =
- npad.sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->sixaxis_fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_handheld_state.sampling_number =
- npad.sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->sixaxis_handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_left_state.sampling_number =
- npad.sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->sixaxis_dual_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_dual_right_state.sampling_number =
- npad.sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->sixaxis_dual_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_left_lifo_state.sampling_number =
- npad.sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->sixaxis_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
sixaxis_right_lifo_state.sampling_number =
- npad.sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
+ npad->sixaxis_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
if (Core::HID::IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) {
// This buffer only is updated on handheld on HW
- npad.sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
+ npad->sixaxis_handheld_lifo.WriteNextEntry(sixaxis_handheld_state);
} else {
// Handheld doesn't update this buffer on HW
- npad.sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
+ npad->sixaxis_fullkey_lifo.WriteNextEntry(sixaxis_fullkey_state);
}
- npad.sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
- npad.sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
- npad.sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
- npad.sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
- std::memcpy(data + NPAD_OFFSET + (i * sizeof(NpadInternalState)),
- &controller.shared_memory_entry, sizeof(NpadInternalState));
+ npad->sixaxis_dual_left_lifo.WriteNextEntry(sixaxis_dual_left_state);
+ npad->sixaxis_dual_right_lifo.WriteNextEntry(sixaxis_dual_right_state);
+ npad->sixaxis_left_lifo.WriteNextEntry(sixaxis_left_lifo_state);
+ npad->sixaxis_right_lifo.WriteNextEntry(sixaxis_right_lifo_state);
}
}
void Controller_NPad::SetSupportedStyleSet(Core::HID::NpadStyleTag style_set) {
hid_core.SetSupportedStyleTag(style_set);
+
+ if (is_controller_initialized) {
+ return;
+ }
+
+ // Once SetSupportedStyleSet is called controllers are fully initialized
+ is_controller_initialized = true;
+
+ // Connect all active controllers
+ for (auto& controller : controller_data) {
+ const auto& device = controller.device;
+ if (device->IsConnected()) {
+ AddNewControllerAt(device->GetNpadStyleIndex(), device->GetNpadIdType());
+ }
+ }
}
Core::HID::NpadStyleTag Controller_NPad::GetSupportedStyleSet() const {
+ if (!is_controller_initialized) {
+ return {Core::HID::NpadStyleSet::None};
+ }
return hid_core.GetSupportedStyleTag();
}
@@ -674,6 +755,12 @@ std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
}
void Controller_NPad::SetHoldType(NpadJoyHoldType joy_hold_type) {
+ if (joy_hold_type != NpadJoyHoldType::Horizontal &&
+ joy_hold_type != NpadJoyHoldType::Vertical) {
+ LOG_ERROR(Service_HID, "Npad joy hold type needs to be valid, joy_hold_type={}",
+ joy_hold_type);
+ return;
+ }
hold_type = joy_hold_type;
}
@@ -682,6 +769,11 @@ Controller_NPad::NpadJoyHoldType Controller_NPad::GetHoldType() const {
}
void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
+ if (activation_mode >= NpadHandheldActivationMode::MaxActivationMode) {
+ ASSERT_MSG(false, "Activation mode should be always None, Single or Dual");
+ return;
+ }
+
handheld_activation_mode = activation_mode;
}
@@ -697,20 +789,21 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode;
}
-void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
- NpadJoyAssignmentMode assignment_mode) {
+Result Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
+ NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return;
+ return InvalidNpadId;
}
auto& controller = GetControllerFromNpadIdType(npad_id);
- if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
- controller.shared_memory_entry.assignment_mode = assignment_mode;
+ if (controller.shared_memory->assignment_mode != assignment_mode) {
+ controller.shared_memory->assignment_mode = assignment_mode;
}
if (!controller.device->IsConnected()) {
- return;
+ return ResultSuccess;
}
if (assignment_mode == NpadJoyAssignmentMode::Dual) {
@@ -719,34 +812,34 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
- return;
+ return ResultSuccess;
}
if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
DisconnectNpad(npad_id);
controller.is_dual_left_connected = false;
controller.is_dual_right_connected = true;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
- return;
+ return ResultSuccess;
}
- return;
+ return ResultSuccess;
}
// This is for NpadJoyAssignmentMode::Single
// Only JoyconDual get affected by this function
if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
- return;
+ return ResultSuccess;
}
if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
- return;
+ return ResultSuccess;
}
if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
DisconnectNpad(npad_id);
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
- return;
+ return ResultSuccess;
}
// We have two controllers connected to the same npad_id we need to split them
@@ -764,6 +857,7 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceTy
controller_2.is_dual_right_connected = false;
UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
}
+ return ResultSuccess;
}
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@@ -795,11 +889,11 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
const auto now = steady_clock::now();
- // Filter out non-zero vibrations that are within 10ms of each other.
+ // Filter out non-zero vibrations that are within 15ms of each other.
if ((vibration_value.low_amplitude != 0.0f || vibration_value.high_amplitude != 0.0f) &&
duration_cast<milliseconds>(
now - controller.vibration[device_index].last_vibration_timepoint) <
- milliseconds(10)) {
+ milliseconds(15)) {
return false;
}
@@ -815,7 +909,7 @@ bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
void Controller_NPad::VibrateController(
const Core::HID::VibrationDeviceHandle& vibration_device_handle,
const Core::HID::VibrationValue& vibration_value) {
- if (!IsDeviceHandleValid(vibration_device_handle)) {
+ if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return;
}
@@ -831,7 +925,7 @@ void Controller_NPad::VibrateController(
}
if (vibration_device_handle.device_index == Core::HID::DeviceIndex::None) {
- UNREACHABLE_MSG("DeviceIndex should never be None!");
+ ASSERT_MSG(false, "DeviceIndex should never be None!");
return;
}
@@ -878,7 +972,7 @@ void Controller_NPad::VibrateControllers(
Core::HID::VibrationValue Controller_NPad::GetLastVibration(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
- if (!IsDeviceHandleValid(vibration_device_handle)) {
+ if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return {};
}
@@ -889,7 +983,7 @@ Core::HID::VibrationValue Controller_NPad::GetLastVibration(
void Controller_NPad::InitializeVibrationDevice(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) {
- if (!IsDeviceHandleValid(vibration_device_handle)) {
+ if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return;
}
@@ -916,7 +1010,7 @@ void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
bool Controller_NPad::IsVibrationDeviceMounted(
const Core::HID::VibrationDeviceHandle& vibration_device_handle) const {
- if (!IsDeviceHandleValid(vibration_device_handle)) {
+ if (IsDeviceHandleValid(vibration_device_handle).IsError()) {
return false;
}
@@ -959,10 +1053,10 @@ void Controller_NPad::UpdateControllerAt(Core::HID::NpadStyleIndex type,
InitNewlyAddedController(npad_id);
}
-void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
+Result Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return;
+ return InvalidNpadId;
}
LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
@@ -973,188 +1067,300 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
controller.vibration[device_idx].device_mounted = false;
}
- auto& shared_memory_entry = controller.shared_memory_entry;
- // Don't reset shared_memory_entry.assignment_mode this value is persistent
- shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
- shared_memory_entry.device_type.raw = 0;
- shared_memory_entry.system_properties.raw = 0;
- shared_memory_entry.button_properties.raw = 0;
- shared_memory_entry.battery_level_dual = 0;
- shared_memory_entry.battery_level_left = 0;
- shared_memory_entry.battery_level_right = 0;
- shared_memory_entry.fullkey_color = {
+ auto* shared_memory = controller.shared_memory;
+ // Don't reset shared_memory->assignment_mode this value is persistent
+ shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
+ shared_memory->device_type.raw = 0;
+ shared_memory->system_properties.raw = 0;
+ shared_memory->button_properties.raw = 0;
+ shared_memory->sixaxis_fullkey_properties.raw = 0;
+ shared_memory->sixaxis_handheld_properties.raw = 0;
+ shared_memory->sixaxis_dual_left_properties.raw = 0;
+ shared_memory->sixaxis_dual_right_properties.raw = 0;
+ shared_memory->sixaxis_left_properties.raw = 0;
+ shared_memory->sixaxis_right_properties.raw = 0;
+ shared_memory->battery_level_dual = 0;
+ shared_memory->battery_level_left = 0;
+ shared_memory->battery_level_right = 0;
+ shared_memory->fullkey_color = {
.attribute = ColorAttribute::NoController,
.fullkey = {},
};
- shared_memory_entry.joycon_color = {
+ shared_memory->joycon_color = {
.attribute = ColorAttribute::NoController,
.left = {},
.right = {},
};
- shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
+ shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::None;
controller.is_dual_left_connected = true;
controller.is_dual_right_connected = true;
controller.is_connected = false;
controller.device->Disconnect();
SignalStyleSetChangedEvent(npad_id);
- WriteEmptyEntry(controller.shared_memory_entry);
+ WriteEmptyEntry(shared_memory);
+ return ResultSuccess;
}
+Result Controller_NPad::SetGyroscopeZeroDriftMode(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, GyroscopeZeroDriftMode drift_mode) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
-void Controller_NPad::SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
- GyroscopeZeroDriftMode drift_mode) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return;
+ auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ sixaxis.gyroscope_zero_drift_mode = drift_mode;
+
+ return ResultSuccess;
+}
+
+Result Controller_NPad::GetGyroscopeZeroDriftMode(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ GyroscopeZeroDriftMode& drift_mode) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- controller.gyroscope_zero_drift_mode = drift_mode;
+
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ drift_mode = sixaxis.gyroscope_zero_drift_mode;
+
+ return ResultSuccess;
}
-Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMode(
- Core::HID::SixAxisSensorHandle sixaxis_handle) const {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- // Return the default value
- return GyroscopeZeroDriftMode::Standard;
+Result Controller_NPad::IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool& is_at_rest) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
+
const auto& controller = GetControllerFromHandle(sixaxis_handle);
- return controller.gyroscope_zero_drift_mode;
+ is_at_rest = controller.sixaxis_at_rest;
+ return ResultSuccess;
}
-bool Controller_NPad::IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- // Return the default value
- return true;
+Result Controller_NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- const auto& controller = GetControllerFromHandle(sixaxis_handle);
- return controller.sixaxis_at_rest;
+
+ const auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
+ is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
+ return ResultSuccess;
}
-void Controller_NPad::SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
- bool sixaxis_status) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return;
+Result Controller_NPad::EnableSixAxisSensorUnalteredPassthrough(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ sixaxis.unaltered_passtrough = is_enabled;
+ return ResultSuccess;
+}
+
+Result Controller_NPad::IsSixAxisSensorUnalteredPassthroughEnabled(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ is_enabled = sixaxis.unaltered_passtrough;
+ return ResultSuccess;
+}
+
+Result Controller_NPad::LoadSixAxisSensorCalibrationParameter(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorCalibrationParameter& calibration) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ // TODO: Request this data to the controller. On error return 0xd8ca
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ calibration = sixaxis.calibration;
+ return ResultSuccess;
+}
+
+Result Controller_NPad::GetSixAxisSensorIcInformation(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorIcInformation& ic_information) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ // TODO: Request this data to the controller. On error return 0xd8ca
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ ic_information = sixaxis.ic_information;
+ return ResultSuccess;
+}
+
+Result Controller_NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
+
+ auto& sixaxis_properties = GetSixaxisProperties(sixaxis_handle);
+ sixaxis_properties.is_newly_assigned.Assign(0);
+
+ return ResultSuccess;
+}
+
+Result Controller_NPad::SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool sixaxis_status) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
auto& controller = GetControllerFromHandle(sixaxis_handle);
controller.sixaxis_sensor_enabled = sixaxis_status;
+ return ResultSuccess;
}
-void Controller_NPad::SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
- bool sixaxis_fusion_status) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return;
+Result Controller_NPad::IsSixAxisSensorFusionEnabled(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_fusion_enabled) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- controller.sixaxis_fusion_enabled = sixaxis_fusion_status;
+
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ is_fusion_enabled = sixaxis.is_fusion_enabled;
+
+ return ResultSuccess;
+}
+Result Controller_NPad::SetSixAxisFusionEnabled(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_fusion_enabled) {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
+ }
+
+ auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ sixaxis.is_fusion_enabled = is_fusion_enabled;
+
+ return ResultSuccess;
}
-void Controller_NPad::SetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle,
+Result Controller_NPad::SetSixAxisFusionParameters(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return;
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- controller.sixaxis_fusion = sixaxis_fusion_parameters;
-}
-Core::HID::SixAxisSensorFusionParameters Controller_NPad::GetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- // Since these parameters are unknow just return zeros
- return {};
+ const auto param1 = sixaxis_fusion_parameters.parameter1;
+ if (param1 < 0.0f || param1 > 1.0f) {
+ return InvalidSixAxisFusionRange;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- return controller.sixaxis_fusion;
+
+ auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ sixaxis.fusion = sixaxis_fusion_parameters;
+
+ return ResultSuccess;
}
-void Controller_NPad::ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle) {
- if (!IsDeviceHandleValid(sixaxis_handle)) {
- LOG_ERROR(Service_HID, "Invalid handle");
- return;
+Result Controller_NPad::GetSixAxisFusionParameters(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorFusionParameters& parameters) const {
+ const auto is_valid = VerifyValidSixAxisSensorHandle(sixaxis_handle);
+ if (is_valid.IsError()) {
+ LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
+ return is_valid;
}
- auto& controller = GetControllerFromHandle(sixaxis_handle);
- // Since these parameters are unknow just fill with zeros
- controller.sixaxis_fusion = {};
+
+ const auto& sixaxis = GetSixaxisState(sixaxis_handle);
+ parameters = sixaxis.fusion;
+
+ return ResultSuccess;
}
-void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
- Core::HID::NpadIdType npad_id_2) {
+Result Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
- return;
+ return InvalidNpadId;
}
auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
- const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
- const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
- bool merge_controllers = false;
+ auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
+ auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
+
+ // Simplify this code by converting dualjoycon with only a side connected to single joycons
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) {
+ if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
+ controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft;
+ }
+ if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
+ controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight;
+ }
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
+ if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
+ controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft;
+ }
+ if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
+ controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight;
+ }
+ }
- // If the controllers at both npad indices form a pair of left and right joycons, merge them.
- // Otherwise, do nothing.
+ // Invalid merge errors
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual ||
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
+ return NpadIsDualJoycon;
+ }
if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) {
+ return NpadIsSameType;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
- merge_controllers = true;
- }
- if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
- controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
- merge_controllers = true;
- }
- if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
- controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
- !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
- controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
- !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
- !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
- merge_controllers = true;
- }
- if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
- controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
- !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
- controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
- merge_controllers = true;
- }
-
- if (merge_controllers) {
- // Disconnect the joycon at the second id and connect the dual joycon at the first index.
- DisconnectNpad(npad_id_2);
- controller_1.is_dual_left_connected = true;
- controller_1.is_dual_right_connected = true;
- AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
- return;
+ return NpadIsSameType;
+ }
+
+ // These exceptions are handled as if they where dual joycon
+ if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) {
+ return NpadIsDualJoycon;
+ }
+ if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) {
+ return NpadIsDualJoycon;
}
- LOG_WARNING(Service_HID,
- "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
- "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
- npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
- controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
- controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
- controller_2.is_dual_right_connected);
+
+ // Disconnect the joycon at the second id and connect the dual joycon at the first index.
+ DisconnectNpad(npad_id_2);
+ controller_1.is_dual_left_connected = true;
+ controller_1.is_dual_right_connected = true;
+ AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
+ return ResultSuccess;
}
void Controller_NPad::StartLRAssignmentMode() {
@@ -1167,17 +1373,17 @@ void Controller_NPad::StopLRAssignmentMode() {
is_in_lr_assignment_mode = false;
}
-bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
- Core::HID::NpadIdType npad_id_2) {
+Result Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2) {
if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
npad_id_2);
- return false;
+ return InvalidNpadId;
}
if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
npad_id_2 == Core::HID::NpadIdType::Other) {
- return true;
+ return ResultSuccess;
}
const auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
const auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
@@ -1187,46 +1393,49 @@ bool Controller_NPad::SwapNpadAssignment(Core::HID::NpadIdType npad_id_1,
const auto is_connected_2 = controller_2->IsConnected();
if (!IsControllerSupported(type_index_1) && is_connected_1) {
- return false;
+ return NpadNotConnected;
}
if (!IsControllerSupported(type_index_2) && is_connected_2) {
- return false;
+ return NpadNotConnected;
}
UpdateControllerAt(type_index_2, npad_id_1, is_connected_2);
UpdateControllerAt(type_index_1, npad_id_2, is_connected_1);
- return true;
+ return ResultSuccess;
}
-Core::HID::LedPattern Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id) {
+Result Controller_NPad::GetLedPattern(Core::HID::NpadIdType npad_id,
+ Core::HID::LedPattern& pattern) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return Core::HID::LedPattern{0, 0, 0, 0};
+ return InvalidNpadId;
}
const auto& controller = GetControllerFromNpadIdType(npad_id).device;
- return controller->GetLedPattern();
+ pattern = controller->GetLedPattern();
+ return ResultSuccess;
}
-bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(
- Core::HID::NpadIdType npad_id) const {
+Result Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
+ bool& is_valid) const {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- // Return the default value
- return false;
+ return InvalidNpadId;
}
const auto& controller = GetControllerFromNpadIdType(npad_id);
- return controller.unintended_home_button_input_protection;
+ is_valid = controller.unintended_home_button_input_protection;
+ return ResultSuccess;
}
-void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
- Core::HID::NpadIdType npad_id) {
+Result Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(
+ bool is_protection_enabled, Core::HID::NpadIdType npad_id) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
- return;
+ return InvalidNpadId;
}
auto& controller = GetControllerFromNpadIdType(npad_id);
controller.unintended_home_button_input_protection = is_protection_enabled;
+ return ResultSuccess;
}
void Controller_NPad::SetAnalogStickUseCenterClamp(bool use_center_clamp) {
@@ -1364,4 +1573,96 @@ const Controller_NPad::NpadControllerData& Controller_NPad::GetControllerFromNpa
return controller_data[npad_index];
}
+Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return controller.shared_memory->sixaxis_fullkey_properties;
+ case Core::HID::NpadStyleIndex::Handheld:
+ return controller.shared_memory->sixaxis_handheld_properties;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ return controller.shared_memory->sixaxis_dual_left_properties;
+ }
+ return controller.shared_memory->sixaxis_dual_right_properties;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return controller.shared_memory->sixaxis_left_properties;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return controller.shared_memory->sixaxis_right_properties;
+ default:
+ return controller.shared_memory->sixaxis_fullkey_properties;
+ }
+}
+
+const Core::HID::SixAxisSensorProperties& Controller_NPad::GetSixaxisProperties(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
+ const auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return controller.shared_memory->sixaxis_fullkey_properties;
+ case Core::HID::NpadStyleIndex::Handheld:
+ return controller.shared_memory->sixaxis_handheld_properties;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ return controller.shared_memory->sixaxis_dual_left_properties;
+ }
+ return controller.shared_memory->sixaxis_dual_right_properties;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return controller.shared_memory->sixaxis_left_properties;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return controller.shared_memory->sixaxis_right_properties;
+ default:
+ return controller.shared_memory->sixaxis_fullkey_properties;
+ }
+}
+
+Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
+ auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return controller.sixaxis_fullkey;
+ case Core::HID::NpadStyleIndex::Handheld:
+ return controller.sixaxis_handheld;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ return controller.sixaxis_dual_left;
+ }
+ return controller.sixaxis_dual_right;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return controller.sixaxis_left;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return controller.sixaxis_right;
+ default:
+ return controller.sixaxis_unknown;
+ }
+}
+
+const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
+ const auto& controller = GetControllerFromHandle(sixaxis_handle);
+ switch (sixaxis_handle.npad_type) {
+ case Core::HID::NpadStyleIndex::ProController:
+ case Core::HID::NpadStyleIndex::Pokeball:
+ return controller.sixaxis_fullkey;
+ case Core::HID::NpadStyleIndex::Handheld:
+ return controller.sixaxis_handheld;
+ case Core::HID::NpadStyleIndex::JoyconDual:
+ if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
+ return controller.sixaxis_dual_left;
+ }
+ return controller.sixaxis_dual_right;
+ case Core::HID::NpadStyleIndex::JoyconLeft:
+ return controller.sixaxis_left;
+ case Core::HID::NpadStyleIndex::JoyconRight:
+ return controller.sixaxis_right;
+ default:
+ return controller.sixaxis_unknown;
+ }
+}
+
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 6b2872bad..1a589cca2 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,7 +9,8 @@
#include "common/bit_field.h"
#include "common/common_types.h"
-#include "common/quaternion.h"
+#include "common/vector_math.h"
+
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
@@ -27,13 +27,15 @@ class KReadableEvent;
namespace Service::KernelHelpers {
class ServiceContext;
-}
+} // namespace Service::KernelHelpers
+
+union Result;
namespace Service::HID {
class Controller_NPad final : public ControllerBase {
public:
- explicit Controller_NPad(Core::HID::HIDCore& hid_core_,
+ explicit Controller_NPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
KernelHelpers::ServiceContext& service_context_);
~Controller_NPad() override;
@@ -44,11 +46,10 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
// When the controller is requesting a motion update for the shared memory
- void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) override;
+ void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) override;
// This is nn::hid::GyroscopeZeroDriftMode
enum class GyroscopeZeroDriftMode : u32 {
@@ -80,6 +81,7 @@ public:
Dual = 0,
Single = 1,
None = 2,
+ MaxActivationMode = 3,
};
// This is nn::hid::NpadCommunicationMode
@@ -106,8 +108,8 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
- void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
- NpadJoyAssignmentMode assignment_mode);
+ Result SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
@@ -140,46 +142,68 @@ public:
void UpdateControllerAt(Core::HID::NpadStyleIndex controller, Core::HID::NpadIdType npad_id,
bool connected);
- void DisconnectNpad(Core::HID::NpadIdType npad_id);
-
- void SetGyroscopeZeroDriftMode(Core::HID::SixAxisSensorHandle sixaxis_handle,
- GyroscopeZeroDriftMode drift_mode);
- GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode(
- Core::HID::SixAxisSensorHandle sixaxis_handle) const;
- bool IsSixAxisSensorAtRest(Core::HID::SixAxisSensorHandle sixaxis_handle) const;
- void SetSixAxisEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle, bool sixaxis_status);
- void SetSixAxisFusionEnabled(Core::HID::SixAxisSensorHandle sixaxis_handle,
- bool sixaxis_fusion_status);
- void SetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle,
+ Result DisconnectNpad(Core::HID::NpadIdType npad_id);
+
+ Result SetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ GyroscopeZeroDriftMode drift_mode);
+ Result GetGyroscopeZeroDriftMode(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ GyroscopeZeroDriftMode& drift_mode) const;
+ Result IsSixAxisSensorAtRest(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool& is_at_rest) const;
+ Result IsFirmwareUpdateAvailableForSixAxisSensor(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_firmware_available) const;
+ Result EnableSixAxisSensorUnalteredPassthrough(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool is_enabled);
+ Result IsSixAxisSensorUnalteredPassthroughEnabled(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle, bool& is_enabled) const;
+ Result LoadSixAxisSensorCalibrationParameter(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorCalibrationParameter& calibration) const;
+ Result GetSixAxisSensorIcInformation(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorIcInformation& ic_information) const;
+ Result ResetIsSixAxisSensorDeviceNewlyAssigned(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle);
+ Result SetSixAxisEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool sixaxis_status);
+ Result IsSixAxisSensorFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool& is_fusion_enabled) const;
+ Result SetSixAxisFusionEnabled(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ bool is_fusion_enabled);
+ Result SetSixAxisFusionParameters(
+ const Core::HID::SixAxisSensorHandle& sixaxis_handle,
Core::HID::SixAxisSensorFusionParameters sixaxis_fusion_parameters);
- Core::HID::SixAxisSensorFusionParameters GetSixAxisFusionParameters(
- Core::HID::SixAxisSensorHandle sixaxis_handle);
- void ResetSixAxisFusionParameters(Core::HID::SixAxisSensorHandle sixaxis_handle);
- Core::HID::LedPattern GetLedPattern(Core::HID::NpadIdType npad_id);
- bool IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id) const;
- void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
- Core::HID::NpadIdType npad_id);
+ Result GetSixAxisFusionParameters(const Core::HID::SixAxisSensorHandle& sixaxis_handle,
+ Core::HID::SixAxisSensorFusionParameters& parameters) const;
+ Result GetLedPattern(Core::HID::NpadIdType npad_id, Core::HID::LedPattern& pattern) const;
+ Result IsUnintendedHomeButtonInputProtectionEnabled(Core::HID::NpadIdType npad_id,
+ bool& is_enabled) const;
+ Result SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
+ Core::HID::NpadIdType npad_id);
void SetAnalogStickUseCenterClamp(bool use_center_clamp);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
- void MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
+ Result MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
+ Core::HID::NpadIdType npad_id_2);
void StartLRAssignmentMode();
void StopLRAssignmentMode();
- bool SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
+ Result SwapNpadAssignment(Core::HID::NpadIdType npad_id_1, Core::HID::NpadIdType npad_id_2);
// Logical OR for all buttons presses on all controllers
// Specifically for cheat engine and other features.
Core::HID::NpadButton GetAndResetPressState();
static bool IsNpadIdValid(Core::HID::NpadIdType npad_id);
- static bool IsDeviceHandleValid(const Core::HID::SixAxisSensorHandle& device_handle);
- static bool IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
+ static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle);
+ static Result VerifyValidSixAxisSensorHandle(
+ const Core::HID::SixAxisSensorHandle& device_handle);
private:
+ static constexpr std::size_t NPAD_COUNT = 10;
+
// This is nn::hid::detail::ColorAttribute
enum class ColorAttribute : u32 {
Ok = 0,
@@ -190,16 +214,16 @@ private:
// This is nn::hid::detail::NpadFullKeyColorState
struct NpadFullKeyColorState {
- ColorAttribute attribute;
- Core::HID::NpadControllerColor fullkey;
+ ColorAttribute attribute{ColorAttribute::NoController};
+ Core::HID::NpadControllerColor fullkey{};
};
static_assert(sizeof(NpadFullKeyColorState) == 0xC, "NpadFullKeyColorState is an invalid size");
// This is nn::hid::detail::NpadJoyColorState
struct NpadJoyColorState {
- ColorAttribute attribute;
- Core::HID::NpadControllerColor left;
- Core::HID::NpadControllerColor right;
+ ColorAttribute attribute{ColorAttribute::NoController};
+ Core::HID::NpadControllerColor left{};
+ Core::HID::NpadControllerColor right{};
};
static_assert(sizeof(NpadJoyColorState) == 0x14, "NpadJoyColorState is an invalid size");
@@ -225,11 +249,11 @@ private:
// This is nn::hid::NpadPalmaState
// This is nn::hid::NpadSystemExtState
struct NPadGenericState {
- s64_le sampling_number;
- Core::HID::NpadButtonState npad_buttons;
- Core::HID::AnalogStickState l_stick;
- Core::HID::AnalogStickState r_stick;
- NpadAttribute connection_status;
+ s64_le sampling_number{};
+ Core::HID::NpadButtonState npad_buttons{};
+ Core::HID::AnalogStickState l_stick{};
+ Core::HID::AnalogStickState r_stick{};
+ NpadAttribute connection_status{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(NPadGenericState) == 0x28, "NPadGenericState is an invalid size");
@@ -252,7 +276,7 @@ private:
Common::Vec3f gyro{};
Common::Vec3f rotation{};
std::array<Common::Vec3f, 3> orientation{};
- SixAxisSensorAttribute attribute;
+ SixAxisSensorAttribute attribute{};
INSERT_PADDING_BYTES(4); // Reserved
};
static_assert(sizeof(SixAxisSensorState) == 0x60, "SixAxisSensorState is an invalid size");
@@ -324,11 +348,11 @@ private:
// This is nn::hid::detail::NfcXcdDeviceHandleStateImpl
struct NfcXcdDeviceHandleStateImpl {
- u64 handle;
- bool is_available;
- bool is_activated;
+ u64 handle{};
+ bool is_available{};
+ bool is_activated{};
INSERT_PADDING_BYTES(0x6); // Reserved
- u64 sampling_number;
+ u64 sampling_number{};
};
static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18,
"NfcXcdDeviceHandleStateImpl is an invalid size");
@@ -365,8 +389,8 @@ private:
};
struct AppletFooterUi {
- AppletFooterUiAttributes attributes;
- AppletFooterUiType type;
+ AppletFooterUiAttributes attributes{};
+ AppletFooterUiType type{AppletFooterUiType::None};
INSERT_PADDING_BYTES(0x5B); // Reserved
};
static_assert(sizeof(AppletFooterUi) == 0x60, "AppletFooterUi is an invalid size");
@@ -401,46 +425,54 @@ private:
U,
};
+ struct AppletNfcXcd {
+ union {
+ AppletFooterUi applet_footer{};
+ Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo;
+ };
+ };
+
// This is nn::hid::detail::NpadInternalState
struct NpadInternalState {
- Core::HID::NpadStyleTag style_tag;
- NpadJoyAssignmentMode assignment_mode;
- NpadFullKeyColorState fullkey_color;
- NpadJoyColorState joycon_color;
- Lifo<NPadGenericState, hid_entry_count> fullkey_lifo;
- Lifo<NPadGenericState, hid_entry_count> handheld_lifo;
- Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo;
- Lifo<NPadGenericState, hid_entry_count> joy_left_lifo;
- Lifo<NPadGenericState, hid_entry_count> joy_right_lifo;
- Lifo<NPadGenericState, hid_entry_count> palma_lifo;
- Lifo<NPadGenericState, hid_entry_count> system_ext_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo;
- Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo;
- DeviceType device_type;
+ Core::HID::NpadStyleTag style_tag{Core::HID::NpadStyleSet::None};
+ NpadJoyAssignmentMode assignment_mode{NpadJoyAssignmentMode::Dual};
+ NpadFullKeyColorState fullkey_color{};
+ NpadJoyColorState joycon_color{};
+ Lifo<NPadGenericState, hid_entry_count> fullkey_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> handheld_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> joy_dual_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> joy_left_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> joy_right_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> palma_lifo{};
+ Lifo<NPadGenericState, hid_entry_count> system_ext_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_fullkey_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_handheld_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_left_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_dual_right_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_left_lifo{};
+ Lifo<SixAxisSensorState, hid_entry_count> sixaxis_right_lifo{};
+ DeviceType device_type{};
INSERT_PADDING_BYTES(0x4); // Reserved
- NPadSystemProperties system_properties;
- NpadSystemButtonProperties button_properties;
- Core::HID::NpadBatteryLevel battery_level_dual;
- Core::HID::NpadBatteryLevel battery_level_left;
- Core::HID::NpadBatteryLevel battery_level_right;
- union {
- Lifo<NfcXcdDeviceHandleStateImpl, 0x2> nfc_xcd_device_lifo{};
- AppletFooterUi applet_footer;
- };
+ NPadSystemProperties system_properties{};
+ NpadSystemButtonProperties button_properties{};
+ Core::HID::NpadBatteryLevel battery_level_dual{};
+ Core::HID::NpadBatteryLevel battery_level_left{};
+ Core::HID::NpadBatteryLevel battery_level_right{};
+ AppletNfcXcd applet_nfc_xcd{};
INSERT_PADDING_BYTES(0x20); // Unknown
- Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo;
- NpadLarkType lark_type_l_and_main;
- NpadLarkType lark_type_r;
- NpadLuciaType lucia_type;
- NpadLagonType lagon_type;
- NpadLagerType lager_type;
- // FW 13.x Investigate there is some sort of bitflag related to joycons
- INSERT_PADDING_BYTES(0x4);
- INSERT_PADDING_BYTES(0xc08); // Unknown
+ Lifo<NpadGcTriggerState, hid_entry_count> gc_trigger_lifo{};
+ NpadLarkType lark_type_l_and_main{};
+ NpadLarkType lark_type_r{};
+ NpadLuciaType lucia_type{};
+ NpadLagonType lagon_type{};
+ NpadLagerType lager_type{};
+ Core::HID::SixAxisSensorProperties sixaxis_fullkey_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_handheld_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_dual_left_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_dual_right_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_left_properties;
+ Core::HID::SixAxisSensorProperties sixaxis_right_properties;
+ INSERT_PADDING_BYTES(0xc06); // Unknown
};
static_assert(sizeof(NpadInternalState) == 0x5000, "NpadInternalState is an invalid size");
@@ -450,10 +482,19 @@ private:
std::chrono::steady_clock::time_point last_vibration_timepoint{};
};
+ struct SixaxisParameters {
+ bool is_fusion_enabled{true};
+ bool unaltered_passtrough{false};
+ Core::HID::SixAxisSensorFusionParameters fusion{};
+ Core::HID::SixAxisSensorCalibrationParameter calibration{};
+ Core::HID::SixAxisSensorIcInformation ic_information{};
+ GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
+ };
+
struct NpadControllerData {
- Core::HID::EmulatedController* device;
Kernel::KEvent* styleset_changed_event{};
- NpadInternalState shared_memory_entry{};
+ NpadInternalState* shared_memory = nullptr;
+ Core::HID::EmulatedController* device = nullptr;
std::array<VibrationData, 2> vibration{};
bool unintended_home_button_input_protection{};
@@ -466,9 +507,13 @@ private:
// Motion parameters
bool sixaxis_at_rest{true};
bool sixaxis_sensor_enabled{true};
- bool sixaxis_fusion_enabled{false};
- Core::HID::SixAxisSensorFusionParameters sixaxis_fusion{};
- GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
+ SixaxisParameters sixaxis_fullkey{};
+ SixaxisParameters sixaxis_handheld{};
+ SixaxisParameters sixaxis_dual_left{};
+ SixaxisParameters sixaxis_dual_right{};
+ SixaxisParameters sixaxis_left{};
+ SixaxisParameters sixaxis_right{};
+ SixaxisParameters sixaxis_unknown{};
// Current pad state
NPadGenericState npad_pad_state{};
@@ -480,14 +525,14 @@ private:
SixAxisSensorState sixaxis_dual_right_state{};
SixAxisSensorState sixaxis_left_lifo_state{};
SixAxisSensorState sixaxis_right_lifo_state{};
- int callback_key;
+ int callback_key{};
};
void ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx);
void InitNewlyAddedController(Core::HID::NpadIdType npad_id);
bool IsControllerSupported(Core::HID::NpadStyleIndex controller) const;
void RequestPadStateUpdate(Core::HID::NpadIdType npad_id);
- void WriteEmptyEntry(NpadInternalState& npad);
+ void WriteEmptyEntry(NpadInternalState* npad);
NpadControllerData& GetControllerFromHandle(
const Core::HID::SixAxisSensorHandle& device_handle);
@@ -500,9 +545,17 @@ private:
NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id);
const NpadControllerData& GetControllerFromNpadIdType(Core::HID::NpadIdType npad_id) const;
+ Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
+ const Core::HID::SixAxisSensorHandle& device_handle);
+ const Core::HID::SixAxisSensorProperties& GetSixaxisProperties(
+ const Core::HID::SixAxisSensorHandle& device_handle) const;
+ SixaxisParameters& GetSixaxisState(const Core::HID::SixAxisSensorHandle& device_handle);
+ const SixaxisParameters& GetSixaxisState(
+ const Core::HID::SixAxisSensorHandle& device_handle) const;
+
std::atomic<u64> press_state{};
- std::array<NpadControllerData, 10> controller_data{};
+ std::array<NpadControllerData, NPAD_COUNT> controller_data{};
KernelHelpers::ServiceContext& service_context;
std::mutex mutex;
std::vector<Core::HID::NpadIdType> supported_npad_id_types{};
@@ -510,7 +563,8 @@ private:
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
NpadCommunicationMode communication_mode{NpadCommunicationMode::Default};
bool permit_vibration_session_enabled{false};
- bool analog_stick_use_center_clamp{};
+ bool analog_stick_use_center_clamp{false};
bool is_in_lr_assignment_mode{false};
+ bool is_controller_initialized{false};
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/palma.cpp b/src/core/hle/service/hid/controllers/palma.cpp
new file mode 100644
index 000000000..575d4e626
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/palma.cpp
@@ -0,0 +1,229 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/core_timing.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hid/hid_types.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/service/hid/controllers/palma.h"
+#include "core/hle/service/kernel_helpers.h"
+
+namespace Service::HID {
+
+Controller_Palma::Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
+ KernelHelpers::ServiceContext& service_context_)
+ : ControllerBase{hid_core_}, service_context{service_context_} {
+ controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
+ operation_complete_event = service_context.CreateEvent("hid:PalmaOperationCompleteEvent");
+}
+
+Controller_Palma::~Controller_Palma() = default;
+
+void Controller_Palma::OnInit() {}
+
+void Controller_Palma::OnRelease() {}
+
+void Controller_Palma::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ if (!IsControllerActivated()) {
+ return;
+ }
+}
+
+Result Controller_Palma::GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id,
+ PalmaConnectionHandle& handle) {
+ active_handle.npad_id = npad_id;
+ handle = active_handle;
+ return ResultSuccess;
+}
+
+Result Controller_Palma::InitializePalma(const PalmaConnectionHandle& handle) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ ActivateController();
+ return ResultSuccess;
+}
+
+Kernel::KReadableEvent& Controller_Palma::AcquirePalmaOperationCompleteEvent(
+ const PalmaConnectionHandle& handle) const {
+ if (handle.npad_id != active_handle.npad_id) {
+ LOG_ERROR(Service_HID, "Invalid npad id {}", handle.npad_id);
+ }
+ return operation_complete_event->GetReadableEvent();
+}
+
+Result Controller_Palma::GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
+ PalmaOperationType& operation_type,
+ PalmaOperationData& data) const {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ operation_type = operation.operation;
+ data = operation.data;
+ return ResultSuccess;
+}
+
+Result Controller_Palma::PlayPalmaActivity(const PalmaConnectionHandle& handle,
+ u64 palma_activity) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ operation.operation = PalmaOperationType::PlayActivity;
+ operation.result = PalmaResultSuccess;
+ operation.data = {};
+ operation_complete_event->GetWritableEvent().Signal();
+ return ResultSuccess;
+}
+
+Result Controller_Palma::SetPalmaFrModeType(const PalmaConnectionHandle& handle,
+ PalmaFrModeType fr_mode_) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ fr_mode = fr_mode_;
+ return ResultSuccess;
+}
+
+Result Controller_Palma::ReadPalmaStep(const PalmaConnectionHandle& handle) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ operation.operation = PalmaOperationType::ReadStep;
+ operation.result = PalmaResultSuccess;
+ operation.data = {};
+ operation_complete_event->GetWritableEvent().Signal();
+ return ResultSuccess;
+}
+
+Result Controller_Palma::EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ return ResultSuccess;
+}
+
+Result Controller_Palma::ResetPalmaStep(const PalmaConnectionHandle& handle) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ return ResultSuccess;
+}
+
+void Controller_Palma::ReadPalmaApplicationSection() {}
+
+void Controller_Palma::WritePalmaApplicationSection() {}
+
+Result Controller_Palma::ReadPalmaUniqueCode(const PalmaConnectionHandle& handle) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ operation.operation = PalmaOperationType::ReadUniqueCode;
+ operation.result = PalmaResultSuccess;
+ operation.data = {};
+ operation_complete_event->GetWritableEvent().Signal();
+ return ResultSuccess;
+}
+
+Result Controller_Palma::SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ operation.operation = PalmaOperationType::SetUniqueCodeInvalid;
+ operation.result = PalmaResultSuccess;
+ operation.data = {};
+ operation_complete_event->GetWritableEvent().Signal();
+ return ResultSuccess;
+}
+
+void Controller_Palma::WritePalmaActivityEntry() {}
+
+Result Controller_Palma::WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle,
+ u64 unknown) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ operation.operation = PalmaOperationType::WriteRgbLedPatternEntry;
+ operation.result = PalmaResultSuccess;
+ operation.data = {};
+ operation_complete_event->GetWritableEvent().Signal();
+ return ResultSuccess;
+}
+
+Result Controller_Palma::WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave,
+ u8* t_mem, u64 size) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ operation.operation = PalmaOperationType::WriteWaveEntry;
+ operation.result = PalmaResultSuccess;
+ operation.data = {};
+ operation_complete_event->GetWritableEvent().Signal();
+ return ResultSuccess;
+}
+
+Result Controller_Palma::SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
+ s32 database_id_version_) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ database_id_version = database_id_version_;
+ operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
+ operation.result = PalmaResultSuccess;
+ operation.data[0] = {};
+ operation_complete_event->GetWritableEvent().Signal();
+ return ResultSuccess;
+}
+
+Result Controller_Palma::GetPalmaDataBaseIdentificationVersion(
+ const PalmaConnectionHandle& handle) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ operation.operation = PalmaOperationType::ReadDataBaseIdentificationVersion;
+ operation.result = PalmaResultSuccess;
+ operation.data = {};
+ operation.data[0] = static_cast<u8>(database_id_version);
+ operation_complete_event->GetWritableEvent().Signal();
+ return ResultSuccess;
+}
+
+void Controller_Palma::SuspendPalmaFeature() {}
+
+Result Controller_Palma::GetPalmaOperationResult(const PalmaConnectionHandle& handle) const {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ return operation.result;
+}
+void Controller_Palma::ReadPalmaPlayLog() {}
+
+void Controller_Palma::ResetPalmaPlayLog() {}
+
+void Controller_Palma::SetIsPalmaAllConnectable(bool is_all_connectable) {
+ // If true controllers are able to be paired
+ is_connectable = is_all_connectable;
+}
+
+void Controller_Palma::SetIsPalmaPairedConnectable() {}
+
+Result Controller_Palma::PairPalma(const PalmaConnectionHandle& handle) {
+ if (handle.npad_id != active_handle.npad_id) {
+ return InvalidPalmaHandle;
+ }
+ // TODO: Do something
+ return ResultSuccess;
+}
+
+void Controller_Palma::SetPalmaBoostMode(bool boost_mode) {}
+
+void Controller_Palma::CancelWritePalmaWaveEntry() {}
+
+void Controller_Palma::EnablePalmaBoostMode() {}
+
+void Controller_Palma::GetPalmaBluetoothAddress() {}
+
+void Controller_Palma::SetDisallowedPalmaConnection() {}
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/palma.h b/src/core/hle/service/hid/controllers/palma.h
new file mode 100644
index 000000000..1d7fc94e1
--- /dev/null
+++ b/src/core/hle/service/hid/controllers/palma.h
@@ -0,0 +1,163 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/hle/service/hid/errors.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::KernelHelpers {
+class ServiceContext;
+}
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::HID {
+class Controller_Palma final : public ControllerBase {
+public:
+ using PalmaOperationData = std::array<u8, 0x140>;
+
+ // This is nn::hid::PalmaOperationType
+ enum class PalmaOperationType {
+ PlayActivity,
+ SetFrModeType,
+ ReadStep,
+ EnableStep,
+ ResetStep,
+ ReadApplicationSection,
+ WriteApplicationSection,
+ ReadUniqueCode,
+ SetUniqueCodeInvalid,
+ WriteActivityEntry,
+ WriteRgbLedPatternEntry,
+ WriteWaveEntry,
+ ReadDataBaseIdentificationVersion,
+ WriteDataBaseIdentificationVersion,
+ SuspendFeature,
+ ReadPlayLog,
+ ResetPlayLog,
+ };
+
+ // This is nn::hid::PalmaWaveSet
+ enum class PalmaWaveSet : u64 {
+ Small,
+ Medium,
+ Large,
+ };
+
+ // This is nn::hid::PalmaFrModeType
+ enum class PalmaFrModeType : u64 {
+ Off,
+ B01,
+ B02,
+ B03,
+ Downloaded,
+ };
+
+ // This is nn::hid::PalmaFeature
+ enum class PalmaFeature : u64 {
+ FrMode,
+ RumbleFeedback,
+ Step,
+ MuteSwitch,
+ };
+
+ // This is nn::hid::PalmaOperationInfo
+ struct PalmaOperationInfo {
+ PalmaOperationType operation{};
+ Result result{PalmaResultSuccess};
+ PalmaOperationData data{};
+ };
+ static_assert(sizeof(PalmaOperationInfo) == 0x148, "PalmaOperationInfo is an invalid size");
+
+ // This is nn::hid::PalmaActivityEntry
+ struct PalmaActivityEntry {
+ u32 rgb_led_pattern_index;
+ INSERT_PADDING_BYTES(2);
+ PalmaWaveSet wave_set;
+ u32 wave_index;
+ INSERT_PADDING_BYTES(12);
+ };
+ static_assert(sizeof(PalmaActivityEntry) == 0x20, "PalmaActivityEntry is an invalid size");
+
+ struct PalmaConnectionHandle {
+ Core::HID::NpadIdType npad_id;
+ INSERT_PADDING_BYTES(4); // Unknown
+ };
+ static_assert(sizeof(PalmaConnectionHandle) == 0x8,
+ "PalmaConnectionHandle has incorrect size.");
+
+ explicit Controller_Palma(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_,
+ KernelHelpers::ServiceContext& service_context_);
+ ~Controller_Palma() override;
+
+ // Called when the controller is initialized
+ void OnInit() override;
+
+ // When the controller is released
+ void OnRelease() override;
+
+ // When the controller is requesting an update for the shared memory
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
+
+ Result GetPalmaConnectionHandle(Core::HID::NpadIdType npad_id, PalmaConnectionHandle& handle);
+ Result InitializePalma(const PalmaConnectionHandle& handle);
+ Kernel::KReadableEvent& AcquirePalmaOperationCompleteEvent(
+ const PalmaConnectionHandle& handle) const;
+ Result GetPalmaOperationInfo(const PalmaConnectionHandle& handle,
+ PalmaOperationType& operation_type,
+ PalmaOperationData& data) const;
+ Result PlayPalmaActivity(const PalmaConnectionHandle& handle, u64 palma_activity);
+ Result SetPalmaFrModeType(const PalmaConnectionHandle& handle, PalmaFrModeType fr_mode_);
+ Result ReadPalmaStep(const PalmaConnectionHandle& handle);
+ Result EnablePalmaStep(const PalmaConnectionHandle& handle, bool is_enabled);
+ Result ResetPalmaStep(const PalmaConnectionHandle& handle);
+ Result ReadPalmaUniqueCode(const PalmaConnectionHandle& handle);
+ Result SetPalmaUniqueCodeInvalid(const PalmaConnectionHandle& handle);
+ Result WritePalmaRgbLedPatternEntry(const PalmaConnectionHandle& handle, u64 unknown);
+ Result WritePalmaWaveEntry(const PalmaConnectionHandle& handle, PalmaWaveSet wave, u8* t_mem,
+ u64 size);
+ Result SetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle,
+ s32 database_id_version_);
+ Result GetPalmaDataBaseIdentificationVersion(const PalmaConnectionHandle& handle);
+ Result GetPalmaOperationResult(const PalmaConnectionHandle& handle) const;
+ void SetIsPalmaAllConnectable(bool is_all_connectable);
+ Result PairPalma(const PalmaConnectionHandle& handle);
+ void SetPalmaBoostMode(bool boost_mode);
+
+private:
+ void ReadPalmaApplicationSection();
+ void WritePalmaApplicationSection();
+ void WritePalmaActivityEntry();
+ void SuspendPalmaFeature();
+ void ReadPalmaPlayLog();
+ void ResetPalmaPlayLog();
+ void SetIsPalmaPairedConnectable();
+ void CancelWritePalmaWaveEntry();
+ void EnablePalmaBoostMode();
+ void GetPalmaBluetoothAddress();
+ void SetDisallowedPalmaConnection();
+
+ bool is_connectable{};
+ s32 database_id_version{};
+ PalmaOperationInfo operation{};
+ PalmaFrModeType fr_mode{};
+ PalmaConnectionHandle active_handle{};
+
+ Core::HID::EmulatedController* controller;
+
+ Kernel::KEvent* operation_complete_event;
+ KernelHelpers::ServiceContext& service_context;
+};
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index b7d7a5756..df9ee0c3f 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
@@ -10,15 +9,18 @@
namespace Service::HID {
-Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+Controller_Stubbed::Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
+ : ControllerBase{hid_core_} {
+ raw_shared_memory = raw_shared_memory_;
+}
+
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}
void Controller_Stubbed::OnRelease() {}
-void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {
+void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!smart_update) {
return;
}
@@ -29,7 +31,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
header.entry_count = 0;
header.last_entry_index = 0;
- std::memcpy(data + common_offset, &header, sizeof(CommonHeader));
+ std::memcpy(raw_shared_memory + common_offset, &header, sizeof(CommonHeader));
}
void Controller_Stubbed::SetCommonHeaderOffset(std::size_t off) {
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 0044a4efa..1483a968e 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,7 +9,7 @@
namespace Service::HID {
class Controller_Stubbed final : public ControllerBase {
public:
- explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_);
+ explicit Controller_Stubbed(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Stubbed() override;
// Called when the controller is initialized
@@ -20,19 +19,20 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
void SetCommonHeaderOffset(std::size_t off);
private:
struct CommonHeader {
- s64 timestamp;
- s64 total_entry_count;
- s64 last_entry_index;
- s64 entry_count;
+ s64 timestamp{};
+ s64 total_entry_count{};
+ s64 last_entry_index{};
+ s64 entry_count{};
};
static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
+ u8* raw_shared_memory = nullptr;
bool smart_update{};
std::size_t common_offset{};
};
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 48978e5c6..1da8d3eb0 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -1,11 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
#include "common/common_types.h"
-#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -17,8 +15,13 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
-Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_)
+Controller_Touchscreen::Controller_Touchscreen(Core::HID::HIDCore& hid_core_,
+ u8* raw_shared_memory_)
: ControllerBase{hid_core_} {
+ static_assert(SHARED_MEMORY_OFFSET + sizeof(TouchSharedMemory) < shared_memory_size,
+ "TouchSharedMemory is bigger than the shared memory");
+ shared_memory = std::construct_at(
+ reinterpret_cast<TouchSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
console = hid_core.GetEmulatedConsole();
}
@@ -28,14 +31,12 @@ void Controller_Touchscreen::OnInit() {}
void Controller_Touchscreen::OnRelease() {}
-void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {
- touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
+void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
+ shared_memory->touch_screen_lifo.timestamp = core_timing.GetCPUTicks();
if (!IsControllerActivated()) {
- touch_screen_lifo.buffer_count = 0;
- touch_screen_lifo.buffer_tail = 0;
- std::memcpy(data, &touch_screen_lifo, sizeof(touch_screen_lifo));
+ shared_memory->touch_screen_lifo.buffer_count = 0;
+ shared_memory->touch_screen_lifo.buffer_tail = 0;
return;
}
@@ -43,7 +44,6 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
for (std::size_t id = 0; id < MAX_FINGERS; id++) {
const auto& current_touch = touch_status[id];
auto& finger = fingers[id];
- finger.position = current_touch.position;
finger.id = current_touch.id;
if (finger.attribute.start_touch) {
@@ -60,13 +60,18 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
if (!finger.pressed && current_touch.pressed) {
finger.attribute.start_touch.Assign(1);
finger.pressed = true;
+ finger.position = current_touch.position;
continue;
}
if (finger.pressed && !current_touch.pressed) {
finger.attribute.raw = 0;
finger.attribute.end_touch.Assign(1);
+ continue;
}
+
+ // Only update position if touch is not on a special frame
+ finger.position = current_touch.position;
}
std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers;
@@ -76,7 +81,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter));
const u64 tick = core_timing.GetCPUTicks();
- const auto& last_entry = touch_screen_lifo.ReadCurrentEntry().state;
+ const auto& last_entry = shared_memory->touch_screen_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
next_state.entry_count = static_cast<s32>(active_fingers_count);
@@ -108,8 +113,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
}
}
- touch_screen_lifo.WriteNextEntry(next_state);
- std::memcpy(data + SHARED_MEMORY_OFFSET, &touch_screen_lifo, sizeof(touch_screen_lifo));
+ shared_memory->touch_screen_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 708dde4f0..e57a3a80e 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -1,14 +1,10 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/point.h"
-#include "common/swap.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
@@ -29,14 +25,14 @@ public:
// This is nn::hid::TouchScreenConfigurationForNx
struct TouchScreenConfigurationForNx {
- TouchScreenModeForNx mode;
+ TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting};
INSERT_PADDING_BYTES_NOINIT(0x7);
INSERT_PADDING_BYTES_NOINIT(0xF); // Reserved
};
static_assert(sizeof(TouchScreenConfigurationForNx) == 0x17,
"TouchScreenConfigurationForNx is an invalid size");
- explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_);
+ explicit Controller_Touchscreen(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_Touchscreen() override;
// Called when the controller is initialized
@@ -46,26 +42,32 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
static constexpr std::size_t MAX_FINGERS = 16;
// This is nn::hid::TouchScreenState
struct TouchScreenState {
- s64 sampling_number;
- s32 entry_count;
+ s64 sampling_number{};
+ s32 entry_count{};
INSERT_PADDING_BYTES(4); // Reserved
- std::array<Core::HID::TouchState, MAX_FINGERS> states;
+ std::array<Core::HID::TouchState, MAX_FINGERS> states{};
};
static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size");
- // This is nn::hid::detail::TouchScreenLifo
- Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
- static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
+ struct TouchSharedMemory {
+ // This is nn::hid::detail::TouchScreenLifo
+ Lifo<TouchScreenState, hid_entry_count> touch_screen_lifo{};
+ static_assert(sizeof(touch_screen_lifo) == 0x2C38, "touch_screen_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0xF2);
+ };
+ static_assert(sizeof(TouchSharedMemory) == 0x3000, "TouchSharedMemory is an invalid size");
+
TouchScreenState next_state{};
+ TouchSharedMemory* shared_memory = nullptr;
+ Core::HID::EmulatedConsole* console = nullptr;
- std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers;
- Core::HID::EmulatedConsole* console;
+ std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{};
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index e4da16466..62119e2c5 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/common_types.h"
@@ -11,28 +10,31 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
-Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}
+Controller_XPad::Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_)
+ : ControllerBase{hid_core_} {
+ static_assert(SHARED_MEMORY_OFFSET + sizeof(XpadSharedMemory) < shared_memory_size,
+ "XpadSharedMemory is bigger than the shared memory");
+ shared_memory = std::construct_at(
+ reinterpret_cast<XpadSharedMemory*>(raw_shared_memory_ + SHARED_MEMORY_OFFSET));
+}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}
void Controller_XPad::OnRelease() {}
-void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
- std::size_t size) {
+void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
if (!IsControllerActivated()) {
- basic_xpad_lifo.buffer_count = 0;
- basic_xpad_lifo.buffer_tail = 0;
- std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
+ shared_memory->basic_xpad_lifo.buffer_count = 0;
+ shared_memory->basic_xpad_lifo.buffer_tail = 0;
return;
}
- const auto& last_entry = basic_xpad_lifo.ReadCurrentEntry().state;
+ const auto& last_entry = shared_memory->basic_xpad_lifo.ReadCurrentEntry().state;
next_state.sampling_number = last_entry.sampling_number + 1;
// TODO(ogniK): Update xpad states
- basic_xpad_lifo.WriteNextEntry(next_state);
- std::memcpy(data + SHARED_MEMORY_OFFSET, &basic_xpad_lifo, sizeof(basic_xpad_lifo));
+ shared_memory->basic_xpad_lifo.WriteNextEntry(next_state);
}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index ba8db8d9d..d01dee5fc 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -1,13 +1,10 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/bit_field.h"
-#include "common/common_funcs.h"
#include "common/common_types.h"
-#include "common/swap.h"
#include "core/hid/hid_types.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/hle/service/hid/ring_lifo.h"
@@ -15,7 +12,7 @@
namespace Service::HID {
class Controller_XPad final : public ControllerBase {
public:
- explicit Controller_XPad(Core::HID::HIDCore& hid_core_);
+ explicit Controller_XPad(Core::HID::HIDCore& hid_core_, u8* raw_shared_memory_);
~Controller_XPad() override;
// Called when the controller is initialized
@@ -25,7 +22,7 @@ public:
void OnRelease() override;
// When the controller is requesting an update for the shared memory
- void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
private:
// This is nn::hid::BasicXpadAttributeSet
@@ -93,17 +90,23 @@ private:
// This is nn::hid::detail::BasicXpadState
struct BasicXpadState {
- s64 sampling_number;
- BasicXpadAttributeSet attributes;
- BasicXpadButtonSet pad_states;
- Core::HID::AnalogStickState l_stick;
- Core::HID::AnalogStickState r_stick;
+ s64 sampling_number{};
+ BasicXpadAttributeSet attributes{};
+ BasicXpadButtonSet pad_states{};
+ Core::HID::AnalogStickState l_stick{};
+ Core::HID::AnalogStickState r_stick{};
};
static_assert(sizeof(BasicXpadState) == 0x20, "BasicXpadState is an invalid size");
- // This is nn::hid::detail::BasicXpadLifo
- Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
- static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
+ struct XpadSharedMemory {
+ // This is nn::hid::detail::BasicXpadLifo
+ Lifo<BasicXpadState, hid_entry_count> basic_xpad_lifo{};
+ static_assert(sizeof(basic_xpad_lifo) == 0x2C8, "basic_xpad_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0x4E);
+ };
+ static_assert(sizeof(XpadSharedMemory) == 0x400, "XpadSharedMemory is an invalid size");
+
BasicXpadState next_state{};
+ XpadSharedMemory* shared_memory = nullptr;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/errors.h b/src/core/hle/service/hid/errors.h
index 3583642e7..76208e9a4 100644
--- a/src/core/hle/service/hid/errors.h
+++ b/src/core/hle/service/hid/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,6 +7,24 @@
namespace Service::HID {
-constexpr ResultCode ERR_NPAD_NOT_CONNECTED{ErrorModule::HID, 710};
+constexpr Result PalmaResultSuccess{ErrorModule::HID, 0};
+constexpr Result NpadInvalidHandle{ErrorModule::HID, 100};
+constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107};
+constexpr Result VibrationInvalidStyleIndex{ErrorModule::HID, 122};
+constexpr Result VibrationInvalidNpadId{ErrorModule::HID, 123};
+constexpr Result VibrationDeviceIndexOutOfRange{ErrorModule::HID, 124};
+constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423};
+constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601};
+constexpr Result NpadIsSameType{ErrorModule::HID, 602};
+constexpr Result InvalidNpadId{ErrorModule::HID, 709};
+constexpr Result NpadNotConnected{ErrorModule::HID, 710};
+constexpr Result InvalidPalmaHandle{ErrorModule::HID, 3302};
} // namespace Service::HID
+
+namespace Service::IRS {
+
+constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
+constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index d9202ea6c..46bad7871 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include "common/common_types.h"
@@ -16,6 +15,7 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/hid/hidbus.h"
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/hid/xcd.h"
#include "core/memory.h"
@@ -27,6 +27,7 @@
#include "core/hle/service/hid/controllers/keyboard.h"
#include "core/hle/service/hid/controllers/mouse.h"
#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/controllers/palma.h"
#include "core/hle/service/hid/controllers/stubbed.h"
#include "core/hle/service/hid/controllers/touchscreen.h"
#include "core/hle/service/hid/controllers/xpad.h"
@@ -35,11 +36,10 @@ namespace Service::HID {
// Updating period for each HID device.
// Period time is obtained by measuring the number of samples in a second on HW using a homebrew
-constexpr auto pad_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 250Hz)
+// Correct pad_update_ns is 4ms this is overclocked to lower input lag
+constexpr auto pad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz)
constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz)
-// TODO: Correct update rate for motion is 5ms. Check why some games don't behave at that speed
-constexpr auto motion_update_ns = std::chrono::nanoseconds{10 * 1000 * 1000}; // (10ms, 100Hz)
-constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
+constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz)
IAppletResource::IAppletResource(Core::System& system_,
KernelHelpers::ServiceContext& service_context_)
@@ -48,20 +48,21 @@ IAppletResource::IAppletResource(Core::System& system_,
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
};
RegisterHandlers(functions);
-
- MakeController<Controller_DebugPad>(HidController::DebugPad);
- MakeController<Controller_Touchscreen>(HidController::Touchscreen);
- MakeController<Controller_Mouse>(HidController::Mouse);
- MakeController<Controller_Keyboard>(HidController::Keyboard);
- MakeController<Controller_XPad>(HidController::XPad);
- MakeController<Controller_Stubbed>(HidController::HomeButton);
- MakeController<Controller_Stubbed>(HidController::SleepButton);
- MakeController<Controller_Stubbed>(HidController::CaptureButton);
- MakeController<Controller_Stubbed>(HidController::InputDetector);
- MakeController<Controller_Stubbed>(HidController::UniquePad);
- MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad);
- MakeController<Controller_Gesture>(HidController::Gesture);
- MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor);
+ u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer();
+ MakeController<Controller_DebugPad>(HidController::DebugPad, shared_memory);
+ MakeController<Controller_Touchscreen>(HidController::Touchscreen, shared_memory);
+ MakeController<Controller_Mouse>(HidController::Mouse, shared_memory);
+ MakeController<Controller_Keyboard>(HidController::Keyboard, shared_memory);
+ MakeController<Controller_XPad>(HidController::XPad, shared_memory);
+ MakeController<Controller_Stubbed>(HidController::HomeButton, shared_memory);
+ MakeController<Controller_Stubbed>(HidController::SleepButton, shared_memory);
+ MakeController<Controller_Stubbed>(HidController::CaptureButton, shared_memory);
+ MakeController<Controller_Stubbed>(HidController::InputDetector, shared_memory);
+ MakeController<Controller_Stubbed>(HidController::UniquePad, shared_memory);
+ MakeControllerWithServiceContext<Controller_NPad>(HidController::NPad, shared_memory);
+ MakeController<Controller_Gesture>(HidController::Gesture, shared_memory);
+ MakeController<Controller_ConsoleSixAxis>(HidController::ConsoleSixAxisSensor, shared_memory);
+ MakeControllerWithServiceContext<Controller_Palma>(HidController::Palma, shared_memory);
// Homebrew doesn't try to activate some controllers, so we activate them by default
GetController<Controller_NPad>(HidController::NPad).ActivateController();
@@ -76,26 +77,34 @@ IAppletResource::IAppletResource(Core::System& system_,
// Register update callbacks
pad_update_event = Core::Timing::CreateEvent(
"HID::UpdatePadCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateControllers(user_data, ns_late);
+ return std::nullopt;
});
mouse_keyboard_update_event = Core::Timing::CreateEvent(
"HID::UpdateMouseKeyboardCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateMouseKeyboard(user_data, ns_late);
+ return std::nullopt;
});
motion_update_event = Core::Timing::CreateEvent(
"HID::UpdateMotionCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto guard = LockService();
UpdateMotion(user_data, ns_late);
+ return std::nullopt;
});
- system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
- system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event);
- system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
+ mouse_keyboard_update_event);
+ system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
+ motion_update_event);
system.HIDCore().ReloadInputDevices();
}
@@ -135,47 +144,22 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
if (controller == controllers[static_cast<size_t>(HidController::Mouse)]) {
continue;
}
- controller->OnUpdate(core_timing, system.Kernel().GetHidSharedMem().GetPointer(),
- SHARED_MEMORY_SIZE);
- }
-
- // If ns_late is higher than the update rate ignore the delay
- if (ns_late > pad_update_ns) {
- ns_late = {};
+ controller->OnUpdate(core_timing);
}
-
- core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
}
void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
- controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(
- core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
- controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(
- core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
-
- // If ns_late is higher than the update rate ignore the delay
- if (ns_late > mouse_keyboard_update_ns) {
- ns_late = {};
- }
-
- core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
+ controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
+ controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
}
void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
- controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(
- core_timing, system.Kernel().GetHidSharedMem().GetPointer(), SHARED_MEMORY_SIZE);
-
- // If ns_late is higher than the update rate ignore the delay
- if (ns_late > motion_update_ns) {
- ns_late = {};
- }
-
- core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
+ controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
}
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
@@ -247,7 +231,7 @@ Hid::Hid(Core::System& system_)
{65, nullptr, "GetJoySixAxisSensorLifoHandle"},
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
- {68, nullptr, "IsSixAxisSensorFusionEnabled"},
+ {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"},
{69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
{70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"},
{71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"},
@@ -263,12 +247,12 @@ Hid::Hid(Core::System& system_)
{81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"},
{82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
{83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"},
- {84, nullptr, "EnableSixAxisSensorUnalteredPassthrough"},
- {85, nullptr, "IsSixAxisSensorUnalteredPassthroughEnabled"},
+ {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"},
+ {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"},
{86, nullptr, "StoreSixAxisSensorCalibrationParameter"},
- {87, nullptr, "LoadSixAxisSensorCalibrationParameter"},
- {88, nullptr, "GetSixAxisSensorIcInformation"},
- {89, nullptr, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
+ {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"},
+ {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"},
+ {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"},
{91, &Hid::ActivateGesture, "ActivateGesture"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
@@ -329,40 +313,40 @@ Hid::Hid(Core::System& system_)
{406, nullptr, "GetNpadLeftRightInterfaceType"},
{407, nullptr, "GetNpadOfHighestBatteryLevel"},
{408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
- {500, nullptr, "GetPalmaConnectionHandle"},
- {501, nullptr, "InitializePalma"},
- {502, nullptr, "AcquirePalmaOperationCompleteEvent"},
- {503, nullptr, "GetPalmaOperationInfo"},
- {504, nullptr, "PlayPalmaActivity"},
- {505, nullptr, "SetPalmaFrModeType"},
- {506, nullptr, "ReadPalmaStep"},
- {507, nullptr, "EnablePalmaStep"},
- {508, nullptr, "ResetPalmaStep"},
- {509, nullptr, "ReadPalmaApplicationSection"},
- {510, nullptr, "WritePalmaApplicationSection"},
- {511, nullptr, "ReadPalmaUniqueCode"},
- {512, nullptr, "SetPalmaUniqueCodeInvalid"},
- {513, nullptr, "WritePalmaActivityEntry"},
- {514, nullptr, "WritePalmaRgbLedPatternEntry"},
- {515, nullptr, "WritePalmaWaveEntry"},
- {516, nullptr, "SetPalmaDataBaseIdentificationVersion"},
- {517, nullptr, "GetPalmaDataBaseIdentificationVersion"},
- {518, nullptr, "SuspendPalmaFeature"},
- {519, nullptr, "GetPalmaOperationResult"},
- {520, nullptr, "ReadPalmaPlayLog"},
- {521, nullptr, "ResetPalmaPlayLog"},
+ {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"},
+ {501, &Hid::InitializePalma, "InitializePalma"},
+ {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"},
+ {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"},
+ {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"},
+ {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"},
+ {506, &Hid::ReadPalmaStep, "ReadPalmaStep"},
+ {507, &Hid::EnablePalmaStep, "EnablePalmaStep"},
+ {508, &Hid::ResetPalmaStep, "ResetPalmaStep"},
+ {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"},
+ {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"},
+ {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"},
+ {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"},
+ {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"},
+ {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"},
+ {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"},
+ {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"},
+ {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"},
+ {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"},
+ {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"},
+ {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"},
+ {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"},
{522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
- {523, nullptr, "SetIsPalmaPairedConnectable"},
- {524, nullptr, "PairPalma"},
+ {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"},
+ {524, &Hid::PairPalma, "PairPalma"},
{525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
- {526, nullptr, "CancelWritePalmaWaveEntry"},
- {527, nullptr, "EnablePalmaBoostMode"},
- {528, nullptr, "GetPalmaBluetoothAddress"},
- {529, nullptr, "SetDisallowedPalmaConnection"},
+ {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"},
+ {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"},
+ {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"},
+ {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"},
{1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"},
{1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"},
{1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"},
- {1003, nullptr, "IsFirmwareUpdateNeededForNotification"},
+ {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"},
{2000, nullptr, "ActivateDigitizer"},
};
// clang-format on
@@ -527,8 +511,8 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisEnabled(parameters.sixaxis_handle, true);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -536,7 +520,7 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -550,8 +534,8 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisEnabled(parameters.sixaxis_handle, false);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -559,7 +543,33 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
+}
+
+void Hid::IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ bool is_enabled{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push(is_enabled);
}
void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
@@ -574,9 +584,9 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisFusionEnabled(parameters.sixaxis_handle,
- parameters.enable_sixaxis_sensor_fusion);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle,
+ parameters.enable_sixaxis_sensor_fusion);
LOG_DEBUG(Service_HID,
"called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
@@ -586,7 +596,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -601,8 +611,9 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, parameter1={}, "
@@ -612,7 +623,7 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -626,9 +637,11 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- const auto sixaxis_fusion_parameters =
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetSixAxisFusionParameters(parameters.sixaxis_handle);
+ Core::HID::SixAxisSensorFusionParameters fusion_parameters{};
+ const auto& controller =
+ GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -636,8 +649,8 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.PushRaw(sixaxis_fusion_parameters);
+ rb.Push(result);
+ rb.PushRaw(fusion_parameters);
}
void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
@@ -651,8 +664,15 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .ResetSixAxisFusionParameters(parameters.sixaxis_handle);
+ // Since these parameters are unknow just use what HW outputs
+ const Core::HID::SixAxisSensorFusionParameters fusion_parameters{
+ .parameter1 = 0.03f,
+ .parameter2 = 0.4f,
+ };
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result1 =
+ controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters);
+ const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -660,7 +680,11 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ if (result1.IsError()) {
+ rb.Push(result1);
+ return;
+ }
+ rb.Push(result2);
}
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -669,8 +693,8 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
@@ -679,7 +703,7 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
drift_mode, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -693,15 +717,18 @@ void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
+ auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
+
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetGyroscopeZeroDriftMode(parameters.sixaxis_handle));
+ rb.Push(result);
+ rb.PushEnum(drift_mode);
}
void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
@@ -714,10 +741,10 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
- const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
+ const auto drift_mode{Controller_NPad::GyroscopeZeroDriftMode::Standard};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode);
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -725,7 +752,7 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
@@ -739,6 +766,10 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
+ bool is_at_rest{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest);
+
LOG_DEBUG(Service_HID,
"called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
@@ -746,8 +777,7 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .IsSixAxisSensorAtRest(parameters.sixaxis_handle));
+ rb.Push(is_at_rest);
}
void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -761,6 +791,11 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
const auto parameters{rp.PopRaw<Parameters>()};
+ bool is_firmware_available{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle,
+ is_firmware_available);
+
LOG_WARNING(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
@@ -769,7 +804,145 @@ void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& c
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(false);
+ rb.Push(is_firmware_available);
+}
+
+void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ bool enabled;
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
+ parameters.sixaxis_handle, parameters.enabled);
+
+ LOG_DEBUG(Service_HID,
+ "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
+ "applet_resource_user_id={}",
+ parameters.enabled, parameters.sixaxis_handle.npad_type,
+ parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
+ parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ bool is_unaltered_sisxaxis_enabled{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
+ parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
+
+ LOG_DEBUG(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push(is_unaltered_sisxaxis_enabled);
+}
+
+void Hid::LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ Core::HID::SixAxisSensorCalibrationParameter calibration{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration);
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ if (result.IsSuccess()) {
+ ctx.WriteBuffer(calibration);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ Core::HID::SixAxisSensorIcInformation ic_information{};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information);
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ if (result.IsSuccess()) {
+ ctx.WriteBuffer(ic_information);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::SixAxisSensorHandle sixaxis_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle);
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
}
void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
@@ -878,6 +1051,10 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
+ // Games expect this event to be signaled after calling this function
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SignalStyleSetChangedEvent(parameters.npad_id);
+
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
@@ -895,8 +1072,8 @@ void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .DisconnectNpad(parameters.npad_id);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ controller.DisconnectNpad(parameters.npad_id);
LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
@@ -909,13 +1086,15 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
+ Core::HID::LedPattern pattern{0, 0, 0, 0};
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.GetLedPattern(npad_id, pattern);
+
LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetLedPattern(npad_id)
- .raw);
+ rb.Push(result);
+ rb.Push(pattern.raw);
}
void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
@@ -975,9 +1154,9 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
- Controller_NPad::NpadJoyAssignmentMode::Single);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ controller.SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
@@ -998,9 +1177,9 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
- Controller_NPad::NpadJoyAssignmentMode::Single);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ controller.SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
parameters.npad_id, parameters.applet_resource_user_id,
@@ -1021,8 +1200,8 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ controller.SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
parameters.applet_resource_user_id);
@@ -1037,14 +1216,14 @@ void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
npad_id_1, npad_id_2, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
@@ -1104,19 +1283,14 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
const auto npad_id_2{rp.PopEnum<Core::HID::NpadIdType>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SwapNpadAssignment(npad_id_1, npad_id_2);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2);
LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
npad_id_1, npad_id_2, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- if (res) {
- rb.Push(ResultSuccess);
- } else {
- LOG_ERROR(Service_HID, "Npads are not connected!");
- rb.Push(ERR_NPAD_NOT_CONNECTED);
- }
+ rb.Push(result);
}
void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
@@ -1130,13 +1304,17 @@ void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext
const auto parameters{rp.PopRaw<Parameters>()};
+ bool is_enabled = false;
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result =
+ controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled);
+
LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
parameters.npad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
+ rb.Push(result);
+ rb.Push(is_enabled);
}
void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
@@ -1151,9 +1329,9 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetUnintendedHomeButtonInputProtectionEnabled(
- parameters.unintended_home_button_input_protection, parameters.npad_id);
+ auto& controller = GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
+ const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled(
+ parameters.unintended_home_button_input_protection, parameters.npad_id);
LOG_WARNING(Service_HID,
"(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
@@ -1162,7 +1340,7 @@ void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& c
parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void Hid::SetNpadAnalogStickUseCenterClamp(Kernel::HLERequestContext& ctx) {
@@ -1222,8 +1400,11 @@ void Hid::ClearNpadCaptureButtonAssignment(Kernel::HLERequestContext& ctx) {
void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
+ const auto& controller =
+ GetAppletResource()->GetController<Controller_NPad>(HidController::NPad);
Core::HID::VibrationDeviceInfo vibration_device_info;
+ bool check_device_index = false;
switch (vibration_device_handle.npad_type) {
case Core::HID::NpadStyleIndex::ProController:
@@ -1231,34 +1412,46 @@ void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
case Core::HID::NpadStyleIndex::JoyconDual:
case Core::HID::NpadStyleIndex::JoyconLeft:
case Core::HID::NpadStyleIndex::JoyconRight:
- default:
vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator;
+ check_device_index = true;
break;
case Core::HID::NpadStyleIndex::GameCube:
vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm;
break;
- case Core::HID::NpadStyleIndex::Pokeball:
+ case Core::HID::NpadStyleIndex::N64:
+ vibration_device_info.type = Core::HID::VibrationDeviceType::N64;
+ break;
+ default:
vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown;
break;
}
- switch (vibration_device_handle.device_index) {
- case Core::HID::DeviceIndex::Left:
- vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
- break;
- case Core::HID::DeviceIndex::Right:
- vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
- break;
- case Core::HID::DeviceIndex::None:
- default:
- UNREACHABLE_MSG("DeviceIndex should never be None!");
- vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
- break;
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::None;
+ if (check_device_index) {
+ switch (vibration_device_handle.device_index) {
+ case Core::HID::DeviceIndex::Left:
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::Left;
+ break;
+ case Core::HID::DeviceIndex::Right:
+ vibration_device_info.position = Core::HID::VibrationDevicePosition::Right;
+ break;
+ case Core::HID::DeviceIndex::None:
+ default:
+ ASSERT_MSG(false, "DeviceIndex should never be None!");
+ break;
+ }
}
LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
vibration_device_info.type, vibration_device_info.position);
+ const auto result = controller.IsDeviceHandleValid(vibration_device_handle);
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.PushRaw(vibration_device_info);
@@ -1324,6 +1517,8 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto can_vibrate{rp.Pop<bool>()};
+ // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value
+ // by converting it to a bool
Settings::values.vibration_enabled.SetValue(can_vibrate);
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
@@ -1335,9 +1530,12 @@ void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
+ // nnSDK checks if a float is greater than zero. We return the bool we stored earlier
+ const auto is_enabled = Settings::values.vibration_enabled.GetValue();
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.Push(Settings::values.vibration_enabled.GetValue());
+ rb.Push(is_enabled);
}
void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
@@ -1683,14 +1881,361 @@ void Hid::IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx) {
rb.Push(false);
}
+void Hid::GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::NpadIdType npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.applet_resource_user_id);
+
+ Controller_Palma::PalmaConnectionHandle handle;
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+ const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(result);
+ rb.PushRaw(handle);
+}
+
+void Hid::InitializePalma(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+ const auto result = controller.InitializePalma(connection_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle));
+}
+
+void Hid::GetPalmaOperationInfo(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ Controller_Palma::PalmaOperationType operation_type;
+ Controller_Palma::PalmaOperationData data;
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+ const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data);
+
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
+ ctx.WriteBuffer(data);
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(result);
+ rb.Push(static_cast<u64>(operation_type));
+}
+
+void Hid::PlayPalmaActivity(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+ const auto palma_activity{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}",
+ connection_handle.npad_id, palma_activity);
+
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+ const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::SetPalmaFrModeType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+ const auto fr_mode{rp.PopEnum<Controller_Palma::PalmaFrModeType>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}",
+ connection_handle.npad_id, fr_mode);
+
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+ const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::ReadPalmaStep(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+ const auto result = controller.ReadPalmaStep(connection_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::EnablePalmaStep(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ bool is_enabled;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ Controller_Palma::PalmaConnectionHandle connection_handle;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}",
+ parameters.connection_handle.npad_id, parameters.is_enabled);
+
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+ const auto result =
+ controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::ResetPalmaStep(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ auto& controller = GetAppletResource()->GetController<Controller_Palma>(HidController::Palma);
+ const auto result = controller.ResetPalmaStep(connection_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::WritePalmaApplicationSection(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .ReadPalmaUniqueCode(connection_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .SetPalmaUniqueCodeInvalid(connection_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::WritePalmaActivityEntry(Kernel::HLERequestContext& ctx) {
+ LOG_CRITICAL(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+ const auto unknown{rp.Pop<u64>()};
+
+ const auto buffer = ctx.ReadBuffer();
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}",
+ connection_handle.npad_id, unknown);
+
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .WritePalmaRgbLedPatternEntry(connection_handle, unknown);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::WritePalmaWaveEntry(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+ const auto wave_set{rp.PopEnum<Controller_Palma::PalmaWaveSet>()};
+ const auto unknown{rp.Pop<u64>()};
+ const auto t_mem_size{rp.Pop<u64>()};
+ const auto t_mem_handle{ctx.GetCopyHandle(0)};
+ const auto size{rp.Pop<u64>()};
+
+ ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes");
+
+ auto t_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+
+ if (t_mem.IsNull()) {
+ LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size");
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, connection_handle={}, wave_set={}, unkown={}, "
+ "t_mem_handle=0x{:08X}, t_mem_size={}, size={}",
+ connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size);
+
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .WritePalmaWaveEntry(connection_handle, wave_set,
+ system.Memory().GetPointer(t_mem->GetSourceAddress()), t_mem_size);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ s32 database_id_version;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ Controller_Palma::PalmaConnectionHandle connection_handle;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}",
+ parameters.connection_handle.npad_id, parameters.database_id_version);
+
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle,
+ parameters.database_id_version);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .GetPalmaDataBaseIdentificationVersion(connection_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::SuspendPalmaFeature(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::GetPalmaOperationResult(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ const auto result = applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .GetPalmaOperationResult(connection_handle);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void Hid::ReadPalmaPlayLog(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::ResetPalmaPlayLog(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void Hid::SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto is_palma_all_connectable{rp.Pop<bool>()};
+ struct Parameters {
+ bool is_palma_all_connectable;
+ INSERT_PADDING_BYTES_NOINIT(7);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
LOG_WARNING(Service_HID,
- "(STUBBED) called, applet_resource_user_id={}, is_palma_all_connectable={}",
- applet_resource_user_id, is_palma_all_connectable);
+ "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}",
+ parameters.is_palma_all_connectable, parameters.applet_resource_user_id);
+
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::PairPalma(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto connection_handle{rp.PopRaw<Controller_Palma::PalmaConnectionHandle>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id);
+
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .PairPalma(connection_handle);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1702,6 +2247,37 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode);
+ applet_resource->GetController<Controller_Palma>(HidController::Palma)
+ .SetPalmaBoostMode(palma_boost_mode);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::EnablePalmaBoostMode(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void Hid::SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
@@ -1744,6 +2320,25 @@ void Hid::SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
+void Hid::IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ s32 unknown;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}",
+ parameters.unknown, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(false);
+}
+
class HidDbg final : public ServiceFramework<HidDbg> {
public:
explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} {
@@ -1932,12 +2527,18 @@ public:
{324, nullptr, "GetUniquePadButtonSet"},
{325, nullptr, "GetUniquePadColor"},
{326, nullptr, "GetUniquePadAppletDetailedUiType"},
+ {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
+ {328, nullptr, "AttachAbstractedPadToNpad"},
+ {329, nullptr, "DetachAbstractedPadAll"},
+ {330, nullptr, "CheckAbstractedPadConnection"},
{500, nullptr, "SetAppletResourceUserId"},
{501, nullptr, "RegisterAppletResourceUserId"},
{502, nullptr, "UnregisterAppletResourceUserId"},
{503, nullptr, "EnableAppletToGetInput"},
{504, nullptr, "SetAruidValidForVibration"},
{505, nullptr, "EnableAppletToGetSixAxisSensor"},
+ {506, nullptr, "EnableAppletToGetPadInput"},
+ {507, nullptr, "EnableAppletToGetTouchScreen"},
{510, nullptr, "SetVibrationMasterVolume"},
{511, nullptr, "GetVibrationMasterVolume"},
{512, nullptr, "BeginPermitVibrationSession"},
@@ -2124,32 +2725,6 @@ public:
}
};
-class HidBus final : public ServiceFramework<HidBus> {
-public:
- explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {1, nullptr, "GetBusHandle"},
- {2, nullptr, "IsExternalDeviceConnected"},
- {3, nullptr, "Initialize"},
- {4, nullptr, "Finalize"},
- {5, nullptr, "EnableExternalDevice"},
- {6, nullptr, "GetExternalDeviceId"},
- {7, nullptr, "SendCommandAsync"},
- {8, nullptr, "GetSendCommandAsynceResult"},
- {9, nullptr, "SetEventForSendCommandAsycResult"},
- {10, nullptr, "GetSharedMemoryHandle"},
- {11, nullptr, "EnableJoyPollingReceiveMode"},
- {12, nullptr, "DisableJoyPollingReceiveMode"},
- {13, nullptr, "GetPollingData"},
- {14, nullptr, "SetStatusManagerType"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
std::make_shared<Hid>(system)->InstallAsService(service_manager);
std::make_shared<HidBus>(system)->InstallAsService(service_manager);
@@ -2157,8 +2732,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
- std::make_shared<IRS>(system)->InstallAsService(service_manager);
- std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager);
+ std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
+ std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
}
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index c281081a7..340d26fdc 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -34,6 +33,7 @@ enum class HidController : std::size_t {
NPad,
Gesture,
ConsoleSixAxisSensor,
+ Palma,
MaxControllers,
};
@@ -59,13 +59,14 @@ public:
private:
template <typename T>
- void MakeController(HidController controller) {
- controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system.HIDCore());
+ void MakeController(HidController controller, u8* shared_memory) {
+ controllers[static_cast<std::size_t>(controller)] =
+ std::make_unique<T>(system.HIDCore(), shared_memory);
}
template <typename T>
- void MakeControllerWithServiceContext(HidController controller) {
+ void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) {
controllers[static_cast<std::size_t>(controller)] =
- std::make_unique<T>(system.HIDCore(), service_context);
+ std::make_unique<T>(system.HIDCore(), shared_memory, service_context);
}
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -103,6 +104,7 @@ private:
void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void IsSixAxisSensorFusionEnabled(Kernel::HLERequestContext& ctx);
void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
void SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
void GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx);
@@ -112,6 +114,11 @@ private:
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
void IsFirmwareUpdateAvailableForSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx);
+ void IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext& ctx);
+ void LoadSixAxisSensorCalibrationParameter(Kernel::HLERequestContext& ctx);
+ void GetSixAxisSensorIcInformation(Kernel::HLERequestContext& ctx);
+ void ResetIsSixAxisSensorDeviceNewlyAssigned(Kernel::HLERequestContext& ctx);
void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
@@ -160,11 +167,40 @@ private:
void FinalizeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
void ResetSevenSixAxisSensorTimestamp(Kernel::HLERequestContext& ctx);
void IsUsbFullKeyControllerEnabled(Kernel::HLERequestContext& ctx);
+ void GetPalmaConnectionHandle(Kernel::HLERequestContext& ctx);
+ void InitializePalma(Kernel::HLERequestContext& ctx);
+ void AcquirePalmaOperationCompleteEvent(Kernel::HLERequestContext& ctx);
+ void GetPalmaOperationInfo(Kernel::HLERequestContext& ctx);
+ void PlayPalmaActivity(Kernel::HLERequestContext& ctx);
+ void SetPalmaFrModeType(Kernel::HLERequestContext& ctx);
+ void ReadPalmaStep(Kernel::HLERequestContext& ctx);
+ void EnablePalmaStep(Kernel::HLERequestContext& ctx);
+ void ResetPalmaStep(Kernel::HLERequestContext& ctx);
+ void ReadPalmaApplicationSection(Kernel::HLERequestContext& ctx);
+ void WritePalmaApplicationSection(Kernel::HLERequestContext& ctx);
+ void ReadPalmaUniqueCode(Kernel::HLERequestContext& ctx);
+ void SetPalmaUniqueCodeInvalid(Kernel::HLERequestContext& ctx);
+ void WritePalmaActivityEntry(Kernel::HLERequestContext& ctx);
+ void WritePalmaRgbLedPatternEntry(Kernel::HLERequestContext& ctx);
+ void WritePalmaWaveEntry(Kernel::HLERequestContext& ctx);
+ void SetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx);
+ void GetPalmaDataBaseIdentificationVersion(Kernel::HLERequestContext& ctx);
+ void SuspendPalmaFeature(Kernel::HLERequestContext& ctx);
+ void GetPalmaOperationResult(Kernel::HLERequestContext& ctx);
+ void ReadPalmaPlayLog(Kernel::HLERequestContext& ctx);
+ void ResetPalmaPlayLog(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
+ void SetIsPalmaPairedConnectable(Kernel::HLERequestContext& ctx);
+ void PairPalma(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
+ void CancelWritePalmaWaveEntry(Kernel::HLERequestContext& ctx);
+ void EnablePalmaBoostMode(Kernel::HLERequestContext& ctx);
+ void GetPalmaBluetoothAddress(Kernel::HLERequestContext& ctx);
+ void SetDisallowedPalmaConnection(Kernel::HLERequestContext& ctx);
void SetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
void GetNpadCommunicationMode(Kernel::HLERequestContext& ctx);
void SetTouchScreenConfiguration(Kernel::HLERequestContext& ctx);
+ void IsFirmwareUpdateNeededForNotification(Kernel::HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
new file mode 100644
index 000000000..e5e50845f
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -0,0 +1,524 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "common/settings.h"
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hid/hid_types.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/service/hid/hidbus.h"
+#include "core/hle/service/hid/hidbus/ringcon.h"
+#include "core/hle/service/hid/hidbus/starlink.h"
+#include "core/hle/service/hid/hidbus/stubbed.h"
+#include "core/hle/service/service.h"
+#include "core/memory.h"
+
+namespace Service::HID {
+// (15ms, 66Hz)
+constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
+
+HidBus::HidBus(Core::System& system_)
+ : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
+
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, &HidBus::GetBusHandle, "GetBusHandle"},
+ {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
+ {3, &HidBus::Initialize, "Initialize"},
+ {4, &HidBus::Finalize, "Finalize"},
+ {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
+ {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
+ {7, &HidBus::SendCommandAsync, "SendCommandAsync"},
+ {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
+ {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
+ {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
+ {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
+ {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
+ {13, nullptr, "GetPollingData"},
+ {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ // Register update callbacks
+ hidbus_update_event = Core::Timing::CreateEvent(
+ "Hidbus::UpdateCallback",
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
+ const auto guard = LockService();
+ UpdateHidbus(user_data, ns_late);
+ return std::nullopt;
+ });
+
+ system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
+ hidbus_update_event);
+}
+
+HidBus::~HidBus() {
+ system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
+}
+
+void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ if (is_hidbus_enabled) {
+ for (std::size_t i = 0; i < devices.size(); ++i) {
+ if (!devices[i].is_device_initializated) {
+ continue;
+ }
+ auto& device = devices[i].device;
+ device->OnUpdate();
+ auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
+ cur_entry.is_polling_mode = device->IsPollingMode();
+ cur_entry.polling_mode = device->GetPollingMode();
+ cur_entry.is_enabled = device->IsEnabled();
+
+ u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
+ std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
+ sizeof(HidbusStatusManagerEntry));
+ }
+ }
+}
+
+std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
+ for (std::size_t i = 0; i < devices.size(); ++i) {
+ const auto& device_handle = devices[i].handle;
+ if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
+ handle.internal_index == device_handle.internal_index &&
+ handle.player_number == device_handle.player_number &&
+ handle.bus_type == device_handle.bus_type &&
+ handle.is_valid == device_handle.is_valid) {
+ return i;
+ }
+ }
+ return std::nullopt;
+}
+
+void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::HID::NpadIdType npad_id;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ BusType bus_type;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
+
+ bool is_handle_found = 0;
+ std::size_t handle_index = 0;
+
+ for (std::size_t i = 0; i < devices.size(); i++) {
+ const auto& handle = devices[i].handle;
+ if (!handle.is_valid) {
+ continue;
+ }
+ if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
+ handle.bus_type == parameters.bus_type) {
+ is_handle_found = true;
+ handle_index = i;
+ break;
+ }
+ }
+
+ // Handle not found. Create a new one
+ if (!is_handle_found) {
+ for (std::size_t i = 0; i < devices.size(); i++) {
+ if (devices[i].handle.is_valid) {
+ continue;
+ }
+ devices[i].handle = {
+ .abstracted_pad_id = static_cast<u8>(i),
+ .internal_index = static_cast<u8>(i),
+ .player_number = static_cast<u8>(parameters.npad_id),
+ .bus_type = parameters.bus_type,
+ .is_valid = true,
+ };
+ handle_index = i;
+ break;
+ }
+ }
+
+ struct OutData {
+ bool is_valid;
+ INSERT_PADDING_BYTES(7);
+ BusHandle handle;
+ };
+ static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
+
+ const OutData out_data{
+ .is_valid = true,
+ .handle = devices[handle_index].handle,
+ };
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(out_data);
+}
+
+void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_INFO(Service_HID,
+ "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto& device = devices[device_index.value()].device;
+ const bool is_attached = device->IsDeviceActivated();
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(is_attached);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={} bus_type={} internal_index={} "
+ "player_number={} is_valid={}, applet_resource_user_id={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
+
+ is_hidbus_enabled = true;
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto entry_index = devices[device_index.value()].handle.internal_index;
+ auto& cur_entry = hidbus_status.entries[entry_index];
+
+ if (bus_handle_.internal_index == 0 && Settings::values.enable_ring_controller) {
+ MakeDevice<RingController>(bus_handle_);
+ devices[device_index.value()].is_device_initializated = true;
+ devices[device_index.value()].device->ActivateDevice();
+ cur_entry.is_in_focus = true;
+ cur_entry.is_connected = true;
+ cur_entry.is_connected_result = ResultSuccess;
+ cur_entry.is_enabled = false;
+ cur_entry.is_polling_mode = false;
+ } else {
+ MakeDevice<HidbusStubbed>(bus_handle_);
+ devices[device_index.value()].is_device_initializated = true;
+ cur_entry.is_in_focus = true;
+ cur_entry.is_connected = false;
+ cur_entry.is_connected_result = ResultSuccess;
+ cur_entry.is_enabled = false;
+ cur_entry.is_polling_mode = false;
+ }
+
+ std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
+ sizeof(hidbus_status));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::Finalize(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}, applet_resource_user_id={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto entry_index = devices[device_index.value()].handle.internal_index;
+ auto& cur_entry = hidbus_status.entries[entry_index];
+ auto& device = devices[device_index.value()].device;
+ devices[device_index.value()].is_device_initializated = false;
+ device->DeactivateDevice();
+
+ cur_entry.is_in_focus = true;
+ cur_entry.is_connected = false;
+ cur_entry.is_connected_result = ResultSuccess;
+ cur_entry.is_enabled = false;
+ cur_entry.is_polling_mode = false;
+ std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
+ sizeof(hidbus_status));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ bool enable;
+ INSERT_PADDING_BYTES_NOINIT(7);
+ BusHandle bus_handle;
+ u64 inval;
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_INFO(Service_HID,
+ "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
+ parameters.enable, parameters.bus_handle.abstracted_pad_id,
+ parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
+ parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
+ parameters.applet_resource_user_id);
+
+ const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
+
+ if (device_index) {
+ auto& device = devices[device_index.value()].device;
+ device->Enable(parameters.enable);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto& device = devices[device_index.value()].device;
+ u32 device_id = device->GetDeviceId();
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(device_id);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto data = ctx.ReadBuffer();
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
+ "player_number={}, is_valid={}",
+ data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
+ bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ auto& device = devices[device_index.value()].device;
+ device->SetCommand(data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+};
+
+void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto& device = devices[device_index.value()].device;
+ const std::vector<u8> data = device->GetReply();
+ const u64 data_size = ctx.WriteBuffer(data);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(data_size);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+};
+
+void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ const auto& device = devices[device_index.value()].device;
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(device->GetSendCommandAsycEvent());
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+};
+
+void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
+}
+
+void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto t_mem_size{rp.Pop<u32>()};
+ const auto t_mem_handle{ctx.GetCopyHandle(0)};
+ const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
+
+ auto t_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+
+ if (t_mem.IsNull()) {
+ LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
+
+ LOG_INFO(Service_HID,
+ "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
+ "internal_index={}, player_number={}, is_valid={}",
+ t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
+ bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ auto& device = devices[device_index.value()].device;
+ device->SetPollingMode(polling_mode_);
+ device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress()));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto bus_handle_{rp.PopRaw<BusHandle>()};
+
+ LOG_INFO(Service_HID,
+ "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
+ "is_valid={}",
+ bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
+ bus_handle_.player_number, bus_handle_.is_valid);
+
+ const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
+
+ if (device_index) {
+ auto& device = devices[device_index.value()].device;
+ device->DisablePollingMode();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Invalid handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+}
+
+void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto manager_type{rp.PopEnum<StatusManagerType>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+};
+} // 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..8c687f678
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.h
@@ -0,0 +1,130 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <functional>
+
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/service.h"
+
+namespace Core::Timing {
+struct EventType;
+} // namespace Core::Timing
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace Service::HID {
+
+class HidBus final : public ServiceFramework<HidBus> {
+public:
+ explicit HidBus(Core::System& system_);
+ ~HidBus() override;
+
+private:
+ static const std::size_t max_number_of_handles = 0x13;
+
+ enum class HidBusDeviceId : std::size_t {
+ RingController = 0x20,
+ FamicomRight = 0x21,
+ Starlink = 0x28,
+ };
+
+ // This is nn::hidbus::detail::StatusManagerType
+ enum class StatusManagerType : u32 {
+ None,
+ Type16,
+ Type32,
+ };
+
+ // This is nn::hidbus::BusType
+ enum class BusType : u8 {
+ LeftJoyRail,
+ RightJoyRail,
+ InternalBus, // Lark microphone
+
+ MaxBusType,
+ };
+
+ // This is nn::hidbus::BusHandle
+ struct BusHandle {
+ u32 abstracted_pad_id;
+ u8 internal_index;
+ u8 player_number;
+ BusType bus_type;
+ bool is_valid;
+ };
+ static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
+
+ // This is nn::hidbus::JoyPollingReceivedData
+ struct JoyPollingReceivedData {
+ std::array<u8, 0x30> data;
+ u64 out_size;
+ u64 sampling_number;
+ };
+ static_assert(sizeof(JoyPollingReceivedData) == 0x40,
+ "JoyPollingReceivedData is an invalid size");
+
+ struct HidbusStatusManagerEntry {
+ u8 is_connected{};
+ INSERT_PADDING_BYTES(0x3);
+ Result is_connected_result{0};
+ u8 is_enabled{};
+ u8 is_in_focus{};
+ u8 is_polling_mode{};
+ u8 reserved{};
+ JoyPollingMode polling_mode{};
+ INSERT_PADDING_BYTES(0x70); // Unknown
+ };
+ static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
+ "HidbusStatusManagerEntry is an invalid size");
+
+ struct HidbusStatusManager {
+ std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
+ INSERT_PADDING_BYTES(0x680); // Unused
+ };
+ static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
+
+ struct HidbusDevice {
+ bool is_device_initializated{};
+ BusHandle handle{};
+ std::unique_ptr<HidbusBase> device{nullptr};
+ };
+
+ void GetBusHandle(Kernel::HLERequestContext& ctx);
+ void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx);
+ void Initialize(Kernel::HLERequestContext& ctx);
+ void Finalize(Kernel::HLERequestContext& ctx);
+ void EnableExternalDevice(Kernel::HLERequestContext& ctx);
+ void GetExternalDeviceId(Kernel::HLERequestContext& ctx);
+ void SendCommandAsync(Kernel::HLERequestContext& ctx);
+ void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx);
+ void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx);
+ void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
+ void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
+ void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
+ void SetStatusManagerType(Kernel::HLERequestContext& ctx);
+
+ void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
+
+ template <typename T>
+ void MakeDevice(BusHandle handle) {
+ const auto device_index = GetDeviceIndexFromHandle(handle);
+ if (device_index) {
+ devices[device_index.value()].device =
+ std::make_unique<T>(system.HIDCore(), service_context);
+ }
+ }
+
+ bool is_hidbus_enabled{false};
+ HidbusStatusManager hidbus_status{};
+ std::array<HidbusDevice, max_number_of_handles> devices{};
+ std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
+ KernelHelpers::ServiceContext service_context;
+};
+
+} // 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..b569b3c20
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
@@ -0,0 +1,71 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hid/hid_core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+#include "core/hle/service/kernel_helpers.h"
+
+namespace Service::HID {
+
+HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
+ : service_context(service_context_) {
+ send_command_async_event = service_context.CreateEvent("hidbus:SendCommandAsyncEvent");
+}
+HidbusBase::~HidbusBase() = default;
+
+void HidbusBase::ActivateDevice() {
+ if (is_activated) {
+ return;
+ }
+ is_activated = true;
+ OnInit();
+}
+
+void HidbusBase::DeactivateDevice() {
+ if (is_activated) {
+ OnRelease();
+ }
+ is_activated = false;
+}
+
+bool HidbusBase::IsDeviceActivated() const {
+ return is_activated;
+}
+
+void HidbusBase::Enable(bool enable) {
+ device_enabled = enable;
+}
+
+bool HidbusBase::IsEnabled() const {
+ return device_enabled;
+}
+
+bool HidbusBase::IsPollingMode() const {
+ return polling_mode_enabled;
+}
+
+JoyPollingMode HidbusBase::GetPollingMode() const {
+ return polling_mode;
+}
+
+void HidbusBase::SetPollingMode(JoyPollingMode mode) {
+ polling_mode = mode;
+ polling_mode_enabled = true;
+}
+
+void HidbusBase::DisablePollingMode() {
+ polling_mode_enabled = false;
+}
+
+void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
+ is_transfer_memory_set = true;
+ transfer_memory = t_mem;
+}
+
+Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
+ return send_command_async_event->GetReadableEvent();
+}
+
+} // 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..d3960f506
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -0,0 +1,178 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include "common/common_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::KernelHelpers {
+class ServiceContext;
+}
+
+namespace Service::HID {
+
+// This is nn::hidbus::JoyPollingMode
+enum class JoyPollingMode : u32 {
+ SixAxisSensorDisable,
+ SixAxisSensorEnable,
+ ButtonOnly,
+};
+
+struct DataAccessorHeader {
+ Result result{ResultUnknown};
+ INSERT_PADDING_WORDS(0x1);
+ std::array<u8, 0x18> unused{};
+ u64 latest_entry{};
+ u64 total_entries{};
+};
+static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
+
+struct JoyDisableSixAxisPollingData {
+ std::array<u8, 0x26> data;
+ u8 out_size;
+ INSERT_PADDING_BYTES(0x1);
+ u64 sampling_number;
+};
+static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
+ "JoyDisableSixAxisPollingData is an invalid size");
+
+struct JoyEnableSixAxisPollingData {
+ std::array<u8, 0x8> data;
+ u8 out_size;
+ INSERT_PADDING_BYTES(0x7);
+ u64 sampling_number;
+};
+static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
+ "JoyEnableSixAxisPollingData is an invalid size");
+
+struct JoyButtonOnlyPollingData {
+ std::array<u8, 0x2c> data;
+ u8 out_size;
+ INSERT_PADDING_BYTES(0x3);
+ u64 sampling_number;
+};
+static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
+ "JoyButtonOnlyPollingData is an invalid size");
+
+struct JoyDisableSixAxisPollingEntry {
+ u64 sampling_number;
+ JoyDisableSixAxisPollingData polling_data;
+};
+static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
+ "JoyDisableSixAxisPollingEntry is an invalid size");
+
+struct JoyEnableSixAxisPollingEntry {
+ u64 sampling_number;
+ JoyEnableSixAxisPollingData polling_data;
+};
+static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
+ "JoyEnableSixAxisPollingEntry is an invalid size");
+
+struct JoyButtonOnlyPollingEntry {
+ u64 sampling_number;
+ JoyButtonOnlyPollingData polling_data;
+};
+static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
+ "JoyButtonOnlyPollingEntry is an invalid size");
+
+struct JoyDisableSixAxisDataAccessor {
+ DataAccessorHeader header{};
+ std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
+};
+static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
+ "JoyDisableSixAxisDataAccessor is an invalid size");
+
+struct JoyEnableSixAxisDataAccessor {
+ DataAccessorHeader header{};
+ std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
+};
+static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
+ "JoyEnableSixAxisDataAccessor is an invalid size");
+
+struct ButtonOnlyPollingDataAccessor {
+ DataAccessorHeader header;
+ std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
+};
+static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
+ "ButtonOnlyPollingDataAccessor is an invalid size");
+
+class HidbusBase {
+public:
+ explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
+ virtual ~HidbusBase();
+
+ void ActivateDevice();
+
+ void DeactivateDevice();
+
+ bool IsDeviceActivated() const;
+
+ // Enables/disables the device
+ void Enable(bool enable);
+
+ // returns true if device is enabled
+ bool IsEnabled() const;
+
+ // returns true if polling mode is enabled
+ bool IsPollingMode() const;
+
+ // returns polling mode
+ JoyPollingMode GetPollingMode() const;
+
+ // Sets and enables JoyPollingMode
+ void SetPollingMode(JoyPollingMode mode);
+
+ // Disables JoyPollingMode
+ void DisablePollingMode();
+
+ // Called on EnableJoyPollingReceiveMode
+ void SetTransferMemoryPointer(u8* t_mem);
+
+ Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
+
+ virtual void OnInit() {}
+
+ virtual void OnRelease() {}
+
+ // Updates device transfer memory
+ virtual void OnUpdate() {}
+
+ // Returns the device ID of the joycon
+ virtual u8 GetDeviceId() const {
+ return {};
+ }
+
+ // Assigns a command from data
+ virtual bool SetCommand(const std::vector<u8>& data) {
+ return {};
+ }
+
+ // Returns a reply from a command
+ virtual std::vector<u8> GetReply() const {
+ return {};
+ }
+
+protected:
+ bool is_activated{};
+ bool device_enabled{};
+ bool polling_mode_enabled{};
+ JoyPollingMode polling_mode = {};
+ // TODO(German77): All data accessors need to be replaced with a ring lifo object
+ JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
+ JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
+ ButtonOnlyPollingDataAccessor button_only_data{};
+
+ u8* transfer_memory{nullptr};
+ bool is_transfer_memory_set{};
+
+ Kernel::KEvent* send_command_async_event;
+ KernelHelpers::ServiceContext& service_context;
+};
+} // 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..ad223d649
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -0,0 +1,285 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hid/emulated_devices.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/service/hid/hidbus/ringcon.h"
+
+namespace Service::HID {
+
+RingController::RingController(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_)
+ : HidbusBase(service_context_) {
+ input = hid_core_.GetEmulatedDevices();
+}
+
+RingController::~RingController() = default;
+
+void RingController::OnInit() {
+ return;
+}
+
+void RingController::OnRelease() {
+ return;
+};
+
+void RingController::OnUpdate() {
+ if (!is_activated) {
+ return;
+ }
+
+ if (!device_enabled) {
+ return;
+ }
+
+ if (!polling_mode_enabled || !is_transfer_memory_set) {
+ return;
+ }
+
+ // TODO: Increment multitasking counters from motion and sensor data
+
+ switch (polling_mode) {
+ case JoyPollingMode::SixAxisSensorEnable: {
+ enable_sixaxis_data.header.total_entries = 10;
+ enable_sixaxis_data.header.result = ResultSuccess;
+ const auto& last_entry =
+ enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
+
+ enable_sixaxis_data.header.latest_entry =
+ (enable_sixaxis_data.header.latest_entry + 1) % 10;
+ auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
+
+ curr_entry.sampling_number = last_entry.sampling_number + 1;
+ curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
+
+ const RingConData ringcon_value = GetSensorValue();
+ curr_entry.polling_data.out_size = sizeof(ringcon_value);
+ std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
+
+ std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
+ break;
+ }
+ default:
+ LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
+ break;
+ }
+}
+
+RingController::RingConData RingController::GetSensorValue() const {
+ RingConData ringcon_sensor_value{
+ .status = DataValid::Valid,
+ .data = 0,
+ };
+
+ const f32 force_value = input->GetRingSensorForce().force * range;
+ ringcon_sensor_value.data = static_cast<s16>(force_value) + idle_value;
+
+ return ringcon_sensor_value;
+}
+
+u8 RingController::GetDeviceId() const {
+ return device_id;
+}
+
+std::vector<u8> RingController::GetReply() const {
+ const RingConCommands current_command = command;
+
+ switch (current_command) {
+ case RingConCommands::GetFirmwareVersion:
+ return GetFirmwareVersionReply();
+ case RingConCommands::ReadId:
+ return GetReadIdReply();
+ case RingConCommands::c20105:
+ return GetC020105Reply();
+ case RingConCommands::ReadUnkCal:
+ return GetReadUnkCalReply();
+ case RingConCommands::ReadFactoryCal:
+ return GetReadFactoryCalReply();
+ case RingConCommands::ReadUserCal:
+ return GetReadUserCalReply();
+ case RingConCommands::ReadRepCount:
+ return GetReadRepCountReply();
+ case RingConCommands::ReadTotalPushCount:
+ return GetReadTotalPushCountReply();
+ case RingConCommands::ResetRepCount:
+ return GetResetRepCountReply();
+ case RingConCommands::SaveCalData:
+ return GetSaveDataReply();
+ default:
+ return GetErrorReply();
+ }
+}
+
+bool RingController::SetCommand(const std::vector<u8>& data) {
+ if (data.size() < 4) {
+ LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
+ command = RingConCommands::Error;
+ return false;
+ }
+
+ std::memcpy(&command, data.data(), sizeof(RingConCommands));
+
+ switch (command) {
+ case RingConCommands::GetFirmwareVersion:
+ case RingConCommands::ReadId:
+ case RingConCommands::c20105:
+ case RingConCommands::ReadUnkCal:
+ case RingConCommands::ReadFactoryCal:
+ case RingConCommands::ReadUserCal:
+ case RingConCommands::ReadRepCount:
+ case RingConCommands::ReadTotalPushCount:
+ ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
+ send_command_async_event->GetWritableEvent().Signal();
+ return true;
+ case RingConCommands::ResetRepCount:
+ ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
+ total_rep_count = 0;
+ send_command_async_event->GetWritableEvent().Signal();
+ return true;
+ case RingConCommands::SaveCalData: {
+ ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
+
+ SaveCalData save_info{};
+ std::memcpy(&save_info, data.data(), sizeof(SaveCalData));
+ user_calibration = save_info.calibration;
+ send_command_async_event->GetWritableEvent().Signal();
+ return true;
+ }
+ default:
+ LOG_ERROR(Service_HID, "Command not implemented {}", command);
+ command = RingConCommands::Error;
+ // Signal a reply to avoid softlocking the game
+ send_command_async_event->GetWritableEvent().Signal();
+ return false;
+ }
+}
+
+std::vector<u8> RingController::GetFirmwareVersionReply() const {
+ const FirmwareVersionReply reply{
+ .status = DataValid::Valid,
+ .firmware = version,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadIdReply() const {
+ // The values are hardcoded from a real joycon
+ const ReadIdReply reply{
+ .status = DataValid::Valid,
+ .id_l_x0 = 8,
+ .id_l_x0_2 = 41,
+ .id_l_x4 = 22294,
+ .id_h_x0 = 19777,
+ .id_h_x0_2 = 13621,
+ .id_h_x4 = 8245,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetC020105Reply() const {
+ const Cmd020105Reply reply{
+ .status = DataValid::Valid,
+ .data = 1,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadUnkCalReply() const {
+ const ReadUnkCalReply reply{
+ .status = DataValid::Valid,
+ .data = 0,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadFactoryCalReply() const {
+ const ReadFactoryCalReply reply{
+ .status = DataValid::Valid,
+ .calibration = factory_calibration,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadUserCalReply() const {
+ const ReadUserCalReply reply{
+ .status = DataValid::Valid,
+ .calibration = user_calibration,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadRepCountReply() const {
+ const GetThreeByteReply reply{
+ .status = DataValid::Valid,
+ .data = {total_rep_count, 0, 0},
+ .crc = GetCrcValue({total_rep_count, 0, 0, 0}),
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetReadTotalPushCountReply() const {
+ const GetThreeByteReply reply{
+ .status = DataValid::Valid,
+ .data = {total_push_count, 0, 0},
+ .crc = GetCrcValue({total_push_count, 0, 0, 0}),
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetResetRepCountReply() const {
+ return GetReadRepCountReply();
+}
+
+std::vector<u8> RingController::GetSaveDataReply() const {
+ const StatusReply reply{
+ .status = DataValid::Valid,
+ };
+
+ return GetDataVector(reply);
+}
+
+std::vector<u8> RingController::GetErrorReply() const {
+ const ErrorReply reply{
+ .status = DataValid::BadCRC,
+ };
+
+ return GetDataVector(reply);
+}
+
+u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
+ u8 crc = 0;
+ for (std::size_t index = 0; index < data.size(); index++) {
+ for (u8 i = 0x80; i > 0; i >>= 1) {
+ bool bit = (crc & 0x80) != 0;
+ if ((data[index] & i) != 0) {
+ bit = !bit;
+ }
+ crc <<= 1;
+ if (bit) {
+ crc ^= 0x8d;
+ }
+ }
+ }
+ return crc;
+}
+
+template <typename T>
+std::vector<u8> RingController::GetDataVector(const T& reply) const {
+ static_assert(std::is_trivially_copyable_v<T>);
+ std::vector<u8> data;
+ data.resize(sizeof(reply));
+ std::memcpy(data.data(), &reply, sizeof(reply));
+ return data;
+}
+
+} // 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..b37df50ac
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -0,0 +1,253 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+
+namespace Core::HID {
+class EmulatedDevices;
+} // namespace Core::HID
+
+namespace Service::HID {
+
+class RingController final : public HidbusBase {
+public:
+ explicit RingController(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_);
+ ~RingController() override;
+
+ void OnInit() override;
+
+ void OnRelease() override;
+
+ // Updates ringcon transfer memory
+ void OnUpdate() override;
+
+ // Returns the device ID of the joycon
+ u8 GetDeviceId() const override;
+
+ // Assigns a command from data
+ bool SetCommand(const std::vector<u8>& data) override;
+
+ // Returns a reply from a command
+ std::vector<u8> GetReply() const override;
+
+private:
+ // These values are obtained from a real ring controller
+ static constexpr s16 idle_value = 2280;
+ static constexpr s16 idle_deadzone = 120;
+ static constexpr s16 range = 2500;
+
+ // Most missing command names are leftovers from other firmware versions
+ enum class RingConCommands : u32 {
+ GetFirmwareVersion = 0x00020000,
+ ReadId = 0x00020100,
+ JoyPolling = 0x00020101,
+ Unknown1 = 0x00020104,
+ c20105 = 0x00020105,
+ Unknown2 = 0x00020204,
+ Unknown3 = 0x00020304,
+ Unknown4 = 0x00020404,
+ ReadUnkCal = 0x00020504,
+ ReadFactoryCal = 0x00020A04,
+ Unknown5 = 0x00021104,
+ Unknown6 = 0x00021204,
+ Unknown7 = 0x00021304,
+ ReadUserCal = 0x00021A04,
+ ReadRepCount = 0x00023104,
+ ReadTotalPushCount = 0x00023204,
+ ResetRepCount = 0x04013104,
+ Unknown8 = 0x04011104,
+ Unknown9 = 0x04011204,
+ Unknown10 = 0x04011304,
+ SaveCalData = 0x10011A04,
+ Error = 0xFFFFFFFF,
+ };
+
+ enum class DataValid : u32 {
+ Valid,
+ BadCRC,
+ Cal,
+ };
+
+ struct FirmwareVersion {
+ u8 sub;
+ u8 main;
+ };
+ static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
+
+ struct FactoryCalibration {
+ s32_le os_max;
+ s32_le hk_max;
+ s32_le zero_min;
+ s32_le zero_max;
+ };
+ static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
+
+ struct CalibrationValue {
+ s16 value;
+ u16 crc;
+ };
+ static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
+
+ struct UserCalibration {
+ CalibrationValue os_max;
+ CalibrationValue hk_max;
+ CalibrationValue zero;
+ };
+ static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
+
+ struct SaveCalData {
+ RingConCommands command;
+ UserCalibration calibration;
+ INSERT_PADDING_BYTES_NOINIT(4);
+ };
+ static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
+ static_assert(std::is_trivially_copyable_v<SaveCalData>,
+ "SaveCalData must be trivially copyable");
+
+ struct FirmwareVersionReply {
+ DataValid status;
+ FirmwareVersion firmware;
+ INSERT_PADDING_BYTES(0x2);
+ };
+ static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
+
+ struct Cmd020105Reply {
+ DataValid status;
+ u8 data;
+ INSERT_PADDING_BYTES(0x3);
+ };
+ static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
+
+ struct StatusReply {
+ DataValid status;
+ };
+ static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
+
+ struct GetThreeByteReply {
+ DataValid status;
+ std::array<u8, 3> data;
+ u8 crc;
+ };
+ static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
+
+ struct ReadUnkCalReply {
+ DataValid status;
+ u16 data;
+ INSERT_PADDING_BYTES(0x2);
+ };
+ static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
+
+ struct ReadFactoryCalReply {
+ DataValid status;
+ FactoryCalibration calibration;
+ };
+ static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
+
+ struct ReadUserCalReply {
+ DataValid status;
+ UserCalibration calibration;
+ INSERT_PADDING_BYTES(0x4);
+ };
+ static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
+
+ struct ReadIdReply {
+ DataValid status;
+ u16 id_l_x0;
+ u16 id_l_x0_2;
+ u16 id_l_x4;
+ u16 id_h_x0;
+ u16 id_h_x0_2;
+ u16 id_h_x4;
+ };
+ static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
+
+ struct ErrorReply {
+ DataValid status;
+ INSERT_PADDING_BYTES(0x3);
+ };
+ static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
+
+ struct RingConData {
+ DataValid status;
+ s16_le data;
+ INSERT_PADDING_BYTES(0x2);
+ };
+ static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
+
+ // Returns RingConData struct with pressure sensor values
+ RingConData GetSensorValue() const;
+
+ // Returns 8 byte reply with firmware version
+ std::vector<u8> GetFirmwareVersionReply() const;
+
+ // Returns 16 byte reply with ID values
+ std::vector<u8> GetReadIdReply() const;
+
+ // (STUBBED) Returns 8 byte reply
+ std::vector<u8> GetC020105Reply() const;
+
+ // (STUBBED) Returns 8 byte empty reply
+ std::vector<u8> GetReadUnkCalReply() const;
+
+ // Returns 20 byte reply with factory calibration values
+ std::vector<u8> GetReadFactoryCalReply() const;
+
+ // Returns 20 byte reply with user calibration values
+ std::vector<u8> GetReadUserCalReply() const;
+
+ // Returns 8 byte reply
+ std::vector<u8> GetReadRepCountReply() const;
+
+ // Returns 8 byte reply
+ std::vector<u8> GetReadTotalPushCountReply() const;
+
+ // Returns 8 byte reply
+ std::vector<u8> GetResetRepCountReply() const;
+
+ // Returns 4 byte save data reply
+ std::vector<u8> GetSaveDataReply() const;
+
+ // Returns 8 byte error reply
+ std::vector<u8> GetErrorReply() const;
+
+ // Returns 8 bit redundancy check from provided data
+ u8 GetCrcValue(const std::vector<u8>& data) const;
+
+ // Converts structs to an u8 vector equivalent
+ template <typename T>
+ std::vector<u8> GetDataVector(const T& reply) const;
+
+ RingConCommands command{RingConCommands::Error};
+
+ // These counters are used in multitasking mode while the switch is sleeping
+ // Total steps taken
+ u8 total_rep_count = 0;
+ // Total times the ring was pushed
+ u8 total_push_count = 0;
+
+ const u8 device_id = 0x20;
+ const FirmwareVersion version = {
+ .sub = 0x0,
+ .main = 0x2c,
+ };
+ const FactoryCalibration factory_calibration = {
+ .os_max = idle_value + range + idle_deadzone,
+ .hk_max = idle_value - range - idle_deadzone,
+ .zero_min = idle_value - idle_deadzone,
+ .zero_max = idle_value + idle_deadzone,
+ };
+ UserCalibration user_calibration = {
+ .os_max = {.value = range, .crc = 228},
+ .hk_max = {.value = -range, .crc = 239},
+ .zero = {.value = idle_value, .crc = 225},
+ };
+
+ Core::HID::EmulatedDevices* input;
+};
+} // 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..dd439f60a
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.cpp
@@ -0,0 +1,50 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/hidbus/starlink.h"
+
+namespace Service::HID {
+constexpr u8 DEVICE_ID = 0x28;
+
+Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
+ : HidbusBase(service_context_) {}
+Starlink::~Starlink() = default;
+
+void Starlink::OnInit() {
+ return;
+}
+
+void Starlink::OnRelease() {
+ return;
+};
+
+void Starlink::OnUpdate() {
+ if (!is_activated) {
+ return;
+ }
+ if (!device_enabled) {
+ return;
+ }
+ if (!polling_mode_enabled || !is_transfer_memory_set) {
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
+}
+
+u8 Starlink::GetDeviceId() const {
+ return DEVICE_ID;
+}
+
+std::vector<u8> Starlink::GetReply() const {
+ return {};
+}
+
+bool Starlink::SetCommand(const std::vector<u8>& data) {
+ LOG_ERROR(Service_HID, "Command not implemented");
+ return false;
+}
+
+} // 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..0b1b7ba49
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.h
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::HID {
+
+class Starlink final : public HidbusBase {
+public:
+ explicit Starlink(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_);
+ ~Starlink() override;
+
+ void OnInit() override;
+
+ void OnRelease() override;
+
+ // Updates ringcon transfer memory
+ void OnUpdate() override;
+
+ // Returns the device ID of the joycon
+ u8 GetDeviceId() const override;
+
+ // Assigns a command from data
+ bool SetCommand(const std::vector<u8>& data) override;
+
+ // Returns a reply from a command
+ std::vector<u8> GetReply() const override;
+};
+
+} // 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..e477443e3
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.cpp
@@ -0,0 +1,51 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/hidbus/stubbed.h"
+
+namespace Service::HID {
+constexpr u8 DEVICE_ID = 0xFF;
+
+HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_)
+ : HidbusBase(service_context_) {}
+HidbusStubbed::~HidbusStubbed() = default;
+
+void HidbusStubbed::OnInit() {
+ return;
+}
+
+void HidbusStubbed::OnRelease() {
+ return;
+};
+
+void HidbusStubbed::OnUpdate() {
+ if (!is_activated) {
+ return;
+ }
+ if (!device_enabled) {
+ return;
+ }
+ if (!polling_mode_enabled || !is_transfer_memory_set) {
+ return;
+ }
+
+ LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
+}
+
+u8 HidbusStubbed::GetDeviceId() const {
+ return DEVICE_ID;
+}
+
+std::vector<u8> HidbusStubbed::GetReply() const {
+ return {};
+}
+
+bool HidbusStubbed::SetCommand(const std::vector<u8>& data) {
+ LOG_ERROR(Service_HID, "Command not implemented");
+ return false;
+}
+
+} // 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..91165ceff
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.h
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/hid/hidbus/hidbus_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::HID {
+
+class HidbusStubbed final : public HidbusBase {
+public:
+ explicit HidbusStubbed(Core::HID::HIDCore& hid_core_,
+ KernelHelpers::ServiceContext& service_context_);
+ ~HidbusStubbed() override;
+
+ void OnInit() override;
+
+ void OnRelease() override;
+
+ // Updates ringcon transfer memory
+ void OnUpdate() override;
+
+ // Returns the device ID of the joycon
+ u8 GetDeviceId() const override;
+
+ // Assigns a command from data
+ bool SetCommand(const std::vector<u8>& data) override;
+
+ // Returns a reply from a command
+ std::vector<u8> GetReply() const override;
+};
+
+} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 8812b8ecb..6a3453457 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -1,15 +1,28 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <algorithm>
+#include <random>
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_shared_memory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/service/hid/errors.h"
#include "core/hle/service/hid/irs.h"
+#include "core/hle/service/hid/irsensor/clustering_processor.h"
+#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
+#include "core/hle/service/hid/irsensor/ir_led_processor.h"
+#include "core/hle/service/hid/irsensor/moment_processor.h"
+#include "core/hle/service/hid/irsensor/pointing_processor.h"
+#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
+#include "core/memory.h"
-namespace Service::HID {
+namespace Service::IRS {
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
// clang-format off
@@ -35,25 +48,41 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
};
// clang-format on
+ u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
RegisterHandlers(functions);
+ shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));
+
+ npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
}
+IRS::~IRS() = default;
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_IRS, "called");
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
@@ -61,114 +90,462 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
}
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Stop Image processor
+ result = ResultSuccess;
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ Core::IrSensor::PackedMomentProcessorConfig processor_config;
+ };
+ static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ Core::IrSensor::PackedClusteringProcessorConfig processor_config;
+ };
+ static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ClusteringProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
+ u32 transfer_memory_size;
+ };
+ static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+ const auto t_mem_handle{ctx.GetCopyHandle(0)};
+
+ auto t_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+
+ if (t_mem.IsNull()) {
+ LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
+
+ u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
+
+ LOG_INFO(Service_IRS,
+ "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsError()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ return;
+ }
+
+ const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+
+ if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidProcessorState);
+ return;
+ }
- IPC::ResponseBuilder rb{ctx, 5};
+ std::vector<u8> data{};
+ const auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ const auto& state = image_transfer_processor.GetState(data);
+
+ ctx.WriteBuffer(data);
+ IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
- rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
- rb.PushRaw<u32>(0);
+ rb.PushRaw(state);
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
+ parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
+
+ const auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()};
+
+ if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
+ npad_id != Core::HID::NpadIdType::Handheld) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(Service::HID::InvalidNpadId);
+ return;
+ }
+
+ Core::IrSensor::IrCameraHandle camera_handle{
+ .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
+ .npad_type = Core::HID::NpadStyleIndex::None,
+ };
+
+ LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
+ camera_handle.npad_id, camera_handle.npad_type);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.PushRaw<u32>(device_handle);
+ rb.PushRaw(camera_handle);
}
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
+ processor_config.required_mcu_version.minor, applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
+ MakeProcessor<PointingProcessor>(camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
+ image_transfer_processor.SetConfig(processor_config);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Suspend image processor
+ result = ResultSuccess;
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}",
+ camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
+ mcu_version.minor);
+
+ auto result = IsIrCameraHandleValid(camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Check firmware version
+ result = ResultSuccess;
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(
+ Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
+ applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Set Function level
+ result = ResultSuccess;
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
+ u64 transfer_memory_size;
+ };
+ static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+ const auto t_mem_handle{ctx.GetCopyHandle(0)};
+
+ auto t_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
+
+ u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
+
+ LOG_INFO(Service_IRS,
+ "called, npad_type={}, npad_id={}, transfer_memory_size={}, "
+ "applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.transfer_memory_size, parameters.applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
+ MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
+ auto& image_transfer_processor =
+ GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
+ image_transfer_processor.SetConfig(parameters.processor_config);
+ image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
+ const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} "
+ "applet_resource_user_id={}",
+ camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target,
+ processor_config.required_mcu_version.major,
+ processor_config.required_mcu_version.minor, applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(camera_handle);
+
+ if (result.IsSuccess()) {
+ auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
+ MakeProcessor<IrLedProcessor>(camera_handle, device);
+ auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
+ image_transfer_processor.SetConfig(processor_config);
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::IrCameraHandle camera_handle;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS,
+ "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
+ parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
+ parameters.applet_resource_user_id);
+
+ auto result = IsIrCameraHandleValid(parameters.camera_handle);
+ if (result.IsSuccess()) {
+ // TODO: Stop image processor async
+ result = ResultSuccess;
+ }
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ rb.Push(result);
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_IRS, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Core::IrSensor::PackedFunctionLevel function_level;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ u64 applet_resource_user_id;
+ };
+ static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}",
+ parameters.function_level.function_level, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
-IRS::~IRS() = default;
+Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
+ if (camera_handle.npad_id >
+ static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
+ return InvalidIrCameraHandle;
+ }
+ if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
+ return InvalidIrCameraHandle;
+ }
+ return ResultSuccess;
+}
+
+Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
+ const Core::IrSensor::IrCameraHandle& camera_handle) {
+ const auto npad_id_max_index = static_cast<u8>(sizeof(StatusManager::device));
+ ASSERT_MSG(camera_handle.npad_id < npad_id_max_index, "invalid npad_id");
+ return shared_memory->device[camera_handle.npad_id];
+}
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
// clang-format off
@@ -185,4 +562,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
IRS_SYS::~IRS_SYS() = default;
-} // namespace Service::HID
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 9bc6462b0..2e6115c73 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -1,16 +1,22 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include "core/hid/hid_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
-namespace Service::HID {
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
class IRS final : public ServiceFramework<IRS> {
public:
@@ -18,6 +24,20 @@ public:
~IRS() override;
private:
+ // This is nn::irsensor::detail::AruidFormat
+ struct AruidFormat {
+ u64 sensor_aruid;
+ u64 sensor_aruid_status;
+ };
+ static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
+
+ // This is nn::irsensor::detail::StatusManager
+ struct StatusManager {
+ std::array<Core::IrSensor::DeviceFormat, 9> device;
+ std::array<AruidFormat, 5> aruid;
+ };
+ static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
+
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -37,7 +57,55 @@ private:
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
- const u32 device_handle{0xABCD};
+ Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
+ Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
+ const Core::IrSensor::IrCameraHandle& camera_handle);
+
+ template <typename T>
+ void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
+ Core::IrSensor::DeviceFormat& device_state) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return;
+ }
+ processors[index] = std::make_unique<T>(device_state);
+ }
+
+ template <typename T>
+ void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
+ Core::IrSensor::DeviceFormat& device_state) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return;
+ }
+ processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
+ }
+
+ template <typename T>
+ T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return static_cast<T&>(*processors[0]);
+ }
+ return static_cast<T&>(*processors[index]);
+ }
+
+ template <typename T>
+ const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
+ const auto index = static_cast<std::size_t>(handle.npad_id);
+ if (index > sizeof(processors)) {
+ LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
+ return static_cast<T&>(*processors[0]);
+ }
+ return static_cast<T&>(*processors[index]);
+ }
+
+ Core::HID::EmulatedController* npad_device = nullptr;
+ StatusManager* shared_memory = nullptr;
+ std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
};
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
@@ -46,4 +114,4 @@ public:
~IRS_SYS() override;
};
-} // namespace Service::HID
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irs_ring_lifo.h b/src/core/hle/service/hid/irs_ring_lifo.h
new file mode 100644
index 000000000..255d1d296
--- /dev/null
+++ b/src/core/hle/service/hid/irs_ring_lifo.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+
+namespace Service::IRS {
+
+template <typename State, std::size_t max_buffer_size>
+struct Lifo {
+ s64 sampling_number{};
+ s64 buffer_count{};
+ std::array<State, max_buffer_size> entries{};
+
+ const State& ReadCurrentEntry() const {
+ return entries[GetBufferTail()];
+ }
+
+ const State& ReadPreviousEntry() const {
+ return entries[GetPreviousEntryIndex()];
+ }
+
+ s64 GetBufferTail() const {
+ return sampling_number % max_buffer_size;
+ }
+
+ std::size_t GetPreviousEntryIndex() const {
+ return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
+ }
+
+ std::size_t GetNextEntryIndex() const {
+ return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
+ }
+
+ void WriteNextEntry(const State& new_state) {
+ if (buffer_count < static_cast<s64>(max_buffer_size)) {
+ buffer_count++;
+ }
+ sampling_number++;
+ entries[GetBufferTail()] = new_state;
+ }
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
new file mode 100644
index 000000000..e2f4ae876
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -0,0 +1,265 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <queue>
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/irsensor/clustering_processor.h"
+
+namespace Service::IRS {
+ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index)
+ : device{device_format} {
+ npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
+
+ device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+ SetDefaultConfig();
+
+ shared_memory = std::construct_at(
+ reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
+ .is_npad_service = true,
+ };
+ callback_key = npad_device->SetCallback(engine_callback);
+}
+
+ClusteringProcessor::~ClusteringProcessor() {
+ npad_device->DeleteCallback(callback_key);
+};
+
+void ClusteringProcessor::StartProcessor() {
+ device.camera_status = Core::IrSensor::IrCameraStatus::Available;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
+}
+
+void ClusteringProcessor::SuspendProcessor() {}
+
+void ClusteringProcessor::StopProcessor() {}
+
+void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (type != Core::HID::ControllerTriggerType::IrSensor) {
+ return;
+ }
+
+ next_state = {};
+ const auto camera_data = npad_device->GetCamera();
+ auto filtered_image = camera_data.data;
+
+ RemoveLowIntensityData(filtered_image);
+
+ const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
+ const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
+ const auto window_end_x =
+ window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
+ const auto window_end_y =
+ window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
+
+ for (std::size_t y = window_start_y; y < window_end_y; y++) {
+ for (std::size_t x = window_start_x; x < window_end_x; x++) {
+ u8 pixel = GetPixel(filtered_image, x, y);
+ if (pixel == 0) {
+ continue;
+ }
+ const auto cluster = GetClusterProperties(filtered_image, x, y);
+ if (cluster.pixel_count > current_config.pixel_count_max) {
+ continue;
+ }
+ if (cluster.pixel_count < current_config.pixel_count_min) {
+ continue;
+ }
+ // Cluster object limit reached
+ if (next_state.object_count >= next_state.data.size()) {
+ continue;
+ }
+ next_state.data[next_state.object_count] = cluster;
+ next_state.object_count++;
+ }
+ }
+
+ next_state.sampling_number = camera_data.sample;
+ next_state.timestamp = next_state.timestamp + 131;
+ next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+ shared_memory->clustering_lifo.WriteNextEntry(next_state);
+
+ if (!IsProcessorActive()) {
+ StartProcessor();
+ }
+}
+
+void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
+ for (u8& pixel : data) {
+ if (pixel < current_config.pixel_count_min) {
+ pixel = 0;
+ }
+ }
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
+ std::size_t x,
+ std::size_t y) {
+ using DataPoint = Common::Point<std::size_t>;
+ std::queue<DataPoint> search_points{};
+ ClusteringData current_cluster = GetPixelProperties(data, x, y);
+ SetPixel(data, x, y, 0);
+ search_points.emplace<DataPoint>({x, y});
+
+ while (!search_points.empty()) {
+ const auto point = search_points.front();
+ search_points.pop();
+
+ // Avoid negative numbers
+ if (point.x == 0 || point.y == 0) {
+ continue;
+ }
+
+ std::array<DataPoint, 4> new_points{
+ DataPoint{point.x - 1, point.y},
+ {point.x, point.y - 1},
+ {point.x + 1, point.y},
+ {point.x, point.y + 1},
+ };
+
+ for (const auto new_point : new_points) {
+ if (new_point.x >= width) {
+ continue;
+ }
+ if (new_point.y >= height) {
+ continue;
+ }
+ if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
+ continue;
+ }
+ const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
+ current_cluster = MergeCluster(current_cluster, cluster);
+ SetPixel(data, new_point.x, new_point.y, 0);
+ search_points.emplace<DataPoint>({new_point.x, new_point.y});
+ }
+ }
+
+ return current_cluster;
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
+ const std::vector<u8>& data, std::size_t x, std::size_t y) const {
+ return {
+ .average_intensity = GetPixel(data, x, y) / 255.0f,
+ .centroid =
+ {
+ .x = static_cast<f32>(x),
+ .y = static_cast<f32>(y),
+
+ },
+ .pixel_count = 1,
+ .bound =
+ {
+ .x = static_cast<s16>(x),
+ .y = static_cast<s16>(y),
+ .width = 1,
+ .height = 1,
+ },
+ };
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
+ const ClusteringData a, const ClusteringData b) const {
+ const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
+ const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
+ const f32 pixel_count = a_pixel_count + b_pixel_count;
+ const f32 average_intensity =
+ (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
+ const Core::IrSensor::IrsCentroid centroid = {
+ .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
+ .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
+ };
+ s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
+ s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
+ s16 a_bound_end_x = a.bound.x + a.bound.width;
+ s16 a_bound_end_y = a.bound.y + a.bound.height;
+ s16 b_bound_end_x = b.bound.x + b.bound.width;
+ s16 b_bound_end_y = b.bound.y + b.bound.height;
+
+ const Core::IrSensor::IrsRect bound = {
+ .x = bound_start_x,
+ .y = bound_start_y,
+ .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
+ : static_cast<s16>(b_bound_end_x - bound_start_x),
+ .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
+ : static_cast<s16>(b_bound_end_y - bound_start_y),
+ };
+
+ return {
+ .average_intensity = average_intensity,
+ .centroid = centroid,
+ .pixel_count = static_cast<u32>(pixel_count),
+ .bound = bound,
+ };
+}
+
+u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
+ if ((y * width) + x > data.size()) {
+ return 0;
+ }
+ return data[(y * width) + x];
+}
+
+void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
+ if ((y * width) + x > data.size()) {
+ return;
+ }
+ data[(y * width) + x] = value;
+}
+
+void ClusteringProcessor::SetDefaultConfig() {
+ using namespace std::literals::chrono_literals;
+ current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
+ current_config.camera_config.gain = 2;
+ current_config.camera_config.is_negative_used = false;
+ current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
+ current_config.window_of_interest = {
+ .x = 0,
+ .y = 0,
+ .width = width,
+ .height = height,
+ };
+ current_config.pixel_count_min = 3;
+ current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
+ current_config.is_external_light_filter_enabled = true;
+ current_config.object_intensity_min = 150;
+
+ npad_device->SetCameraFormat(format);
+}
+
+void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.window_of_interest = config.window_of_interest;
+ current_config.pixel_count_min = config.pixel_count_min;
+ current_config.pixel_count_max = config.pixel_count_max;
+ current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
+ current_config.object_intensity_min = config.object_intensity_min;
+
+ LOG_INFO(Service_IRS,
+ "Processor config, exposure_time={}, gain={}, is_negative_used={}, "
+ "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
+ "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
+ current_config.camera_config.exposure_time, current_config.camera_config.gain,
+ current_config.camera_config.is_negative_used,
+ current_config.camera_config.light_target, current_config.window_of_interest.x,
+ current_config.window_of_interest.y, current_config.window_of_interest.width,
+ current_config.window_of_interest.height, current_config.pixel_count_min,
+ current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
+ current_config.object_intensity_min);
+
+ npad_device->SetCameraFormat(format);
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
new file mode 100644
index 000000000..dc01a8ea7
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -0,0 +1,110 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irs_ring_lifo.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
+class ClusteringProcessor final : public ProcessorBase {
+public:
+ explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index);
+ ~ClusteringProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
+
+private:
+ static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
+ static constexpr std::size_t width = 320;
+ static constexpr std::size_t height = 240;
+
+ // This is nn::irsensor::ClusteringProcessorConfig
+ struct ClusteringProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::IrsRect window_of_interest;
+ u32 pixel_count_min;
+ u32 pixel_count_max;
+ u32 object_intensity_min;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
+ "ClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::AdaptiveClusteringProcessorConfig
+ struct AdaptiveClusteringProcessorConfig {
+ Core::IrSensor::AdaptiveClusteringMode mode;
+ Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
+ };
+ static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
+ "AdaptiveClusteringProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ClusteringData
+ struct ClusteringData {
+ f32 average_intensity;
+ Core::IrSensor::IrsCentroid centroid;
+ u32 pixel_count;
+ Core::IrSensor::IrsRect bound;
+ };
+ static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
+
+ // This is nn::irsensor::ClusteringProcessorState
+ struct ClusteringProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ u8 object_count;
+ INSERT_PADDING_BYTES(3);
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ std::array<ClusteringData, 0x10> data;
+ };
+ static_assert(sizeof(ClusteringProcessorState) == 0x198,
+ "ClusteringProcessorState is an invalid size");
+
+ struct ClusteringSharedMemory {
+ Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
+ static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0x11F);
+ };
+ static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
+ "ClusteringSharedMemory is an invalid size");
+
+ void OnControllerUpdate(Core::HID::ControllerTriggerType type);
+ void RemoveLowIntensityData(std::vector<u8>& data);
+ ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
+ ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
+ std::size_t y) const;
+ ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
+ u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
+ void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
+
+ // Sets config parameters of the camera
+ void SetDefaultConfig();
+
+ ClusteringSharedMemory* shared_memory = nullptr;
+ ClusteringProcessorState next_state{};
+
+ ClusteringProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+ Core::HID::EmulatedController* npad_device;
+ int callback_key{};
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
new file mode 100644
index 000000000..98f0c579d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
@@ -0,0 +1,150 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
+
+namespace Service::IRS {
+ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index)
+ : device{device_format} {
+ npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
+ .is_npad_service = true,
+ };
+ callback_key = npad_device->SetCallback(engine_callback);
+
+ device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+ImageTransferProcessor::~ImageTransferProcessor() {
+ npad_device->DeleteCallback(callback_key);
+};
+
+void ImageTransferProcessor::StartProcessor() {
+ is_active = true;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Available;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
+ processor_state.sampling_number = 0;
+ processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+}
+
+void ImageTransferProcessor::SuspendProcessor() {}
+
+void ImageTransferProcessor::StopProcessor() {}
+
+void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (type != Core::HID::ControllerTriggerType::IrSensor) {
+ return;
+ }
+ if (!is_transfer_memory_set) {
+ return;
+ }
+
+ const auto camera_data = npad_device->GetCamera();
+
+ // This indicates how much ambient light is precent
+ processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+ processor_state.sampling_number = camera_data.sample;
+
+ if (camera_data.format != current_config.origin_format) {
+ LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
+ current_config.origin_format);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ if (current_config.origin_format > current_config.trimming_format) {
+ LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
+ current_config.origin_format, current_config.trimming_format);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ std::vector<u8> window_data{};
+ const auto origin_width = GetDataWidth(current_config.origin_format);
+ const auto origin_height = GetDataHeight(current_config.origin_format);
+ const auto trimming_width = GetDataWidth(current_config.trimming_format);
+ const auto trimming_height = GetDataHeight(current_config.trimming_format);
+ window_data.resize(GetDataSize(current_config.trimming_format));
+
+ if (trimming_width + current_config.trimming_start_x > origin_width ||
+ trimming_height + current_config.trimming_start_y > origin_height) {
+ LOG_WARNING(Service_IRS,
+ "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
+ current_config.trimming_start_x, current_config.trimming_start_y,
+ trimming_width, trimming_height, origin_width, origin_height);
+ memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
+ return;
+ }
+
+ for (std::size_t y = 0; y < trimming_height; y++) {
+ for (std::size_t x = 0; x < trimming_width; x++) {
+ const std::size_t window_index = (y * trimming_width) + x;
+ const std::size_t origin_index =
+ ((y + current_config.trimming_start_y) * origin_width) + x +
+ current_config.trimming_start_x;
+ window_data[window_index] = camera_data.data[origin_index];
+ }
+ }
+
+ memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
+
+ if (!IsProcessorActive()) {
+ StartProcessor();
+ }
+}
+
+void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.origin_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
+ current_config.trimming_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
+ current_config.trimming_start_x = 0;
+ current_config.trimming_start_y = 0;
+
+ npad_device->SetCameraFormat(current_config.origin_format);
+}
+
+void ImageTransferProcessor::SetConfig(
+ Core::IrSensor::PackedImageTransferProcessorExConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.origin_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
+ current_config.trimming_format =
+ static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
+ current_config.trimming_start_x = config.trimming_start_x;
+ current_config.trimming_start_y = config.trimming_start_y;
+
+ npad_device->SetCameraFormat(current_config.origin_format);
+}
+
+void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
+ is_transfer_memory_set = true;
+ transfer_memory = t_mem;
+}
+
+Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
+ std::vector<u8>& data) const {
+ const auto size = GetDataSize(current_config.trimming_format);
+ data.resize(size);
+ memcpy(data.data(), transfer_memory, size);
+ return processor_state;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/image_transfer_processor.h b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
new file mode 100644
index 000000000..393df492d
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/image_transfer_processor.h
@@ -0,0 +1,73 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
+namespace Service::IRS {
+class ImageTransferProcessor final : public ProcessorBase {
+public:
+ explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index);
+ ~ImageTransferProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
+ void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
+
+ // Transfer memory where the image data will be stored
+ void SetTransferMemoryPointer(u8* t_mem);
+
+ Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
+
+private:
+ // This is nn::irsensor::ImageTransferProcessorConfig
+ struct ImageTransferProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::ImageTransferProcessorFormat format;
+ };
+ static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
+ "ImageTransferProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::ImageTransferProcessorExConfig
+ struct ImageTransferProcessorExConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::ImageTransferProcessorFormat origin_format;
+ Core::IrSensor::ImageTransferProcessorFormat trimming_format;
+ u16 trimming_start_x;
+ u16 trimming_start_y;
+ bool is_external_light_filter_enabled;
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
+ "ImageTransferProcessorExConfig is an invalid size");
+
+ void OnControllerUpdate(Core::HID::ControllerTriggerType type);
+
+ ImageTransferProcessorExConfig current_config{};
+ Core::IrSensor::ImageTransferProcessorState processor_state{};
+ Core::IrSensor::DeviceFormat& device;
+ Core::HID::EmulatedController* npad_device;
+ int callback_key{};
+
+ u8* transfer_memory = nullptr;
+ bool is_transfer_memory_set = false;
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.cpp b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
new file mode 100644
index 000000000..8e6dd99e4
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.cpp
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/ir_led_processor.h"
+
+namespace Service::IRS {
+IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+IrLedProcessor::~IrLedProcessor() = default;
+
+void IrLedProcessor::StartProcessor() {}
+
+void IrLedProcessor::SuspendProcessor() {}
+
+void IrLedProcessor::StopProcessor() {}
+
+void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
+ current_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/ir_led_processor.h b/src/core/hle/service/hid/irsensor/ir_led_processor.h
new file mode 100644
index 000000000..c3d8693c9
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/ir_led_processor.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class IrLedProcessor final : public ProcessorBase {
+public:
+ explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~IrLedProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
+
+private:
+ // This is nn::irsensor::IrLedProcessorConfig
+ struct IrLedProcessorConfig {
+ Core::IrSensor::CameraLightTarget light_target;
+ };
+ static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
+
+ struct IrLedProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ std::array<u8, 0x8> data;
+ };
+ static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
+
+ IrLedProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.cpp b/src/core/hle/service/hid/irsensor/moment_processor.cpp
new file mode 100644
index 000000000..dbaca420a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.cpp
@@ -0,0 +1,34 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/moment_processor.h"
+
+namespace Service::IRS {
+MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+MomentProcessor::~MomentProcessor() = default;
+
+void MomentProcessor::StartProcessor() {}
+
+void MomentProcessor::SuspendProcessor() {}
+
+void MomentProcessor::StopProcessor() {}
+
+void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
+ current_config.camera_config.exposure_time = config.camera_config.exposure_time;
+ current_config.camera_config.gain = config.camera_config.gain;
+ current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
+ current_config.camera_config.light_target =
+ static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.window_of_interest = config.window_of_interest;
+ current_config.preprocess =
+ static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
+ current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/moment_processor.h b/src/core/hle/service/hid/irsensor/moment_processor.h
new file mode 100644
index 000000000..d4bd22e0f
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/moment_processor.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class MomentProcessor final : public ProcessorBase {
+public:
+ explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~MomentProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
+
+private:
+ // This is nn::irsensor::MomentProcessorConfig
+ struct MomentProcessorConfig {
+ Core::IrSensor::CameraConfig camera_config;
+ Core::IrSensor::IrsRect window_of_interest;
+ Core::IrSensor::MomentProcessorPreprocess preprocess;
+ u32 preprocess_intensity_threshold;
+ };
+ static_assert(sizeof(MomentProcessorConfig) == 0x28,
+ "MomentProcessorConfig is an invalid size");
+
+ // This is nn::irsensor::MomentStatistic
+ struct MomentStatistic {
+ f32 average_intensity;
+ Core::IrSensor::IrsCentroid centroid;
+ };
+ static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
+
+ // This is nn::irsensor::MomentProcessorState
+ struct MomentProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ INSERT_PADDING_BYTES(4);
+ std::array<MomentStatistic, 0x30> stadistic;
+ };
+ static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
+
+ MomentProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.cpp b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
new file mode 100644
index 000000000..929f177fc
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.cpp
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/pointing_processor.h"
+
+namespace Service::IRS {
+PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+PointingProcessor::~PointingProcessor() = default;
+
+void PointingProcessor::StartProcessor() {}
+
+void PointingProcessor::SuspendProcessor() {}
+
+void PointingProcessor::StopProcessor() {}
+
+void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
+ current_config.window_of_interest = config.window_of_interest;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/pointing_processor.h b/src/core/hle/service/hid/irsensor/pointing_processor.h
new file mode 100644
index 000000000..cf4930794
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/pointing_processor.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class PointingProcessor final : public ProcessorBase {
+public:
+ explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~PointingProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
+
+private:
+ // This is nn::irsensor::PointingProcessorConfig
+ struct PointingProcessorConfig {
+ Core::IrSensor::IrsRect window_of_interest;
+ };
+ static_assert(sizeof(PointingProcessorConfig) == 0x8,
+ "PointingProcessorConfig is an invalid size");
+
+ struct PointingProcessorMarkerData {
+ u8 pointing_status;
+ INSERT_PADDING_BYTES(3);
+ u32 unknown;
+ float unkown_float1;
+ float position_x;
+ float position_y;
+ float unkown_float2;
+ Core::IrSensor::IrsRect window_of_interest;
+ };
+ static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
+ "PointingProcessorMarkerData is an invalid size");
+
+ struct PointingProcessorMarkerState {
+ s64 sampling_number;
+ u64 timestamp;
+ std::array<PointingProcessorMarkerData, 0x3> data;
+ };
+ static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
+ "PointingProcessorMarkerState is an invalid size");
+
+ PointingProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.cpp b/src/core/hle/service/hid/irsensor/processor_base.cpp
new file mode 100644
index 000000000..4d43ca17a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.cpp
@@ -0,0 +1,67 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+
+ProcessorBase::ProcessorBase() {}
+ProcessorBase::~ProcessorBase() = default;
+
+bool ProcessorBase::IsProcessorActive() const {
+ return is_active;
+}
+
+std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 320 * 240;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 160 * 120;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 80 * 60;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 40 * 30;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 20 * 15;
+ default:
+ return 0;
+ }
+}
+
+std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 320;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 160;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 80;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 40;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 20;
+ default:
+ return 0;
+ }
+}
+
+std::size_t ProcessorBase::GetDataHeight(
+ Core::IrSensor::ImageTransferProcessorFormat format) const {
+ switch (format) {
+ case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
+ return 240;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
+ return 120;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
+ return 60;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
+ return 30;
+ case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
+ return 15;
+ default:
+ return 0;
+ }
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/processor_base.h b/src/core/hle/service/hid/irsensor/processor_base.h
new file mode 100644
index 000000000..bc0d2977b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/processor_base.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+
+namespace Service::IRS {
+class ProcessorBase {
+public:
+ explicit ProcessorBase();
+ virtual ~ProcessorBase();
+
+ virtual void StartProcessor() = 0;
+ virtual void SuspendProcessor() = 0;
+ virtual void StopProcessor() = 0;
+
+ bool IsProcessorActive() const;
+
+protected:
+ /// Returns the number of bytes the image uses
+ std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ /// Returns the width of the image
+ std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ /// Returns the height of the image
+ std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
+
+ bool is_active{false};
+};
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
new file mode 100644
index 000000000..e691c840a
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
@@ -0,0 +1,29 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
+
+namespace Service::IRS {
+TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
+ : device(device_format) {
+ device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
+ device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+}
+
+TeraPluginProcessor::~TeraPluginProcessor() = default;
+
+void TeraPluginProcessor::StartProcessor() {}
+
+void TeraPluginProcessor::SuspendProcessor() {}
+
+void TeraPluginProcessor::StopProcessor() {}
+
+void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
+ current_config.mode = config.mode;
+ current_config.unknown_1 = config.unknown_1;
+ current_config.unknown_2 = config.unknown_2;
+ current_config.unknown_3 = config.unknown_3;
+}
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/tera_plugin_processor.h b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
new file mode 100644
index 000000000..bbea7ed0b
--- /dev/null
+++ b/src/core/hle/service/hid/irsensor/tera_plugin_processor.h
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irsensor/processor_base.h"
+
+namespace Service::IRS {
+class TeraPluginProcessor final : public ProcessorBase {
+public:
+ explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
+ ~TeraPluginProcessor() override;
+
+ // Called when the processor is initialized
+ void StartProcessor() override;
+
+ // Called when the processor is suspended
+ void SuspendProcessor() override;
+
+ // Called when the processor is stopped
+ void StopProcessor() override;
+
+ // Sets config parameters of the camera
+ void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
+
+private:
+ // This is nn::irsensor::TeraPluginProcessorConfig
+ struct TeraPluginProcessorConfig {
+ u8 mode;
+ u8 unknown_1;
+ u8 unknown_2;
+ u8 unknown_3;
+ };
+ static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
+ "TeraPluginProcessorConfig is an invalid size");
+
+ struct TeraPluginProcessorState {
+ s64 sampling_number;
+ u64 timestamp;
+ Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
+ std::array<u8, 0x12c> data;
+ };
+ static_assert(sizeof(TeraPluginProcessorState) == 0x140,
+ "TeraPluginProcessorState is an invalid size");
+
+ TeraPluginProcessorConfig current_config{};
+ Core::IrSensor::DeviceFormat& device;
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/ring_lifo.h b/src/core/hle/service/hid/ring_lifo.h
index 44c20d967..65eb7ea02 100644
--- a/src/core/hle/service/hid/ring_lifo.h
+++ b/src/core/hle/service/hid/ring_lifo.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/hid/xcd.cpp b/src/core/hle/service/hid/xcd.cpp
index b1efa3d05..75cc266ea 100644
--- a/src/core/hle/service/hid/xcd.cpp
+++ b/src/core/hle/service/hid/xcd.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/hid/xcd.h"
diff --git a/src/core/hle/service/hid/xcd.h b/src/core/hle/service/hid/xcd.h
index 54932c228..fe0b91b91 100644
--- a/src/core/hle/service/hid/xcd.h
+++ b/src/core/hle/service/hid/xcd.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp
new file mode 100644
index 000000000..8f2920c51
--- /dev/null
+++ b/src/core/hle/service/jit/jit.cpp
@@ -0,0 +1,404 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/arm/symbols.h"
+#include "core/core.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_code_memory.h"
+#include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/result.h"
+#include "core/hle/service/jit/jit.h"
+#include "core/hle/service/jit/jit_context.h"
+#include "core/hle/service/service.h"
+#include "core/memory.h"
+
+namespace Service::JIT {
+
+struct CodeRange {
+ u64 offset;
+ u64 size;
+};
+
+class IJitEnvironment final : public ServiceFramework<IJitEnvironment> {
+public:
+ explicit IJitEnvironment(Core::System& system_, Kernel::KProcess& process_, CodeRange user_rx,
+ CodeRange user_ro)
+ : ServiceFramework{system_, "IJitEnvironment", ServiceThreadType::CreateNew},
+ process{&process_}, context{system_.Memory()} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IJitEnvironment::GenerateCode, "GenerateCode"},
+ {1, &IJitEnvironment::Control, "Control"},
+ {1000, &IJitEnvironment::LoadPlugin, "LoadPlugin"},
+ {1001, &IJitEnvironment::GetCodeAddress, "GetCodeAddress"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ // Identity map user code range into sysmodule context
+ configuration.user_ro_memory = user_ro;
+ configuration.user_rx_memory = user_rx;
+ configuration.sys_ro_memory = user_ro;
+ configuration.sys_rx_memory = user_rx;
+ }
+
+ void GenerateCode(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_JIT, "called");
+
+ struct InputParameters {
+ u32 data_size;
+ u64 command;
+ std::array<CodeRange, 2> ranges;
+ Struct32 data;
+ };
+
+ struct OutputParameters {
+ s32 return_value;
+ std::array<CodeRange, 2> ranges;
+ };
+
+ IPC::RequestParser rp{ctx};
+ const auto parameters{rp.PopRaw<InputParameters>()};
+
+ // Optional input/output buffers
+ std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
+ std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
+
+ // Function call prototype:
+ // void GenerateCode(s32* ret, CodeRange* c0_out, CodeRange* c1_out, JITConfiguration* cfg,
+ // u64 cmd, u8* input_buf, size_t input_size, CodeRange* c0_in,
+ // CodeRange* c1_in, Struct32* data, size_t data_size, u8* output_buf,
+ // size_t output_size);
+ //
+ // The command argument is used to control the behavior of the plugin during code
+ // generation. The configuration allows the plugin to access the output code ranges, and the
+ // other arguments are used to transfer state between the game and the plugin.
+
+ const VAddr ret_ptr{context.AddHeap(0u)};
+ const VAddr c0_in_ptr{context.AddHeap(parameters.ranges[0])};
+ const VAddr c1_in_ptr{context.AddHeap(parameters.ranges[1])};
+ const VAddr c0_out_ptr{context.AddHeap(ClearSize(parameters.ranges[0]))};
+ const VAddr c1_out_ptr{context.AddHeap(ClearSize(parameters.ranges[1]))};
+
+ const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
+ const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
+ const VAddr data_ptr{context.AddHeap(parameters.data)};
+ const VAddr configuration_ptr{context.AddHeap(configuration)};
+
+ // The callback does not directly return a value, it only writes to the output pointer
+ context.CallFunction(callbacks.GenerateCode, ret_ptr, c0_out_ptr, c1_out_ptr,
+ configuration_ptr, parameters.command, input_ptr, input_buffer.size(),
+ c0_in_ptr, c1_in_ptr, data_ptr, parameters.data_size, output_ptr,
+ output_buffer.size());
+
+ const s32 return_value{context.GetHeap<s32>(ret_ptr)};
+
+ if (return_value == 0) {
+ // The callback has written to the output executable code range,
+ // requiring an instruction cache invalidation
+ system.InvalidateCpuInstructionCacheRange(configuration.user_rx_memory.offset,
+ configuration.user_rx_memory.size);
+
+ // Write back to the IPC output buffer, if provided
+ if (ctx.CanWriteBuffer()) {
+ context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
+ ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
+ }
+
+ const OutputParameters out{
+ .return_value = return_value,
+ .ranges =
+ {
+ context.GetHeap<CodeRange>(c0_out_ptr),
+ context.GetHeap<CodeRange>(c1_out_ptr),
+ },
+ };
+
+ IPC::ResponseBuilder rb{ctx, 8};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(out);
+ } else {
+ LOG_WARNING(Service_JIT, "plugin GenerateCode callback failed");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ }
+ };
+
+ void Control(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_JIT, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto command{rp.PopRaw<u64>()};
+
+ // Optional input/output buffers
+ std::vector<u8> input_buffer{ctx.CanReadBuffer() ? ctx.ReadBuffer() : std::vector<u8>()};
+ std::vector<u8> output_buffer(ctx.CanWriteBuffer() ? ctx.GetWriteBufferSize() : 0);
+
+ // Function call prototype:
+ // u64 Control(s32* ret, JITConfiguration* cfg, u64 cmd, u8* input_buf, size_t input_size,
+ // u8* output_buf, size_t output_size);
+ //
+ // This function is used to set up the state of the plugin before code generation, generally
+ // passing objects like pointers to VM state from the game. It is usually called once.
+
+ const VAddr ret_ptr{context.AddHeap(0u)};
+ const VAddr configuration_ptr{context.AddHeap(configuration)};
+ const VAddr input_ptr{context.AddHeap(input_buffer.data(), input_buffer.size())};
+ const VAddr output_ptr{context.AddHeap(output_buffer.data(), output_buffer.size())};
+
+ const u64 wrapper_value{context.CallFunction(callbacks.Control, ret_ptr, configuration_ptr,
+ command, input_ptr, input_buffer.size(),
+ output_ptr, output_buffer.size())};
+
+ const s32 return_value{context.GetHeap<s32>(ret_ptr)};
+
+ if (wrapper_value == 0 && return_value == 0) {
+ // Write back to the IPC output buffer, if provided
+ if (ctx.CanWriteBuffer()) {
+ context.GetHeap(output_ptr, output_buffer.data(), output_buffer.size());
+ ctx.WriteBuffer(output_buffer.data(), output_buffer.size());
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(return_value);
+ } else {
+ LOG_WARNING(Service_JIT, "plugin Control callback failed");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ }
+ }
+
+ void LoadPlugin(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_JIT, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto tmem_size{rp.PopRaw<u64>()};
+ const auto tmem_handle{ctx.GetCopyHandle(0)};
+ const auto nro_plugin{ctx.ReadBuffer(1)};
+
+ if (tmem_size == 0) {
+ LOG_ERROR(Service_JIT, "attempted to load plugin with empty transfer memory");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ auto tmem{process->GetHandleTable().GetObject<Kernel::KTransferMemory>(tmem_handle)};
+ if (tmem.IsNull()) {
+ LOG_ERROR(Service_JIT, "attempted to load plugin with invalid transfer memory handle");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ // Set up the configuration with the required TransferMemory address
+ configuration.transfer_memory.offset = tmem->GetSourceAddress();
+ configuration.transfer_memory.size = tmem_size;
+
+ // Gather up all the callbacks from the loaded plugin
+ auto symbols{Core::Symbols::GetSymbols(nro_plugin, true)};
+ const auto GetSymbol{[&](const std::string& name) { return symbols[name].first; }};
+
+ callbacks.rtld_fini = GetSymbol("_fini");
+ callbacks.rtld_init = GetSymbol("_init");
+ callbacks.Control = GetSymbol("nnjitpluginControl");
+ callbacks.ResolveBasicSymbols = GetSymbol("nnjitpluginResolveBasicSymbols");
+ callbacks.SetupDiagnostics = GetSymbol("nnjitpluginSetupDiagnostics");
+ callbacks.Configure = GetSymbol("nnjitpluginConfigure");
+ callbacks.GenerateCode = GetSymbol("nnjitpluginGenerateCode");
+ callbacks.GetVersion = GetSymbol("nnjitpluginGetVersion");
+ callbacks.OnPrepared = GetSymbol("nnjitpluginOnPrepared");
+ callbacks.Keeper = GetSymbol("nnjitpluginKeeper");
+
+ if (callbacks.GetVersion == 0 || callbacks.Configure == 0 || callbacks.GenerateCode == 0 ||
+ callbacks.OnPrepared == 0) {
+ LOG_ERROR(Service_JIT, "plugin does not implement all necessary functionality");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ if (!context.LoadNRO(nro_plugin)) {
+ LOG_ERROR(Service_JIT, "failed to load plugin");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ context.MapProcessMemory(configuration.sys_ro_memory.offset,
+ configuration.sys_ro_memory.size);
+ context.MapProcessMemory(configuration.sys_rx_memory.offset,
+ configuration.sys_rx_memory.size);
+ context.MapProcessMemory(configuration.transfer_memory.offset,
+ configuration.transfer_memory.size);
+
+ // Run ELF constructors, if needed
+ if (callbacks.rtld_init != 0) {
+ context.CallFunction(callbacks.rtld_init);
+ }
+
+ // Function prototype:
+ // u64 GetVersion();
+ const auto version{context.CallFunction(callbacks.GetVersion)};
+ if (version != 1) {
+ LOG_ERROR(Service_JIT, "unknown plugin version {}", version);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ // Function prototype:
+ // void ResolveBasicSymbols(void (*resolver)(const char* name));
+ const auto resolve{context.GetHelper("_resolve")};
+ if (callbacks.ResolveBasicSymbols != 0) {
+ context.CallFunction(callbacks.ResolveBasicSymbols, resolve);
+ }
+
+ // Function prototype:
+ // void SetupDiagnostics(u32 enabled, void (**resolver)(const char* name));
+ const auto resolve_ptr{context.AddHeap(resolve)};
+ if (callbacks.SetupDiagnostics != 0) {
+ context.CallFunction(callbacks.SetupDiagnostics, 0u, resolve_ptr);
+ }
+
+ // Function prototype:
+ // void Configure(u32* memory_flags);
+ context.CallFunction(callbacks.Configure, 0ull);
+
+ // Function prototype:
+ // void OnPrepared(JITConfiguration* cfg);
+ const auto configuration_ptr{context.AddHeap(configuration)};
+ context.CallFunction(callbacks.OnPrepared, configuration_ptr);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetCodeAddress(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_JIT, "called");
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(ResultSuccess);
+ rb.Push(configuration.user_rx_memory.offset);
+ rb.Push(configuration.user_ro_memory.offset);
+ }
+
+private:
+ using Struct32 = std::array<u8, 32>;
+
+ struct GuestCallbacks {
+ VAddr rtld_fini;
+ VAddr rtld_init;
+ VAddr Control;
+ VAddr ResolveBasicSymbols;
+ VAddr SetupDiagnostics;
+ VAddr Configure;
+ VAddr GenerateCode;
+ VAddr GetVersion;
+ VAddr Keeper;
+ VAddr OnPrepared;
+ };
+
+ struct JITConfiguration {
+ CodeRange user_rx_memory;
+ CodeRange user_ro_memory;
+ CodeRange transfer_memory;
+ CodeRange sys_rx_memory;
+ CodeRange sys_ro_memory;
+ };
+
+ static CodeRange ClearSize(CodeRange in) {
+ in.size = 0;
+ return in;
+ }
+
+ Kernel::KScopedAutoObject<Kernel::KProcess> process;
+ GuestCallbacks callbacks;
+ JITConfiguration configuration;
+ JITContext context;
+};
+
+class JITU final : public ServiceFramework<JITU> {
+public:
+ explicit JITU(Core::System& system_) : ServiceFramework{system_, "jit:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &JITU::CreateJitEnvironment, "CreateJitEnvironment"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+ void CreateJitEnvironment(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_JIT, "called");
+
+ struct Parameters {
+ u64 rx_size;
+ u64 ro_size;
+ };
+
+ IPC::RequestParser rp{ctx};
+ const auto parameters{rp.PopRaw<Parameters>()};
+ const auto process_handle{ctx.GetCopyHandle(0)};
+ const auto rx_mem_handle{ctx.GetCopyHandle(1)};
+ const auto ro_mem_handle{ctx.GetCopyHandle(2)};
+
+ if (parameters.rx_size == 0 || parameters.ro_size == 0) {
+ LOG_ERROR(Service_JIT, "attempted to init with empty code regions");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ // Fetch using the handle table for the current process here,
+ // since we are not multiprocess yet.
+ const auto& handle_table{system.CurrentProcess()->GetHandleTable()};
+
+ auto process{handle_table.GetObject<Kernel::KProcess>(process_handle)};
+ if (process.IsNull()) {
+ LOG_ERROR(Service_JIT, "process is null for handle=0x{:08X}", process_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ auto rx_mem{handle_table.GetObject<Kernel::KCodeMemory>(rx_mem_handle)};
+ if (rx_mem.IsNull()) {
+ LOG_ERROR(Service_JIT, "rx_mem is null for handle=0x{:08X}", rx_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ auto ro_mem{handle_table.GetObject<Kernel::KCodeMemory>(ro_mem_handle)};
+ if (ro_mem.IsNull()) {
+ LOG_ERROR(Service_JIT, "ro_mem is null for handle=0x{:08X}", ro_mem_handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultUnknown);
+ return;
+ }
+
+ const CodeRange user_rx{
+ .offset = rx_mem->GetSourceAddress(),
+ .size = parameters.rx_size,
+ };
+
+ const CodeRange user_ro{
+ .offset = ro_mem->GetSourceAddress(),
+ .size = parameters.ro_size,
+ };
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IJitEnvironment>(system, *process, user_rx, user_ro);
+ }
+};
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
+ std::make_shared<JITU>(system)->InstallAsService(sm);
+}
+
+} // 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..af0f5b4f3
--- /dev/null
+++ b/src/core/hle/service/jit/jit.h
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::JIT {
+
+/// Registers all JIT services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+
+} // 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..4ed3f02e2
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.cpp
@@ -0,0 +1,426 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <array>
+#include <map>
+#include <span>
+#include <boost/icl/interval_set.hpp>
+#include <dynarmic/interface/A64/a64.h>
+#include <dynarmic/interface/A64/config.h>
+
+#include "common/alignment.h"
+#include "common/common_funcs.h"
+#include "common/div_ceil.h"
+#include "common/elf.h"
+#include "common/logging/log.h"
+#include "core/hle/service/jit/jit_context.h"
+#include "core/memory.h"
+
+using namespace Common::ELF;
+
+namespace Service::JIT {
+
+constexpr std::array<u8, 8> SVC0_ARM64 = {
+ 0x01, 0x00, 0x00, 0xd4, // svc #0
+ 0xc0, 0x03, 0x5f, 0xd6, // ret
+};
+
+constexpr std::array HELPER_FUNCTIONS{
+ "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset",
+};
+
+constexpr size_t STACK_ALIGN = 16;
+
+class JITContextImpl;
+
+using IntervalSet = boost::icl::interval_set<VAddr>::type;
+using IntervalType = boost::icl::interval_set<VAddr>::interval_type;
+
+class DynarmicCallbacks64 : public Dynarmic::A64::UserCallbacks {
+public:
+ explicit DynarmicCallbacks64(Core::Memory::Memory& memory_, std::vector<u8>& local_memory_,
+ IntervalSet& mapped_ranges_, JITContextImpl& parent_)
+ : memory{memory_}, local_memory{local_memory_},
+ mapped_ranges{mapped_ranges_}, parent{parent_} {}
+
+ u8 MemoryRead8(u64 vaddr) override {
+ return ReadMemory<u8>(vaddr);
+ }
+ u16 MemoryRead16(u64 vaddr) override {
+ return ReadMemory<u16>(vaddr);
+ }
+ u32 MemoryRead32(u64 vaddr) override {
+ return ReadMemory<u32>(vaddr);
+ }
+ u64 MemoryRead64(u64 vaddr) override {
+ return ReadMemory<u64>(vaddr);
+ }
+ u128 MemoryRead128(u64 vaddr) override {
+ return ReadMemory<u128>(vaddr);
+ }
+ std::string MemoryReadCString(u64 vaddr) {
+ std::string result;
+ u8 next;
+
+ while ((next = MemoryRead8(vaddr++)) != 0) {
+ result += next;
+ }
+
+ return result;
+ }
+
+ void MemoryWrite8(u64 vaddr, u8 value) override {
+ WriteMemory<u8>(vaddr, value);
+ }
+ void MemoryWrite16(u64 vaddr, u16 value) override {
+ WriteMemory<u16>(vaddr, value);
+ }
+ void MemoryWrite32(u64 vaddr, u32 value) override {
+ WriteMemory<u32>(vaddr, value);
+ }
+ void MemoryWrite64(u64 vaddr, u64 value) override {
+ WriteMemory<u64>(vaddr, value);
+ }
+ void MemoryWrite128(u64 vaddr, u128 value) override {
+ WriteMemory<u128>(vaddr, value);
+ }
+
+ bool MemoryWriteExclusive8(u64 vaddr, u8 value, u8) override {
+ return WriteMemory<u8>(vaddr, value);
+ }
+ bool MemoryWriteExclusive16(u64 vaddr, u16 value, u16) override {
+ return WriteMemory<u16>(vaddr, value);
+ }
+ bool MemoryWriteExclusive32(u64 vaddr, u32 value, u32) override {
+ return WriteMemory<u32>(vaddr, value);
+ }
+ bool MemoryWriteExclusive64(u64 vaddr, u64 value, u64) override {
+ return WriteMemory<u64>(vaddr, value);
+ }
+ bool MemoryWriteExclusive128(u64 vaddr, u128 value, u128) override {
+ return WriteMemory<u128>(vaddr, value);
+ }
+
+ void CallSVC(u32 swi) override;
+ void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override;
+ void InterpreterFallback(u64 pc, size_t num_instructions) override;
+
+ void AddTicks(u64 ticks) override {}
+ u64 GetTicksRemaining() override {
+ return std::numeric_limits<u32>::max();
+ }
+ u64 GetCNTPCT() override {
+ return 0;
+ }
+
+ template <class T>
+ T ReadMemory(u64 vaddr) {
+ T ret{};
+ if (boost::icl::contains(mapped_ranges, vaddr)) {
+ memory.ReadBlock(vaddr, &ret, sizeof(T));
+ } else if (vaddr + sizeof(T) > local_memory.size()) {
+ LOG_CRITICAL(Service_JIT, "plugin: unmapped read @ 0x{:016x}", vaddr);
+ } else {
+ std::memcpy(&ret, local_memory.data() + vaddr, sizeof(T));
+ }
+ return ret;
+ }
+
+ template <class T>
+ bool WriteMemory(u64 vaddr, const T value) {
+ if (boost::icl::contains(mapped_ranges, vaddr)) {
+ memory.WriteBlock(vaddr, &value, sizeof(T));
+ } else if (vaddr + sizeof(T) > local_memory.size()) {
+ LOG_CRITICAL(Service_JIT, "plugin: unmapped write @ 0x{:016x}", vaddr);
+ } else {
+ std::memcpy(local_memory.data() + vaddr, &value, sizeof(T));
+ }
+ return true;
+ }
+
+private:
+ Core::Memory::Memory& memory;
+ std::vector<u8>& local_memory;
+ IntervalSet& mapped_ranges;
+ JITContextImpl& parent;
+};
+
+class JITContextImpl {
+public:
+ explicit JITContextImpl(Core::Memory::Memory& memory_) : memory{memory_} {
+ callbacks =
+ std::make_unique<DynarmicCallbacks64>(memory, local_memory, mapped_ranges, *this);
+ user_config.callbacks = callbacks.get();
+ jit = std::make_unique<Dynarmic::A64::Jit>(user_config);
+ }
+
+ bool LoadNRO(std::span<const u8> data) {
+ local_memory.clear();
+ local_memory.insert(local_memory.end(), data.begin(), data.end());
+
+ if (FixupRelocations()) {
+ InsertHelperFunctions();
+ InsertStack();
+ return true;
+ }
+
+ return false;
+ }
+
+ bool FixupRelocations() {
+ // The loaded NRO file has ELF relocations that must be processed before it can run.
+ // Normally this would be processed by RTLD, but in HLE context, we don't have
+ // the linker available, so we have to do it ourselves.
+
+ const VAddr mod_offset{callbacks->MemoryRead32(4)};
+ if (callbacks->MemoryRead32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
+ return false;
+ }
+
+ // For more info about dynamic entries, see the ELF ABI specification:
+ // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html
+ // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html
+ VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)};
+ VAddr rela_dyn = 0;
+ size_t num_rela = 0;
+ while (true) {
+ const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)};
+ dynamic_offset += sizeof(Elf64_Dyn);
+
+ if (!dyn.d_tag) {
+ break;
+ }
+ if (dyn.d_tag == ElfDtRela) {
+ rela_dyn = dyn.d_un.d_ptr;
+ }
+ if (dyn.d_tag == ElfDtRelasz) {
+ num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela);
+ }
+ }
+
+ for (size_t i = 0; i < num_rela; i++) {
+ const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))};
+ if (Elf64RelType(rela.r_info) != ElfAArch64Relative) {
+ continue;
+ }
+ const VAddr contents{callbacks->MemoryRead64(rela.r_offset)};
+ callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend);
+ }
+
+ return true;
+ }
+
+ void InsertHelperFunctions() {
+ for (const auto& name : HELPER_FUNCTIONS) {
+ helpers[name] = local_memory.size();
+ local_memory.insert(local_memory.end(), SVC0_ARM64.begin(), SVC0_ARM64.end());
+ }
+ }
+
+ void InsertStack() {
+ // Allocate enough space to avoid any reasonable risk of
+ // overflowing the stack during plugin execution
+ const u64 pad_amount{Common::AlignUp(local_memory.size(), STACK_ALIGN) -
+ local_memory.size()};
+ local_memory.insert(local_memory.end(), 0x10000 + pad_amount, 0);
+ top_of_stack = local_memory.size();
+ heap_pointer = top_of_stack;
+ }
+
+ void MapProcessMemory(VAddr dest_address, std::size_t size) {
+ mapped_ranges.add(IntervalType{dest_address, dest_address + size});
+ }
+
+ void PushArgument(const void* data, size_t size) {
+ const size_t num_words = Common::DivCeil(size, sizeof(u64));
+ const size_t current_pos = argument_stack.size();
+ argument_stack.insert(argument_stack.end(), num_words, 0);
+ std::memcpy(argument_stack.data() + current_pos, data, size);
+ }
+
+ void SetupArguments() {
+ // The first 8 integer registers are used for the first 8 integer
+ // arguments. Floating-point arguments are not handled at this time.
+ //
+ // If a function takes more than 8 arguments, then stack space is reserved
+ // for the remaining arguments, and the remaining arguments are inserted in
+ // ascending memory order, each argument aligned to an 8-byte boundary. The
+ // stack pointer must remain aligned to 16 bytes.
+ //
+ // For more info, see the AArch64 ABI PCS:
+ // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
+
+ for (size_t i = 0; i < 8 && i < argument_stack.size(); i++) {
+ jit->SetRegister(i, argument_stack[i]);
+ }
+
+ if (argument_stack.size() > 8) {
+ const VAddr new_sp = Common::AlignDown(
+ top_of_stack - (argument_stack.size() - 8) * sizeof(u64), STACK_ALIGN);
+ for (size_t i = 8; i < argument_stack.size(); i++) {
+ callbacks->MemoryWrite64(new_sp + (i - 8) * sizeof(u64), argument_stack[i]);
+ }
+ jit->SetSP(new_sp);
+ }
+
+ // Reset the call state for the next invocation
+ argument_stack.clear();
+ heap_pointer = top_of_stack;
+ }
+
+ u64 CallFunction(VAddr func) {
+ jit->SetRegister(30, helpers["_stop"]);
+ jit->SetSP(top_of_stack);
+ SetupArguments();
+
+ jit->SetPC(func);
+ jit->Run();
+ return jit->GetRegister(0);
+ }
+
+ VAddr GetHelper(const std::string& name) {
+ return helpers[name];
+ }
+
+ VAddr AddHeap(const void* data, size_t size) {
+ // Require all heap data types to have the same alignment as the
+ // stack pointer, for compatibility
+ const size_t num_bytes{Common::AlignUp(size, STACK_ALIGN)};
+
+ // Make additional memory space if required
+ if (heap_pointer + num_bytes > local_memory.size()) {
+ local_memory.insert(local_memory.end(),
+ (heap_pointer + num_bytes) - local_memory.size(), 0);
+ }
+
+ const VAddr location{heap_pointer};
+ std::memcpy(local_memory.data() + location, data, size);
+ heap_pointer += num_bytes;
+ return location;
+ }
+
+ void GetHeap(VAddr location, void* data, size_t size) {
+ std::memcpy(data, local_memory.data() + location, size);
+ }
+
+ std::unique_ptr<DynarmicCallbacks64> callbacks;
+ std::vector<u8> local_memory;
+ std::vector<u64> argument_stack;
+ IntervalSet mapped_ranges;
+ Dynarmic::A64::UserConfig user_config;
+ std::unique_ptr<Dynarmic::A64::Jit> jit;
+ std::map<std::string, VAddr, std::less<>> helpers;
+ Core::Memory::Memory& memory;
+ VAddr top_of_stack;
+ VAddr heap_pointer;
+};
+
+void DynarmicCallbacks64::CallSVC(u32 swi) {
+ // Service calls are used to implement helper functionality.
+ //
+ // The most important of these is the _stop helper, which transfers control
+ // from the plugin back to HLE context to return a value. However, a few more
+ // are also implemented to reduce the need for direct ARM implementations of
+ // basic functionality, like memory operations.
+ //
+ // When we receive a helper request, the swi number will be zero, and the call
+ // will have originated from an address we know is a helper function. Otherwise,
+ // the plugin may be trying to issue a service call, which we shouldn't handle.
+
+ if (swi != 0) {
+ LOG_CRITICAL(Service_JIT, "plugin issued unknown service call {}", swi);
+ parent.jit->HaltExecution();
+ return;
+ }
+
+ u64 pc{parent.jit->GetPC() - 4};
+ auto& helpers{parent.helpers};
+
+ if (pc == helpers["memcpy"] || pc == helpers["memmove"]) {
+ const VAddr dest{parent.jit->GetRegister(0)};
+ const VAddr src{parent.jit->GetRegister(1)};
+ const size_t n{parent.jit->GetRegister(2)};
+
+ if (dest < src) {
+ for (size_t i = 0; i < n; i++) {
+ MemoryWrite8(dest + i, MemoryRead8(src + i));
+ }
+ } else {
+ for (size_t i = n; i > 0; i--) {
+ MemoryWrite8(dest + i - 1, MemoryRead8(src + i - 1));
+ }
+ }
+ } else if (pc == helpers["memset"]) {
+ const VAddr dest{parent.jit->GetRegister(0)};
+ const u64 c{parent.jit->GetRegister(1)};
+ const size_t n{parent.jit->GetRegister(2)};
+
+ for (size_t i = 0; i < n; i++) {
+ MemoryWrite8(dest + i, static_cast<u8>(c));
+ }
+ } else if (pc == helpers["_resolve"]) {
+ // X0 contains a char* for a symbol to resolve
+ const auto name{MemoryReadCString(parent.jit->GetRegister(0))};
+ const auto helper{helpers[name]};
+
+ if (helper != 0) {
+ parent.jit->SetRegister(0, helper);
+ } else {
+ LOG_WARNING(Service_JIT, "plugin requested unknown function {}", name);
+ parent.jit->SetRegister(0, helpers["_panic"]);
+ }
+ } else if (pc == helpers["_stop"]) {
+ parent.jit->HaltExecution();
+ } else if (pc == helpers["_panic"]) {
+ LOG_CRITICAL(Service_JIT, "plugin panicked!");
+ parent.jit->HaltExecution();
+ } else {
+ LOG_CRITICAL(Service_JIT, "plugin issued syscall at unknown address 0x{:x}", pc);
+ parent.jit->HaltExecution();
+ }
+}
+
+void DynarmicCallbacks64::ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) {
+ LOG_CRITICAL(Service_JIT, "Illegal operation PC @ {:08x}", pc);
+ parent.jit->HaltExecution();
+}
+
+void DynarmicCallbacks64::InterpreterFallback(u64 pc, size_t num_instructions) {
+ LOG_CRITICAL(Service_JIT, "Unimplemented instruction PC @ {:08x}", pc);
+ parent.jit->HaltExecution();
+}
+
+JITContext::JITContext(Core::Memory::Memory& memory)
+ : impl{std::make_unique<JITContextImpl>(memory)} {}
+
+JITContext::~JITContext() {}
+
+bool JITContext::LoadNRO(std::span<const u8> data) {
+ return impl->LoadNRO(data);
+}
+
+void JITContext::MapProcessMemory(VAddr dest_address, std::size_t size) {
+ impl->MapProcessMemory(dest_address, size);
+}
+
+u64 JITContext::CallFunction(VAddr func) {
+ return impl->CallFunction(func);
+}
+
+void JITContext::PushArgument(const void* data, size_t size) {
+ impl->PushArgument(data, size);
+}
+
+VAddr JITContext::GetHelper(const std::string& name) {
+ return impl->GetHelper(name);
+}
+
+VAddr JITContext::AddHeap(const void* data, size_t size) {
+ return impl->AddHeap(data, size);
+}
+
+void JITContext::GetHeap(VAddr location, void* data, size_t size) {
+ impl->GetHeap(location, data, size);
+}
+
+} // 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..f17fc5e24
--- /dev/null
+++ b/src/core/hle/service/jit/jit_context.h
@@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <memory>
+#include <span>
+#include <string>
+
+#include "common/common_types.h"
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace Service::JIT {
+
+class JITContextImpl;
+
+class JITContext {
+public:
+ explicit JITContext(Core::Memory::Memory& memory);
+ ~JITContext();
+
+ [[nodiscard]] bool LoadNRO(std::span<const u8> data);
+ void MapProcessMemory(VAddr dest_address, std::size_t size);
+
+ template <typename T, typename... Ts>
+ u64 CallFunction(VAddr func, T argument, Ts... rest) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ static_assert(!std::is_floating_point_v<T>);
+ PushArgument(&argument, sizeof(argument));
+
+ if constexpr (sizeof...(rest) > 0) {
+ return CallFunction(func, rest...);
+ } else {
+ return CallFunction(func);
+ }
+ }
+
+ u64 CallFunction(VAddr func);
+ VAddr GetHelper(const std::string& name);
+
+ template <typename T>
+ VAddr AddHeap(T argument) {
+ return AddHeap(&argument, sizeof(argument));
+ }
+ VAddr AddHeap(const void* data, size_t size);
+
+ template <typename T>
+ T GetHeap(VAddr location) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ T result;
+ GetHeap(location, &result, sizeof(result));
+ return result;
+ }
+ void GetHeap(VAddr location, void* data, size_t size);
+
+private:
+ std::unique_ptr<JITContextImpl> impl;
+
+ void PushArgument(const void* data, size_t size);
+};
+
+} // namespace Service::JIT
diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp
index 62f4cdfb2..3e317367b 100644
--- a/src/core/hle/service/kernel_helpers.cpp
+++ b/src/core/hle/service/kernel_helpers.cpp
@@ -1,9 +1,10 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_memory_manager.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_resource_limit.h"
@@ -15,9 +16,11 @@ namespace Service::KernelHelpers {
ServiceContext::ServiceContext(Core::System& system_, std::string name_)
: kernel(system_.Kernel()) {
+ // Create the process.
process = Kernel::KProcess::Create(kernel);
ASSERT(Kernel::KProcess::Initialize(process, system_, std::move(name_),
- Kernel::KProcess::ProcessType::Userland)
+ Kernel::KProcess::ProcessType::KernelInternal,
+ kernel.GetSystemResourceLimit())
.IsSuccess());
}
@@ -43,7 +46,7 @@ Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) {
}
// Initialize the event.
- event->Initialize(std::move(name));
+ event->Initialize(std::move(name), process);
// Commit the thread reservation.
event_reservation.Commit();
diff --git a/src/core/hle/service/kernel_helpers.h b/src/core/hle/service/kernel_helpers.h
index 4f3e95f67..6415838e5 100644
--- a/src/core/hle/service/kernel_helpers.h
+++ b/src/core/hle/service/kernel_helpers.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
index 5c8ae029c..c8415e0bf 100644
--- a/src/core/hle/service/lbl/lbl.cpp
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cmath>
#include <memory>
diff --git a/src/core/hle/service/lbl/lbl.h b/src/core/hle/service/lbl/lbl.h
index 9c2021026..6484105c2 100644
--- a/src/core/hle/service/lbl/lbl.h
+++ b/src/core/hle/service/lbl/lbl.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h
deleted file mode 100644
index a718c5c66..000000000
--- a/src/core/hle/service/ldn/errors.h
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "core/hle/result.h"
-
-namespace Service::LDN {
-
-constexpr ResultCode ERROR_DISABLED{ErrorModule::LDN, 22};
-
-} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp
new file mode 100644
index 000000000..8f3c04550
--- /dev/null
+++ b/src/core/hle/service/ldn/lan_discovery.cpp
@@ -0,0 +1,633 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/service/ldn/lan_discovery.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+
+namespace Service::LDN {
+
+LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_)
+ : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_),
+ discovery(discovery_) {}
+
+LanStation::~LanStation() = default;
+
+NodeStatus LanStation::GetStatus() const {
+ return status;
+}
+
+void LanStation::OnClose() {
+ LOG_INFO(Service_LDN, "OnClose {}", node_id);
+ Reset();
+ discovery->UpdateNodes();
+}
+
+void LanStation::Reset() {
+ status = NodeStatus::Disconnected;
+};
+
+void LanStation::OverrideInfo() {
+ bool connected = GetStatus() == NodeStatus::Connected;
+ node_info->node_id = node_id;
+ node_info->is_connected = connected ? 1 : 0;
+}
+
+LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_)
+ : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}),
+ room_network{room_network_} {}
+
+LANDiscovery::~LANDiscovery() {
+ if (inited) {
+ Result rc = Finalize();
+ LOG_INFO(Service_LDN, "Finalize: {}", rc.raw);
+ }
+}
+
+void LANDiscovery::InitNetworkInfo() {
+ network_info.common.bssid = GetFakeMac();
+ network_info.common.channel = WifiChannel::Wifi24_6;
+ network_info.common.link_level = LinkLevel::Good;
+ network_info.common.network_type = PackedNetworkType::Ldn;
+ network_info.common.ssid = fake_ssid;
+
+ auto& nodes = network_info.ldn.nodes;
+ for (std::size_t i = 0; i < NodeCountMax; i++) {
+ nodes[i].node_id = static_cast<s8>(i);
+ nodes[i].is_connected = 0;
+ }
+}
+
+void LANDiscovery::InitNodeStateChange() {
+ for (auto& node_update : node_changes) {
+ node_update.state_change = NodeStateChange::None;
+ }
+ for (auto& node_state : node_last_states) {
+ node_state = 0;
+ }
+}
+
+State LANDiscovery::GetState() const {
+ return state;
+}
+
+void LANDiscovery::SetState(State new_state) {
+ state = new_state;
+}
+
+Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const {
+ if (state == State::AccessPointCreated || state == State::StationConnected) {
+ std::memcpy(&out_network, &network_info, sizeof(network_info));
+ return ResultSuccess;
+ }
+
+ return ResultBadState;
+}
+
+Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
+ std::vector<NodeLatestUpdate>& out_updates,
+ std::size_t buffer_count) {
+ if (buffer_count > NodeCountMax) {
+ return ResultInvalidBufferCount;
+ }
+
+ if (state == State::AccessPointCreated || state == State::StationConnected) {
+ std::memcpy(&out_network, &network_info, sizeof(network_info));
+ for (std::size_t i = 0; i < buffer_count; i++) {
+ out_updates[i].state_change = node_changes[i].state_change;
+ node_changes[i].state_change = NodeStateChange::None;
+ }
+ return ResultSuccess;
+ }
+
+ return ResultBadState;
+}
+
+DisconnectReason LANDiscovery::GetDisconnectReason() const {
+ return disconnect_reason;
+}
+
+Result LANDiscovery::Scan(std::vector<NetworkInfo>& networks, u16& count,
+ const ScanFilter& filter) {
+ if (!IsFlagSet(filter.flag, ScanFilterFlag::NetworkType) ||
+ filter.network_type <= NetworkType::All) {
+ if (!IsFlagSet(filter.flag, ScanFilterFlag::Ssid) && filter.ssid.length >= SsidLengthMax) {
+ return ResultBadInput;
+ }
+ }
+
+ {
+ std::scoped_lock lock{packet_mutex};
+ scan_results.clear();
+
+ SendBroadcast(Network::LDNPacketType::Scan);
+ }
+
+ LOG_INFO(Service_LDN, "Waiting for scan replies");
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ std::scoped_lock lock{packet_mutex};
+ for (const auto& [key, info] : scan_results) {
+ if (count >= networks.size()) {
+ break;
+ }
+
+ if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) {
+ if (filter.network_id.intent_id.local_communication_id !=
+ info.network_id.intent_id.local_communication_id) {
+ continue;
+ }
+ }
+ if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) {
+ if (filter.network_id.session_id != info.network_id.session_id) {
+ continue;
+ }
+ }
+ if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) {
+ if (filter.network_type != static_cast<NetworkType>(info.common.network_type)) {
+ continue;
+ }
+ }
+ if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) {
+ if (filter.ssid != info.common.ssid) {
+ continue;
+ }
+ }
+ if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) {
+ if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) {
+ continue;
+ }
+ }
+
+ networks[count++] = info;
+ }
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::SetAdvertiseData(std::span<const u8> data) {
+ std::scoped_lock lock{packet_mutex};
+ const std::size_t size = data.size();
+ if (size > AdvertiseDataSizeMax) {
+ return ResultAdvertiseDataTooLarge;
+ }
+
+ std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size);
+ network_info.ldn.advertise_data_size = static_cast<u16>(size);
+
+ UpdateNodes();
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::OpenAccessPoint() {
+ std::scoped_lock lock{packet_mutex};
+ disconnect_reason = DisconnectReason::None;
+ if (state == State::None) {
+ return ResultBadState;
+ }
+
+ ResetStations();
+ SetState(State::AccessPointOpened);
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::CloseAccessPoint() {
+ std::scoped_lock lock{packet_mutex};
+ if (state == State::None) {
+ return ResultBadState;
+ }
+
+ if (state == State::AccessPointCreated) {
+ DestroyNetwork();
+ }
+
+ ResetStations();
+ SetState(State::Initialized);
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::OpenStation() {
+ std::scoped_lock lock{packet_mutex};
+ disconnect_reason = DisconnectReason::None;
+ if (state == State::None) {
+ return ResultBadState;
+ }
+
+ ResetStations();
+ SetState(State::StationOpened);
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::CloseStation() {
+ std::scoped_lock lock{packet_mutex};
+ if (state == State::None) {
+ return ResultBadState;
+ }
+
+ if (state == State::StationConnected) {
+ Disconnect();
+ }
+
+ ResetStations();
+ SetState(State::Initialized);
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config,
+ const UserConfig& user_config,
+ const NetworkConfig& network_config) {
+ std::scoped_lock lock{packet_mutex};
+
+ if (state != State::AccessPointOpened) {
+ return ResultBadState;
+ }
+
+ InitNetworkInfo();
+ network_info.ldn.node_count_max = network_config.node_count_max;
+ network_info.ldn.security_mode = security_config.security_mode;
+
+ if (network_config.channel == WifiChannel::Default) {
+ network_info.common.channel = WifiChannel::Wifi24_6;
+ } else {
+ network_info.common.channel = network_config.channel;
+ }
+
+ std::independent_bits_engine<std::mt19937, 64, u64> bits_engine;
+ network_info.network_id.session_id.high = bits_engine();
+ network_info.network_id.session_id.low = bits_engine();
+ network_info.network_id.intent_id = network_config.intent_id;
+
+ NodeInfo& node0 = network_info.ldn.nodes[0];
+ const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version);
+ if (rc2.IsError()) {
+ return ResultAccessPointConnectionFailed;
+ }
+
+ SetState(State::AccessPointCreated);
+
+ InitNodeStateChange();
+ node0.is_connected = 1;
+ UpdateNodes();
+
+ return rc2;
+}
+
+Result LANDiscovery::DestroyNetwork() {
+ for (auto local_ip : connected_clients) {
+ SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip);
+ }
+
+ ResetStations();
+
+ SetState(State::AccessPointOpened);
+ lan_event();
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config,
+ u16 local_communication_version) {
+ std::scoped_lock lock{packet_mutex};
+ if (network_info_.ldn.node_count == 0) {
+ return ResultInvalidNodeCount;
+ }
+
+ Result rc = GetNodeInfo(node_info, user_config, local_communication_version);
+ if (rc.IsError()) {
+ return ResultConnectionFailed;
+ }
+
+ Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address;
+ std::reverse(std::begin(node_host), std::end(node_host)); // htonl
+ host_ip = node_host;
+ SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip);
+
+ InitNodeStateChange();
+
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::Disconnect() {
+ if (host_ip) {
+ SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip);
+ }
+
+ SetState(State::StationOpened);
+ lan_event();
+
+ return ResultSuccess;
+}
+
+Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) {
+ std::scoped_lock lock{packet_mutex};
+ if (inited) {
+ return ResultSuccess;
+ }
+
+ for (auto& station : stations) {
+ station.discovery = this;
+ station.node_info = &network_info.ldn.nodes[station.node_id];
+ station.Reset();
+ }
+
+ connected_clients.clear();
+ lan_event = lan_event_;
+
+ SetState(State::Initialized);
+
+ inited = true;
+ return ResultSuccess;
+}
+
+Result LANDiscovery::Finalize() {
+ std::scoped_lock lock{packet_mutex};
+ Result rc = ResultSuccess;
+
+ if (inited) {
+ if (state == State::AccessPointCreated) {
+ DestroyNetwork();
+ }
+ if (state == State::StationConnected) {
+ Disconnect();
+ }
+
+ ResetStations();
+ inited = false;
+ }
+
+ SetState(State::None);
+
+ return rc;
+}
+
+void LANDiscovery::ResetStations() {
+ for (auto& station : stations) {
+ station.Reset();
+ }
+ connected_clients.clear();
+}
+
+void LANDiscovery::UpdateNodes() {
+ u8 count = 0;
+ for (auto& station : stations) {
+ bool connected = station.GetStatus() == NodeStatus::Connected;
+ if (connected) {
+ count++;
+ }
+ station.OverrideInfo();
+ }
+ network_info.ldn.node_count = count + 1;
+
+ for (auto local_ip : connected_clients) {
+ SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip);
+ }
+
+ OnNetworkInfoChanged();
+}
+
+void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) {
+ network_info = info;
+ if (state == State::StationOpened) {
+ SetState(State::StationConnected);
+ }
+ OnNetworkInfoChanged();
+}
+
+void LANDiscovery::OnDisconnectFromHost() {
+ LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast<int>(state));
+ host_ip = std::nullopt;
+ if (state == State::StationConnected) {
+ SetState(State::StationOpened);
+ lan_event();
+ }
+}
+
+void LANDiscovery::OnNetworkInfoChanged() {
+ if (IsNodeStateChanged()) {
+ lan_event();
+ }
+ return;
+}
+
+Network::IPv4Address LANDiscovery::GetLocalIp() const {
+ Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF};
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ local_ip = room_member->GetFakeIpAddress();
+ }
+ }
+ return local_ip;
+}
+
+template <typename Data>
+void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data,
+ Ipv4Address remote_ip) {
+ Network::LDNPacket packet;
+ packet.type = type;
+
+ packet.broadcast = false;
+ packet.local_ip = GetLocalIp();
+ packet.remote_ip = remote_ip;
+
+ packet.data.resize(sizeof(data));
+ std::memcpy(packet.data.data(), &data, sizeof(data));
+ SendPacket(packet);
+}
+
+void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) {
+ Network::LDNPacket packet;
+ packet.type = type;
+
+ packet.broadcast = false;
+ packet.local_ip = GetLocalIp();
+ packet.remote_ip = remote_ip;
+
+ SendPacket(packet);
+}
+
+template <typename Data>
+void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) {
+ Network::LDNPacket packet;
+ packet.type = type;
+
+ packet.broadcast = true;
+ packet.local_ip = GetLocalIp();
+
+ packet.data.resize(sizeof(data));
+ std::memcpy(packet.data.data(), &data, sizeof(data));
+ SendPacket(packet);
+}
+
+void LANDiscovery::SendBroadcast(Network::LDNPacketType type) {
+ Network::LDNPacket packet;
+ packet.type = type;
+
+ packet.broadcast = true;
+ packet.local_ip = GetLocalIp();
+
+ SendPacket(packet);
+}
+
+void LANDiscovery::SendPacket(const Network::LDNPacket& packet) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ room_member->SendLdnPacket(packet);
+ }
+ }
+}
+
+void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) {
+ std::scoped_lock lock{packet_mutex};
+ switch (packet.type) {
+ case Network::LDNPacketType::Scan: {
+ LOG_INFO(Frontend, "Scan packet received!");
+ if (state == State::AccessPointCreated) {
+ // Reply to the sender
+ SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip);
+ }
+ break;
+ }
+ case Network::LDNPacketType::ScanResp: {
+ LOG_INFO(Frontend, "ScanResp packet received!");
+
+ NetworkInfo info{};
+ std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));
+ scan_results.insert({info.common.bssid, info});
+
+ break;
+ }
+ case Network::LDNPacketType::Connect: {
+ LOG_INFO(Frontend, "Connect packet received!");
+
+ NodeInfo info{};
+ std::memcpy(&info, packet.data.data(), sizeof(NodeInfo));
+
+ connected_clients.push_back(packet.local_ip);
+
+ for (LanStation& station : stations) {
+ if (station.status != NodeStatus::Connected) {
+ *station.node_info = info;
+ station.status = NodeStatus::Connected;
+ break;
+ }
+ }
+
+ UpdateNodes();
+
+ break;
+ }
+ case Network::LDNPacketType::Disconnect: {
+ LOG_INFO(Frontend, "Disconnect packet received!");
+
+ connected_clients.erase(
+ std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip),
+ connected_clients.end());
+
+ NodeInfo info{};
+ std::memcpy(&info, packet.data.data(), sizeof(NodeInfo));
+
+ for (LanStation& station : stations) {
+ if (station.status == NodeStatus::Connected &&
+ station.node_info->mac_address == info.mac_address) {
+ station.OnClose();
+ break;
+ }
+ }
+
+ break;
+ }
+ case Network::LDNPacketType::DestroyNetwork: {
+ ResetStations();
+ OnDisconnectFromHost();
+ break;
+ }
+ case Network::LDNPacketType::SyncNetwork: {
+ if (state == State::StationOpened || state == State::StationConnected) {
+ LOG_INFO(Frontend, "SyncNetwork packet received!");
+ NetworkInfo info{};
+ std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));
+
+ OnSyncNetwork(info);
+ } else {
+ LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!");
+ }
+
+ break;
+ }
+ default: {
+ LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast<int>(packet.type));
+ break;
+ }
+ }
+}
+
+bool LANDiscovery::IsNodeStateChanged() {
+ bool changed = false;
+ const auto& nodes = network_info.ldn.nodes;
+ for (int i = 0; i < NodeCountMax; i++) {
+ if (nodes[i].is_connected != node_last_states[i]) {
+ if (nodes[i].is_connected) {
+ node_changes[i].state_change |= NodeStateChange::Connect;
+ } else {
+ node_changes[i].state_change |= NodeStateChange::Disconnect;
+ }
+ node_last_states[i] = nodes[i].is_connected;
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const {
+ const auto flag_value = static_cast<u32>(flag);
+ const auto search_flag_value = static_cast<u32>(search_flag);
+ return (flag_value & search_flag_value) == search_flag_value;
+}
+
+int LANDiscovery::GetStationCount() const {
+ return static_cast<int>(
+ std::count_if(stations.begin(), stations.end(), [](const auto& station) {
+ return station.GetStatus() != NodeStatus::Disconnected;
+ }));
+}
+
+MacAddress LANDiscovery::GetFakeMac() const {
+ MacAddress mac{};
+ mac.raw[0] = 0x02;
+ mac.raw[1] = 0x00;
+
+ const auto ip = GetLocalIp();
+ memcpy(mac.raw.data() + 2, &ip, sizeof(ip));
+
+ return mac;
+}
+
+Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig,
+ u16 localCommunicationVersion) {
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+
+ if (!network_interface) {
+ LOG_ERROR(Service_LDN, "No network interface available");
+ return ResultNoIpAddress;
+ }
+
+ node.mac_address = GetFakeMac();
+ node.is_connected = 1;
+ std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1);
+ node.local_communication_version = localCommunicationVersion;
+
+ Ipv4Address current_address = GetLocalIp();
+ std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
+ node.ipv4_address = current_address;
+
+ return ResultSuccess;
+}
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h
new file mode 100644
index 000000000..3833cd764
--- /dev/null
+++ b/src/core/hle/service/ldn/lan_discovery.h
@@ -0,0 +1,134 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <cstring>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <random>
+#include <span>
+#include <thread>
+#include <unordered_map>
+
+#include "common/logging/log.h"
+#include "common/socket_types.h"
+#include "core/hle/result.h"
+#include "core/hle/service/ldn/ldn_results.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "network/network.h"
+
+namespace Service::LDN {
+
+class LANDiscovery;
+
+class LanStation {
+public:
+ LanStation(s8 node_id_, LANDiscovery* discovery_);
+ ~LanStation();
+
+ void OnClose();
+ NodeStatus GetStatus() const;
+ void Reset();
+ void OverrideInfo();
+
+protected:
+ friend class LANDiscovery;
+ NodeInfo* node_info;
+ NodeStatus status;
+ s8 node_id;
+ LANDiscovery* discovery;
+};
+
+class LANDiscovery {
+public:
+ using LanEventFunc = std::function<void()>;
+
+ LANDiscovery(Network::RoomNetwork& room_network_);
+ ~LANDiscovery();
+
+ State GetState() const;
+ void SetState(State new_state);
+
+ Result GetNetworkInfo(NetworkInfo& out_network) const;
+ Result GetNetworkInfo(NetworkInfo& out_network, std::vector<NodeLatestUpdate>& out_updates,
+ std::size_t buffer_count);
+
+ DisconnectReason GetDisconnectReason() const;
+ Result Scan(std::vector<NetworkInfo>& networks, u16& count, const ScanFilter& filter);
+ Result SetAdvertiseData(std::span<const u8> data);
+
+ Result OpenAccessPoint();
+ Result CloseAccessPoint();
+
+ Result OpenStation();
+ Result CloseStation();
+
+ Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config,
+ const NetworkConfig& network_config);
+ Result DestroyNetwork();
+
+ Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config,
+ u16 local_communication_version);
+ Result Disconnect();
+
+ Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true);
+ Result Finalize();
+
+ void ReceivePacket(const Network::LDNPacket& packet);
+
+protected:
+ friend class LanStation;
+
+ void InitNetworkInfo();
+ void InitNodeStateChange();
+
+ void ResetStations();
+ void UpdateNodes();
+
+ void OnSyncNetwork(const NetworkInfo& info);
+ void OnDisconnectFromHost();
+ void OnNetworkInfoChanged();
+
+ bool IsNodeStateChanged();
+ bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const;
+ int GetStationCount() const;
+ MacAddress GetFakeMac() const;
+ Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config,
+ u16 local_communication_version);
+
+ Network::IPv4Address GetLocalIp() const;
+ template <typename Data>
+ void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip);
+ void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip);
+ template <typename Data>
+ void SendBroadcast(Network::LDNPacketType type, const Data& data);
+ void SendBroadcast(Network::LDNPacketType type);
+ void SendPacket(const Network::LDNPacket& packet);
+
+ static const LanEventFunc empty_func;
+ static constexpr Ssid fake_ssid{"YuzuFakeSsidForLdn"};
+
+ bool inited{};
+ std::mutex packet_mutex;
+ std::array<LanStation, StationCountMax> stations;
+ std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
+ std::array<u8, NodeCountMax> node_last_states{};
+ std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
+ NodeInfo node_info{};
+ NetworkInfo network_info{};
+ State state{State::None};
+ DisconnectReason disconnect_reason{DisconnectReason::None};
+
+ // TODO (flTobi): Should this be an std::set?
+ std::vector<Ipv4Address> connected_clients;
+ std::optional<Ipv4Address> host_ip;
+
+ LanEventFunc lan_event;
+
+ Network::RoomNetwork& room_network;
+};
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 6ccbe9623..ea3e7e55a 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -1,14 +1,19 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/result.h"
-#include "core/hle/service/ldn/errors.h"
+#include "core/core.h"
+#include "core/hle/service/ldn/lan_discovery.h"
#include "core/hle/service/ldn/ldn.h"
-#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/ldn/ldn_results.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+#include "network/network.h"
+
+// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
+#undef CreateEvent
namespace Service::LDN {
@@ -101,74 +106,445 @@ class IUserLocalCommunicationService final
: public ServiceFramework<IUserLocalCommunicationService> {
public:
explicit IUserLocalCommunicationService(Core::System& system_)
- : ServiceFramework{system_, "IUserLocalCommunicationService"} {
+ : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew},
+ service_context{system, "IUserLocalCommunicationService"},
+ room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IUserLocalCommunicationService::GetState, "GetState"},
- {1, nullptr, "GetNetworkInfo"},
- {2, nullptr, "GetIpv4Address"},
- {3, nullptr, "GetDisconnectReason"},
- {4, nullptr, "GetSecurityParameter"},
- {5, nullptr, "GetNetworkConfig"},
- {100, nullptr, "AttachStateChangeEvent"},
- {101, nullptr, "GetNetworkInfoLatestUpdate"},
- {102, nullptr, "Scan"},
- {103, nullptr, "ScanPrivate"},
- {104, nullptr, "SetWirelessControllerRestriction"},
- {200, nullptr, "OpenAccessPoint"},
- {201, nullptr, "CloseAccessPoint"},
- {202, nullptr, "CreateNetwork"},
- {203, nullptr, "CreateNetworkPrivate"},
- {204, nullptr, "DestroyNetwork"},
+ {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
+ {2, &IUserLocalCommunicationService::GetIpv4Address, "GetIpv4Address"},
+ {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
+ {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
+ {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
+ {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"},
+ {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
+ {102, &IUserLocalCommunicationService::Scan, "Scan"},
+ {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
+ {104, &IUserLocalCommunicationService::SetWirelessControllerRestriction, "SetWirelessControllerRestriction"},
+ {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
+ {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
+ {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
+ {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"},
+ {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"},
{205, nullptr, "Reject"},
- {206, nullptr, "SetAdvertiseData"},
- {207, nullptr, "SetStationAcceptPolicy"},
- {208, nullptr, "AddAcceptFilterEntry"},
+ {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"},
+ {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"},
+ {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"},
{209, nullptr, "ClearAcceptFilter"},
- {300, nullptr, "OpenStation"},
- {301, nullptr, "CloseStation"},
- {302, nullptr, "Connect"},
+ {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"},
+ {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"},
+ {302, &IUserLocalCommunicationService::Connect, "Connect"},
{303, nullptr, "ConnectPrivate"},
- {304, nullptr, "Disconnect"},
- {400, nullptr, "Initialize"},
- {401, nullptr, "Finalize"},
- {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+
+ {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"},
+ {400, &IUserLocalCommunicationService::Initialize, "Initialize"},
+ {401, &IUserLocalCommunicationService::Finalize, "Finalize"},
+ {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"},
};
// clang-format on
RegisterHandlers(functions);
+
+ state_change_event =
+ service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
+ }
+
+ ~IUserLocalCommunicationService() {
+ if (is_initialized) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ room_member->Unbind(ldn_packet_received);
+ }
+ }
+
+ service_context.CloseEvent(state_change_event);
+ }
+
+ /// Callback to parse and handle a received LDN packet.
+ void OnLDNPacketReceived(const Network::LDNPacket& packet) {
+ lan_discovery.ReceivePacket(packet);
+ }
+
+ void OnEventFired() {
+ state_change_event->GetWritableEvent().Signal();
}
void GetState(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_LDN, "(STUBBED) called");
+ State state = State::Error;
+
+ if (is_initialized) {
+ state = lan_discovery.GetState();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(state);
+ }
+
+ void GetNetworkInfo(Kernel::HLERequestContext& ctx) {
+ const auto write_buffer_size = ctx.GetWriteBufferSize();
+
+ if (write_buffer_size != sizeof(NetworkInfo)) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ NetworkInfo network_info{};
+ const auto rc = lan_discovery.GetNetworkInfo(network_info);
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ ctx.WriteBuffer<NetworkInfo>(network_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+ void GetIpv4Address(Kernel::HLERequestContext& ctx) {
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+
+ if (!network_interface) {
+ LOG_ERROR(Service_LDN, "No network interface available");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultNoIpAddress);
+ return;
+ }
+
+ Ipv4Address current_address{Network::TranslateIPv4(network_interface->ip_address)};
+ Ipv4Address subnet_mask{Network::TranslateIPv4(network_interface->subnet_mask)};
+
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ current_address = room_member->GetFakeIpAddress();
+ }
+ }
+
+ std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
+ std::reverse(std::begin(subnet_mask), std::end(subnet_mask)); // ntohl
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(current_address);
+ rb.PushRaw(subnet_mask);
+ }
+
+ void GetDisconnectReason(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(lan_discovery.GetDisconnectReason());
+ }
- // Indicate a network error, as we do not actually emulate LDN
- rb.Push(static_cast<u32>(State::Error));
+ void GetSecurityParameter(Kernel::HLERequestContext& ctx) {
+ SecurityParameter security_parameter{};
+ NetworkInfo info{};
+ const Result rc = lan_discovery.GetNetworkInfo(info);
+
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ security_parameter.session_id = info.network_id.session_id;
+ std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
+ sizeof(SecurityParameter::data));
+
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(rc);
+ rb.PushRaw<SecurityParameter>(security_parameter);
+ }
+ void GetNetworkConfig(Kernel::HLERequestContext& ctx) {
+ NetworkConfig config{};
+ NetworkInfo info{};
+ const Result rc = lan_discovery.GetNetworkInfo(info);
+
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ config.intent_id = info.network_id.intent_id;
+ config.channel = info.common.channel;
+ config.node_count_max = info.ldn.node_count_max;
+ config.local_communication_version = info.ldn.nodes[0].local_communication_version;
+
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(rc);
+ rb.PushRaw<NetworkConfig>(config);
+ }
+
+ void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
+ rb.PushCopyObjects(state_change_event->GetReadableEvent());
}
- void Initialize2(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_LDN, "called");
+ void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
+ const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
+ const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
- is_initialized = true;
+ if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
+ LOG_ERROR(Service_LDN, "Invalid buffer, size = {}, count = {}", network_buffer_size,
+ node_buffer_count);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ NetworkInfo info{};
+ std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
+
+ const auto rc = lan_discovery.GetNetworkInfo(info, latest_update, latest_update.size());
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ ctx.WriteBuffer(info, 0);
+ ctx.WriteBuffer(latest_update, 1);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_DISABLED);
+ rb.Push(ResultSuccess);
+ }
+
+ void Scan(Kernel::HLERequestContext& ctx) {
+ ScanImpl(ctx);
+ }
+
+ void ScanPrivate(Kernel::HLERequestContext& ctx) {
+ ScanImpl(ctx, true);
+ }
+
+ void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) {
+ IPC::RequestParser rp{ctx};
+ const auto channel{rp.PopEnum<WifiChannel>()};
+ const auto scan_filter{rp.PopRaw<ScanFilter>()};
+
+ const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo);
+
+ if (network_info_size == 0) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ u16 count = 0;
+ std::vector<NetworkInfo> network_infos(network_info_size);
+ Result rc = lan_discovery.Scan(network_infos, count, scan_filter);
+
+ LOG_INFO(Service_LDN,
+ "called, channel={}, filter_scan_flag={}, filter_network_type={}, is_private={}",
+ channel, scan_filter.flag, scan_filter.network_type, is_private);
+
+ ctx.WriteBuffer(network_infos);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(rc);
+ rb.Push<u32>(count);
+ }
+
+ void SetWirelessControllerRestriction(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
}
-private:
- enum class State {
- None,
- Initialized,
- AccessPointOpened,
- AccessPointCreated,
- StationOpened,
- StationConnected,
- Error,
- };
+ void OpenAccessPoint(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.OpenAccessPoint());
+ }
+
+ void CloseAccessPoint(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.CloseAccessPoint());
+ }
+
+ void CreateNetwork(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ CreateNetworkImpl(ctx);
+ }
+
+ void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ CreateNetworkImpl(ctx, true);
+ }
+
+ void CreateNetworkImpl(Kernel::HLERequestContext& ctx, bool is_private = false) {
+ IPC::RequestParser rp{ctx};
+
+ const auto security_config{rp.PopRaw<SecurityConfig>()};
+ [[maybe_unused]] const auto security_parameter{is_private ? rp.PopRaw<SecurityParameter>()
+ : SecurityParameter{}};
+ const auto user_config{rp.PopRaw<UserConfig>()};
+ rp.Pop<u32>(); // Padding
+ const auto network_Config{rp.PopRaw<NetworkConfig>()};
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.CreateNetwork(security_config, user_config, network_Config));
+ }
+
+ void DestroyNetwork(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.DestroyNetwork());
+ }
+
+ void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
+ std::vector<u8> read_buffer = ctx.ReadBuffer();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.SetAdvertiseData(read_buffer));
+ }
+
+ void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void OpenStation(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.OpenStation());
+ }
+
+ void CloseStation(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.CloseStation());
+ }
+
+ void Connect(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ u32 local_communication_version;
+ u32 option;
+ };
+ static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_INFO(Service_LDN,
+ "called, passphrase_size={}, security_mode={}, "
+ "local_communication_version={}",
+ parameters.security_config.passphrase_size,
+ parameters.security_config.security_mode, parameters.local_communication_version);
+
+ const std::vector<u8> read_buffer = ctx.ReadBuffer();
+ if (read_buffer.size() != sizeof(NetworkInfo)) {
+ LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ NetworkInfo network_info{};
+ std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.Connect(network_info, parameters.user_config,
+ static_cast<u16>(parameters.local_communication_version)));
+ }
+
+ void Disconnect(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.Disconnect());
+ }
+
+ void Initialize(Kernel::HLERequestContext& ctx) {
+ const auto rc = InitializeImpl(ctx);
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ }
+
+ void Finalize(Kernel::HLERequestContext& ctx) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ room_member->Unbind(ldn_packet_received);
+ }
+
+ is_initialized = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(lan_discovery.Finalize());
+ }
+
+ void Initialize2(Kernel::HLERequestContext& ctx) {
+ const auto rc = InitializeImpl(ctx);
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "Network isn't initialized, rc={}", rc.raw);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ }
+
+ Result InitializeImpl(Kernel::HLERequestContext& ctx) {
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+ if (!network_interface) {
+ LOG_ERROR(Service_LDN, "No network interface is set");
+ return ResultAirplaneModeEnabled;
+ }
+
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ ldn_packet_received = room_member->BindOnLdnPacketReceived(
+ [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
+ } else {
+ LOG_ERROR(Service_LDN, "Couldn't bind callback!");
+ return ResultAirplaneModeEnabled;
+ }
+
+ lan_discovery.Initialize([&]() { OnEventFired(); });
+ is_initialized = true;
+ return ResultSuccess;
+ }
+
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* state_change_event;
+ Network::RoomNetwork& room_network;
+ LANDiscovery lan_discovery;
+
+ // Callback identifier for the OnLDNPacketReceived event.
+ Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;
bool is_initialized{};
};
@@ -274,7 +650,7 @@ public:
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_DISABLED);
+ rb.Push(ResultDisabled);
}
};
diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h
index 3ccd9738b..6afe2ea6f 100644
--- a/src/core/hle/service/ldn/ldn.h
+++ b/src/core/hle/service/ldn/ldn.h
@@ -1,9 +1,14 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/result.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/sm/sm.h"
+
namespace Core {
class System;
}
diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h
new file mode 100644
index 000000000..f340bda42
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_results.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::LDN {
+
+constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10};
+constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20};
+constexpr Result ResultDisabled{ErrorModule::LDN, 22};
+constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23};
+constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30};
+constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31};
+constexpr Result ResultBadState{ErrorModule::LDN, 32};
+constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33};
+constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50};
+constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65};
+constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66};
+constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67};
+constexpr Result ResultBadInput{ErrorModule::LDN, 96};
+constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97};
+constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113};
+constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
new file mode 100644
index 000000000..44c2c773b
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -0,0 +1,306 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <fmt/format.h>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "network/network.h"
+
+namespace Service::LDN {
+
+constexpr size_t SsidLengthMax = 32;
+constexpr size_t AdvertiseDataSizeMax = 384;
+constexpr size_t UserNameBytesMax = 32;
+constexpr int NodeCountMax = 8;
+constexpr int StationCountMax = NodeCountMax - 1;
+constexpr size_t PassphraseLengthMax = 64;
+
+enum class SecurityMode : u16 {
+ All,
+ Retail,
+ Debug,
+};
+
+enum class NodeStateChange : u8 {
+ None,
+ Connect,
+ Disconnect,
+ DisconnectAndConnect,
+};
+
+DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange)
+
+enum class ScanFilterFlag : u32 {
+ None = 0,
+ LocalCommunicationId = 1 << 0,
+ SessionId = 1 << 1,
+ NetworkType = 1 << 2,
+ Ssid = 1 << 4,
+ SceneId = 1 << 5,
+ IntentId = LocalCommunicationId | SceneId,
+ NetworkId = IntentId | SessionId,
+};
+
+enum class NetworkType : u32 {
+ None,
+ General,
+ Ldn,
+ All,
+};
+
+enum class PackedNetworkType : u8 {
+ None,
+ General,
+ Ldn,
+ All,
+};
+
+enum class State : u32 {
+ None,
+ Initialized,
+ AccessPointOpened,
+ AccessPointCreated,
+ StationOpened,
+ StationConnected,
+ Error,
+};
+
+enum class DisconnectReason : s16 {
+ Unknown = -1,
+ None,
+ DisconnectedByUser,
+ DisconnectedBySystem,
+ DestroyedByUser,
+ DestroyedBySystem,
+ Rejected,
+ SignalLost,
+};
+
+enum class NetworkError {
+ Unknown = -1,
+ None = 0,
+ PortUnreachable,
+ TooManyPlayers,
+ VersionTooLow,
+ VersionTooHigh,
+ ConnectFailure,
+ ConnectNotFound,
+ ConnectTimeout,
+ ConnectRejected,
+ RejectFailed,
+};
+
+enum class AcceptPolicy : u8 {
+ AcceptAll,
+ RejectAll,
+ BlackList,
+ WhiteList,
+};
+
+enum class WifiChannel : s16 {
+ Default = 0,
+ Wifi24_1 = 1,
+ Wifi24_6 = 6,
+ Wifi24_11 = 11,
+ Wifi50_36 = 36,
+ Wifi50_40 = 40,
+ Wifi50_44 = 44,
+ Wifi50_48 = 48,
+};
+
+enum class LinkLevel : s8 {
+ Bad,
+ Low,
+ Good,
+ Excellent,
+};
+
+enum class NodeStatus : u8 {
+ Disconnected,
+ Connected,
+};
+
+struct NodeLatestUpdate {
+ NodeStateChange state_change;
+ INSERT_PADDING_BYTES(0x7); // Unknown
+};
+static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size");
+
+struct SessionId {
+ u64 high;
+ u64 low;
+
+ bool operator==(const SessionId&) const = default;
+};
+static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");
+
+struct IntentId {
+ u64 local_communication_id;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+ u16 scene_id;
+ INSERT_PADDING_BYTES(0x4); // Reserved
+};
+static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");
+
+struct NetworkId {
+ IntentId intent_id;
+ SessionId session_id;
+};
+static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");
+
+struct Ssid {
+ u8 length{};
+ std::array<char, SsidLengthMax + 1> raw{};
+
+ Ssid() = default;
+
+ constexpr explicit Ssid(std::string_view data) {
+ length = static_cast<u8>(std::min(data.size(), SsidLengthMax));
+ data.copy(raw.data(), length);
+ raw[length] = 0;
+ }
+
+ std::string GetStringValue() const {
+ return std::string(raw.data());
+ }
+
+ bool operator==(const Ssid& b) const {
+ return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0);
+ }
+
+ bool operator!=(const Ssid& b) const {
+ return !operator==(b);
+ }
+};
+static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
+
+using Ipv4Address = std::array<u8, 4>;
+static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
+
+struct MacAddress {
+ std::array<u8, 6> raw{};
+
+ friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
+};
+static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");
+
+struct MACAddressHash {
+ size_t operator()(const MacAddress& address) const {
+ u64 value{};
+ std::memcpy(&value, address.raw.data(), sizeof(address.raw));
+ return value;
+ }
+};
+
+struct ScanFilter {
+ NetworkId network_id;
+ NetworkType network_type;
+ MacAddress mac_address;
+ Ssid ssid;
+ INSERT_PADDING_BYTES(0x10);
+ ScanFilterFlag flag;
+};
+static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size");
+
+struct CommonNetworkInfo {
+ MacAddress bssid;
+ Ssid ssid;
+ WifiChannel channel;
+ LinkLevel link_level;
+ PackedNetworkType network_type;
+ INSERT_PADDING_BYTES(0x4);
+};
+static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");
+
+struct NodeInfo {
+ Ipv4Address ipv4_address;
+ MacAddress mac_address;
+ s8 node_id;
+ u8 is_connected;
+ std::array<u8, UserNameBytesMax + 1> user_name;
+ INSERT_PADDING_BYTES(0x1); // Reserved
+ s16 local_communication_version;
+ INSERT_PADDING_BYTES(0x10); // Reserved
+};
+static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");
+
+struct LdnNetworkInfo {
+ std::array<u8, 0x10> security_parameter;
+ SecurityMode security_mode;
+ AcceptPolicy station_accept_policy;
+ u8 has_action_frame;
+ INSERT_PADDING_BYTES(0x2); // Padding
+ u8 node_count_max;
+ u8 node_count;
+ std::array<NodeInfo, NodeCountMax> nodes;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+ u16 advertise_data_size;
+ std::array<u8, AdvertiseDataSizeMax> advertise_data;
+ INSERT_PADDING_BYTES(0x8C); // Reserved
+ u64 random_authentication_id;
+};
+static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");
+
+struct NetworkInfo {
+ NetworkId network_id;
+ CommonNetworkInfo common;
+ LdnNetworkInfo ldn;
+};
+static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
+
+struct SecurityConfig {
+ SecurityMode security_mode;
+ u16 passphrase_size;
+ std::array<u8, PassphraseLengthMax> passphrase;
+};
+static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size");
+
+struct UserConfig {
+ std::array<u8, UserNameBytesMax + 1> user_name;
+ INSERT_PADDING_BYTES(0xF); // Reserved
+};
+static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size");
+
+#pragma pack(push, 4)
+struct ConnectRequest {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ u32 local_communication_version;
+ u32 option_unknown;
+ NetworkInfo network_info;
+};
+static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size");
+#pragma pack(pop)
+
+struct SecurityParameter {
+ std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig
+ SessionId session_id;
+};
+static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size");
+
+struct NetworkConfig {
+ IntentId intent_id;
+ WifiChannel channel;
+ u8 node_count_max;
+ INSERT_PADDING_BYTES(0x1); // Reserved
+ u16 local_communication_version;
+ INSERT_PADDING_BYTES(0xA); // Reserved
+};
+static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size");
+
+struct AddressEntry {
+ Ipv4Address ipv4_address;
+ MacAddress mac_address;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+};
+static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size");
+
+struct AddressList {
+ std::array<AddressEntry, 0x8> addresses;
+};
+static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 9fc7bb1b1..becd6d1b9 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <fmt/format.h>
@@ -12,7 +11,6 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/k_system_control.h"
#include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/service/ldr/ldr.h"
@@ -22,20 +20,20 @@
namespace Service::LDR {
-constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
-
-[[maybe_unused]] constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
-constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
-constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
-constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
-constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
-constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
-constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
-constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
-constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
-constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
-[[maybe_unused]] constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
-constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
+constexpr Result ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
+
+[[maybe_unused]] constexpr Result ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
+constexpr Result ERROR_INVALID_NRO{ErrorModule::Loader, 52};
+constexpr Result ERROR_INVALID_NRR{ErrorModule::Loader, 53};
+constexpr Result ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
+constexpr Result ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
+constexpr Result ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
+constexpr Result ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
+constexpr Result ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
+constexpr Result ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
+constexpr Result ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
+[[maybe_unused]] constexpr Result ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
+constexpr Result ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200};
@@ -161,7 +159,8 @@ public:
class RelocatableObject final : public ServiceFramework<RelocatableObject> {
public:
- explicit RelocatableObject(Core::System& system_) : ServiceFramework{system_, "ldr:ro"} {
+ explicit RelocatableObject(Core::System& system_)
+ : ServiceFramework{system_, "ldr:ro", ServiceThreadType::CreateNew} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &RelocatableObject::LoadModule, "LoadModule"},
@@ -288,7 +287,7 @@ public:
}
bool ValidateRegionForMap(Kernel::KPageTable& page_table, VAddr start, std::size_t size) const {
- constexpr std::size_t padding_size{4 * Kernel::PageSize};
+ const std::size_t padding_size{page_table.GetNumGuardPages() * Kernel::PageSize};
const auto start_info{page_table.QueryInfo(start - 1)};
if (start_info.state != Kernel::KMemoryState::Free) {
@@ -308,31 +307,69 @@ public:
return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize());
}
- VAddr GetRandomMapRegion(const Kernel::KPageTable& page_table, std::size_t size) const {
- VAddr addr{};
- const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >>
- Kernel::PageBits};
- do {
- addr = page_table.GetAliasCodeRegionStart() +
- (Kernel::KSystemControl::GenerateRandomRange(0, end_pages) << Kernel::PageBits);
- } while (!page_table.IsInsideAddressSpace(addr, size) ||
- page_table.IsInsideHeapRegion(addr, size) ||
- page_table.IsInsideAliasRegion(addr, size));
- return addr;
+ Result GetAvailableMapRegion(Kernel::KPageTable& page_table, u64 size, VAddr& out_addr) {
+ size = Common::AlignUp(size, Kernel::PageSize);
+ size += page_table.GetNumGuardPages() * Kernel::PageSize * 4;
+
+ const auto is_region_available = [&](VAddr addr) {
+ const auto end_addr = addr + size;
+ while (addr < end_addr) {
+ if (system.Memory().IsValidVirtualAddress(addr)) {
+ return false;
+ }
+
+ if (!page_table.IsInsideAddressSpace(out_addr, size)) {
+ return false;
+ }
+
+ if (page_table.IsInsideHeapRegion(out_addr, size)) {
+ return false;
+ }
+
+ if (page_table.IsInsideAliasRegion(out_addr, size)) {
+ return false;
+ }
+
+ addr += Kernel::PageSize;
+ }
+ return true;
+ };
+
+ bool succeeded = false;
+ const auto map_region_end =
+ page_table.GetAliasCodeRegionStart() + page_table.GetAliasCodeRegionSize();
+ while (current_map_addr < map_region_end) {
+ if (is_region_available(current_map_addr)) {
+ succeeded = true;
+ break;
+ }
+ current_map_addr += 0x100000;
+ }
+
+ if (!succeeded) {
+ ASSERT_MSG(false, "Out of address space!");
+ return Kernel::ResultOutOfMemory;
+ }
+
+ out_addr = current_map_addr;
+ current_map_addr += size;
+
+ return ResultSuccess;
}
- ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr baseAddress,
- u64 size) const {
+ ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) {
+ auto& page_table{process->PageTable()};
+ VAddr addr{};
+
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
- auto& page_table{process->PageTable()};
- const VAddr addr{GetRandomMapRegion(page_table, size)};
- const ResultCode result{page_table.MapCodeMemory(addr, baseAddress, size)};
+ R_TRY(GetAvailableMapRegion(page_table, size, addr));
+ const Result result{page_table.MapCodeMemory(addr, base_addr, size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
}
- CASCADE_CODE(result);
+ R_TRY(result);
if (ValidateRegionForMap(page_table, addr, size)) {
return addr;
@@ -343,7 +380,7 @@ public:
}
ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size,
- VAddr bss_addr, std::size_t bss_size, std::size_t size) const {
+ VAddr bss_addr, std::size_t bss_size, std::size_t size) {
for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) {
auto& page_table{process->PageTable()};
VAddr addr{};
@@ -352,12 +389,15 @@ public:
if (bss_size) {
auto block_guard = detail::ScopeExit([&] {
- page_table.UnmapCodeMemory(addr + nro_size, bss_addr, bss_size);
- page_table.UnmapCodeMemory(addr, nro_addr, nro_size);
+ page_table.UnmapCodeMemory(
+ addr + nro_size, bss_addr, bss_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
+ page_table.UnmapCodeMemory(
+ addr, nro_addr, nro_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange);
});
- const ResultCode result{
- page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
+ const Result result{page_table.MapCodeMemory(addr + nro_size, bss_addr, bss_size)};
if (result == Kernel::ResultInvalidCurrentMemory) {
continue;
@@ -378,8 +418,8 @@ public:
return ERROR_INSUFFICIENT_ADDRESS_SPACE;
}
- ResultCode LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr,
- VAddr start) const {
+ Result LoadNro(Kernel::KProcess* process, const NROHeader& nro_header, VAddr nro_addr,
+ VAddr start) const {
const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset};
const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset};
const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset};
@@ -528,22 +568,26 @@ public:
rb.Push(*map_result);
}
- ResultCode UnmapNro(const NROInfo& info) {
+ Result UnmapNro(const NROInfo& info) {
// Each region must be unmapped separately to validate memory state
auto& page_table{system.CurrentProcess()->PageTable()};
if (info.bss_size != 0) {
- CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size +
- info.ro_size + info.data_size,
- info.bss_address, info.bss_size));
+ CASCADE_CODE(page_table.UnmapCodeMemory(
+ info.nro_address + info.text_size + info.ro_size + info.data_size, info.bss_address,
+ info.bss_size, Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
}
- CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size + info.ro_size,
- info.src_addr + info.text_size + info.ro_size,
- info.data_size));
- CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address + info.text_size,
- info.src_addr + info.text_size, info.ro_size));
- CASCADE_CODE(page_table.UnmapCodeMemory(info.nro_address, info.src_addr, info.text_size));
+ CASCADE_CODE(page_table.UnmapCodeMemory(
+ info.nro_address + info.text_size + info.ro_size,
+ info.src_addr + info.text_size + info.ro_size, info.data_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
+ CASCADE_CODE(page_table.UnmapCodeMemory(
+ info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
+ CASCADE_CODE(page_table.UnmapCodeMemory(
+ info.nro_address, info.src_addr, info.text_size,
+ Kernel::KPageTable::ICacheInvalidationStrategy::InvalidateRange));
return ResultSuccess;
}
@@ -597,6 +641,7 @@ public:
LOG_WARNING(Service_LDR, "(STUBBED) called");
initialized = true;
+ current_map_addr = system.CurrentProcess()->PageTable().GetAliasCodeRegionStart();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -607,6 +652,7 @@ private:
std::map<VAddr, NROInfo> nro;
std::map<VAddr, std::vector<SHA256Hash>> nrr;
+ VAddr current_map_addr{};
bool IsValidNROHash(const SHA256Hash& hash) const {
return std::any_of(nrr.begin(), nrr.end(), [&hash](const auto& p) {
diff --git a/src/core/hle/service/ldr/ldr.h b/src/core/hle/service/ldr/ldr.h
index 104fc15c5..25ffd8442 100644
--- a/src/core/hle/service/ldr/ldr.h
+++ b/src/core/hle/service/ldr/ldr.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index e40383134..ef4b54046 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
diff --git a/src/core/hle/service/lm/lm.h b/src/core/hle/service/lm/lm.h
index d40410b5c..266019c30 100644
--- a/src/core/hle/service/lm/lm.h
+++ b/src/core/hle/service/lm/lm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp
index 1599d941b..b9fe0cecd 100644
--- a/src/core/hle/service/mig/mig.cpp
+++ b/src/core/hle/service/mig/mig.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/mig/mig.h b/src/core/hle/service/mig/mig.h
index 2b24cdf2c..f1641a521 100644
--- a/src/core/hle/service/mig/mig.h
+++ b/src/core/hle/service/mig/mig.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index 0b907824d..390514fdc 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
@@ -13,7 +12,7 @@
namespace Service::Mii {
-constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
+constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::Mii, 1};
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
public:
@@ -44,7 +43,7 @@ public:
{20, nullptr, "IsBrokenDatabaseWithClearFlag"},
{21, &IDatabaseService::GetIndex, "GetIndex"},
{22, &IDatabaseService::SetInterfaceVersion, "SetInterfaceVersion"},
- {23, nullptr, "Convert"},
+ {23, &IDatabaseService::Convert, "Convert"},
{24, nullptr, "ConvertCoreDataToCharInfo"},
{25, nullptr, "ConvertCharInfoToCoreData"},
{26, nullptr, "Append"},
@@ -131,7 +130,7 @@ private:
return;
}
- std::vector<MiiInfo> values;
+ std::vector<CharInfo> values;
for (const auto& element : *result) {
values.emplace_back(element.info);
}
@@ -145,7 +144,7 @@ private:
void UpdateLatest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto info{rp.PopRaw<MiiInfo>()};
+ const auto info{rp.PopRaw<CharInfo>()};
const auto source_flag{rp.PopRaw<SourceFlag>()};
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
@@ -157,9 +156,9 @@ private:
return;
}
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
- rb.PushRaw<MiiInfo>(*result);
+ rb.PushRaw<CharInfo>(*result);
}
void BuildRandom(Kernel::HLERequestContext& ctx) {
@@ -192,9 +191,9 @@ private:
return;
}
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
- rb.PushRaw<MiiInfo>(manager.BuildRandom(age, gender, race));
+ rb.PushRaw<CharInfo>(manager.BuildRandom(age, gender, race));
}
void BuildDefault(Kernel::HLERequestContext& ctx) {
@@ -211,14 +210,14 @@ private:
return;
}
- IPC::ResponseBuilder rb{ctx, 2 + sizeof(MiiInfo) / sizeof(u32)};
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
rb.Push(ResultSuccess);
- rb.PushRaw<MiiInfo>(manager.BuildDefault(index));
+ rb.PushRaw<CharInfo>(manager.BuildDefault(index));
}
void GetIndex(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto info{rp.PopRaw<MiiInfo>()};
+ const auto info{rp.PopRaw<CharInfo>()};
LOG_DEBUG(Service_Mii, "called");
@@ -240,6 +239,18 @@ private:
rb.Push(ResultSuccess);
}
+ void Convert(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto mii_v3{rp.PopRaw<Ver3StoreData>()};
+
+ LOG_INFO(Service_Mii, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
+ rb.Push(ResultSuccess);
+ rb.PushRaw<CharInfo>(manager.ConvertV3ToCharInfo(mii_v3));
+ }
+
constexpr bool IsInterfaceVersionSupported(u32 interface_version) const {
return current_interface_version >= interface_version;
}
diff --git a/src/core/hle/service/mii/mii.h b/src/core/hle/service/mii/mii.h
index 9d3238e72..009d80d58 100644
--- a/src/core/hle/service/mii/mii.h
+++ b/src/core/hle/service/mii/mii.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/mii/mii_manager.cpp b/src/core/hle/service/mii/mii_manager.cpp
index 0a57c3cde..3a2fe938f 100644
--- a/src/core/hle/service/mii/mii_manager.cpp
+++ b/src/core/hle/service/mii/mii_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <random>
@@ -12,13 +11,12 @@
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/mii/mii_manager.h"
#include "core/hle/service/mii/raw_data.h"
-#include "core/hle/service/mii/types.h"
namespace Service::Mii {
namespace {
-constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
+constexpr Result ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
constexpr std::size_t BaseMiiCount{2};
constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
@@ -44,7 +42,7 @@ std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& i
return out;
}
-MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
+CharInfo ConvertStoreDataToInfo(const MiiStoreData& data) {
MiiStoreBitFields bf;
std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields));
@@ -292,7 +290,7 @@ MiiStoreData BuildRandomStoreData(Age age, Gender gender, Race race, const Commo
u8 glasses_type{};
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
if (++glasses_type >= glasses_type_info.values_count) {
- UNREACHABLE();
+ ASSERT(false);
break;
}
}
@@ -411,8 +409,8 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
return static_cast<u32>(count);
}
-ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info,
- SourceFlag source_flag) {
+ResultVal<CharInfo> MiiManager::UpdateLatest([[maybe_unused]] const CharInfo& info,
+ SourceFlag source_flag) {
if ((source_flag & SourceFlag::Database) == SourceFlag::None) {
return ERROR_CANNOT_FIND_ENTRY;
}
@@ -421,14 +419,242 @@ ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info
return ERROR_CANNOT_FIND_ENTRY;
}
-MiiInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
+CharInfo MiiManager::BuildRandom(Age age, Gender gender, Race race) {
return ConvertStoreDataToInfo(BuildRandomStoreData(age, gender, race, user_id));
}
-MiiInfo MiiManager::BuildDefault(std::size_t index) {
+CharInfo MiiManager::BuildDefault(std::size_t index) {
return ConvertStoreDataToInfo(BuildDefaultStoreData(RawData::DefaultMii.at(index), user_id));
}
+CharInfo MiiManager::ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const {
+ Service::Mii::MiiManager manager;
+ auto mii = manager.BuildDefault(0);
+
+ if (!ValidateV3Info(mii_v3)) {
+ return mii;
+ }
+
+ // TODO: We are ignoring a bunch of data from the mii_v3
+
+ mii.gender = static_cast<u8>(mii_v3.mii_information.gender);
+ mii.favorite_color = static_cast<u8>(mii_v3.mii_information.favorite_color);
+ mii.height = mii_v3.height;
+ mii.build = mii_v3.build;
+
+ // Copy name until string terminator
+ mii.name = {};
+ for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
+ mii.name[index] = mii_v3.mii_name[index];
+ if (mii.name[index] == 0) {
+ break;
+ }
+ }
+
+ mii.font_region = mii_v3.region_information.character_set;
+
+ mii.faceline_type = mii_v3.appearance_bits1.face_shape;
+ mii.faceline_color = mii_v3.appearance_bits1.skin_color;
+ mii.faceline_wrinkle = mii_v3.appearance_bits2.wrinkles;
+ mii.faceline_make = mii_v3.appearance_bits2.makeup;
+
+ mii.hair_type = mii_v3.hair_style;
+ mii.hair_color = mii_v3.appearance_bits3.hair_color;
+ mii.hair_flip = mii_v3.appearance_bits3.flip_hair;
+
+ mii.eye_type = static_cast<u8>(mii_v3.appearance_bits4.eye_type);
+ mii.eye_color = static_cast<u8>(mii_v3.appearance_bits4.eye_color);
+ mii.eye_scale = static_cast<u8>(mii_v3.appearance_bits4.eye_scale);
+ mii.eye_aspect = static_cast<u8>(mii_v3.appearance_bits4.eye_vertical_stretch);
+ mii.eye_rotate = static_cast<u8>(mii_v3.appearance_bits4.eye_rotation);
+ mii.eye_x = static_cast<u8>(mii_v3.appearance_bits4.eye_spacing);
+ mii.eye_y = static_cast<u8>(mii_v3.appearance_bits4.eye_y_position);
+
+ mii.eyebrow_type = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_style);
+ mii.eyebrow_color = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_color);
+ mii.eyebrow_scale = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_scale);
+ mii.eyebrow_aspect = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_yscale);
+ mii.eyebrow_rotate = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_rotation);
+ mii.eyebrow_x = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_spacing);
+ mii.eyebrow_y = static_cast<u8>(mii_v3.appearance_bits5.eyebrow_y_position);
+
+ mii.nose_type = static_cast<u8>(mii_v3.appearance_bits6.nose_type);
+ mii.nose_scale = static_cast<u8>(mii_v3.appearance_bits6.nose_scale);
+ mii.nose_y = static_cast<u8>(mii_v3.appearance_bits6.nose_y_position);
+
+ mii.mouth_type = static_cast<u8>(mii_v3.appearance_bits7.mouth_type);
+ mii.mouth_color = static_cast<u8>(mii_v3.appearance_bits7.mouth_color);
+ mii.mouth_scale = static_cast<u8>(mii_v3.appearance_bits7.mouth_scale);
+ mii.mouth_aspect = static_cast<u8>(mii_v3.appearance_bits7.mouth_horizontal_stretch);
+ mii.mouth_y = static_cast<u8>(mii_v3.appearance_bits8.mouth_y_position);
+
+ mii.mustache_type = static_cast<u8>(mii_v3.appearance_bits8.mustache_type);
+ mii.mustache_scale = static_cast<u8>(mii_v3.appearance_bits9.mustache_scale);
+ mii.mustache_y = static_cast<u8>(mii_v3.appearance_bits9.mustache_y_position);
+
+ mii.beard_type = static_cast<u8>(mii_v3.appearance_bits9.bear_type);
+ mii.beard_color = static_cast<u8>(mii_v3.appearance_bits9.facial_hair_color);
+
+ mii.glasses_type = static_cast<u8>(mii_v3.appearance_bits10.glasses_type);
+ mii.glasses_color = static_cast<u8>(mii_v3.appearance_bits10.glasses_color);
+ mii.glasses_scale = static_cast<u8>(mii_v3.appearance_bits10.glasses_scale);
+ mii.glasses_y = static_cast<u8>(mii_v3.appearance_bits10.glasses_y_position);
+
+ mii.mole_type = static_cast<u8>(mii_v3.appearance_bits11.mole_enabled);
+ mii.mole_scale = static_cast<u8>(mii_v3.appearance_bits11.mole_scale);
+ mii.mole_x = static_cast<u8>(mii_v3.appearance_bits11.mole_x_position);
+ mii.mole_y = static_cast<u8>(mii_v3.appearance_bits11.mole_y_position);
+
+ // TODO: Validate mii data
+
+ return mii;
+}
+
+Ver3StoreData MiiManager::ConvertCharInfoToV3(const CharInfo& mii) const {
+ Service::Mii::MiiManager manager;
+ Ver3StoreData mii_v3{};
+
+ // TODO: We are ignoring a bunch of data from the mii_v3
+
+ mii_v3.version = 1;
+ mii_v3.mii_information.gender.Assign(mii.gender);
+ mii_v3.mii_information.favorite_color.Assign(mii.favorite_color);
+ mii_v3.height = mii.height;
+ mii_v3.build = mii.build;
+
+ // Copy name until string terminator
+ mii_v3.mii_name = {};
+ for (std::size_t index = 0; index < mii.name.size() - 1; index++) {
+ mii_v3.mii_name[index] = mii.name[index];
+ if (mii_v3.mii_name[index] == 0) {
+ break;
+ }
+ }
+
+ mii_v3.region_information.character_set.Assign(mii.font_region);
+
+ mii_v3.appearance_bits1.face_shape.Assign(mii.faceline_type);
+ mii_v3.appearance_bits1.skin_color.Assign(mii.faceline_color);
+ mii_v3.appearance_bits2.wrinkles.Assign(mii.faceline_wrinkle);
+ mii_v3.appearance_bits2.makeup.Assign(mii.faceline_make);
+
+ mii_v3.hair_style = mii.hair_type;
+ mii_v3.appearance_bits3.hair_color.Assign(mii.hair_color);
+ mii_v3.appearance_bits3.flip_hair.Assign(mii.hair_flip);
+
+ mii_v3.appearance_bits4.eye_type.Assign(mii.eye_type);
+ mii_v3.appearance_bits4.eye_color.Assign(mii.eye_color);
+ mii_v3.appearance_bits4.eye_scale.Assign(mii.eye_scale);
+ mii_v3.appearance_bits4.eye_vertical_stretch.Assign(mii.eye_aspect);
+ mii_v3.appearance_bits4.eye_rotation.Assign(mii.eye_rotate);
+ mii_v3.appearance_bits4.eye_spacing.Assign(mii.eye_x);
+ mii_v3.appearance_bits4.eye_y_position.Assign(mii.eye_y);
+
+ mii_v3.appearance_bits5.eyebrow_style.Assign(mii.eyebrow_type);
+ mii_v3.appearance_bits5.eyebrow_color.Assign(mii.eyebrow_color);
+ mii_v3.appearance_bits5.eyebrow_scale.Assign(mii.eyebrow_scale);
+ mii_v3.appearance_bits5.eyebrow_yscale.Assign(mii.eyebrow_aspect);
+ mii_v3.appearance_bits5.eyebrow_rotation.Assign(mii.eyebrow_rotate);
+ mii_v3.appearance_bits5.eyebrow_spacing.Assign(mii.eyebrow_x);
+ mii_v3.appearance_bits5.eyebrow_y_position.Assign(mii.eyebrow_y);
+
+ mii_v3.appearance_bits6.nose_type.Assign(mii.nose_type);
+ mii_v3.appearance_bits6.nose_scale.Assign(mii.nose_scale);
+ mii_v3.appearance_bits6.nose_y_position.Assign(mii.nose_y);
+
+ mii_v3.appearance_bits7.mouth_type.Assign(mii.mouth_type);
+ mii_v3.appearance_bits7.mouth_color.Assign(mii.mouth_color);
+ mii_v3.appearance_bits7.mouth_scale.Assign(mii.mouth_scale);
+ mii_v3.appearance_bits7.mouth_horizontal_stretch.Assign(mii.mouth_aspect);
+ mii_v3.appearance_bits8.mouth_y_position.Assign(mii.mouth_y);
+
+ mii_v3.appearance_bits8.mustache_type.Assign(mii.mustache_type);
+ mii_v3.appearance_bits9.mustache_scale.Assign(mii.mustache_scale);
+ mii_v3.appearance_bits9.mustache_y_position.Assign(mii.mustache_y);
+
+ mii_v3.appearance_bits9.bear_type.Assign(mii.beard_type);
+ mii_v3.appearance_bits9.facial_hair_color.Assign(mii.beard_color);
+
+ mii_v3.appearance_bits10.glasses_type.Assign(mii.glasses_type);
+ mii_v3.appearance_bits10.glasses_color.Assign(mii.glasses_color);
+ mii_v3.appearance_bits10.glasses_scale.Assign(mii.glasses_scale);
+ mii_v3.appearance_bits10.glasses_y_position.Assign(mii.glasses_y);
+
+ mii_v3.appearance_bits11.mole_enabled.Assign(mii.mole_type);
+ mii_v3.appearance_bits11.mole_scale.Assign(mii.mole_scale);
+ mii_v3.appearance_bits11.mole_x_position.Assign(mii.mole_x);
+ mii_v3.appearance_bits11.mole_y_position.Assign(mii.mole_y);
+
+ // TODO: Validate mii_v3 data
+
+ return mii_v3;
+}
+
+bool MiiManager::ValidateV3Info(const Ver3StoreData& mii_v3) const {
+ bool is_valid = mii_v3.version == 0 || mii_v3.version == 3;
+
+ is_valid = is_valid && (mii_v3.mii_name[0] != 0);
+
+ is_valid = is_valid && (mii_v3.mii_information.birth_month < 13);
+ is_valid = is_valid && (mii_v3.mii_information.birth_day < 32);
+ is_valid = is_valid && (mii_v3.mii_information.favorite_color < 12);
+ is_valid = is_valid && (mii_v3.height < 128);
+ is_valid = is_valid && (mii_v3.build < 128);
+
+ is_valid = is_valid && (mii_v3.appearance_bits1.face_shape < 12);
+ is_valid = is_valid && (mii_v3.appearance_bits1.skin_color < 7);
+ is_valid = is_valid && (mii_v3.appearance_bits2.wrinkles < 12);
+ is_valid = is_valid && (mii_v3.appearance_bits2.makeup < 12);
+
+ is_valid = is_valid && (mii_v3.hair_style < 132);
+ is_valid = is_valid && (mii_v3.appearance_bits3.hair_color < 8);
+
+ is_valid = is_valid && (mii_v3.appearance_bits4.eye_type < 60);
+ is_valid = is_valid && (mii_v3.appearance_bits4.eye_color < 6);
+ is_valid = is_valid && (mii_v3.appearance_bits4.eye_scale < 8);
+ is_valid = is_valid && (mii_v3.appearance_bits4.eye_vertical_stretch < 7);
+ is_valid = is_valid && (mii_v3.appearance_bits4.eye_rotation < 8);
+ is_valid = is_valid && (mii_v3.appearance_bits4.eye_spacing < 13);
+ is_valid = is_valid && (mii_v3.appearance_bits4.eye_y_position < 19);
+
+ is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_style < 25);
+ is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_color < 8);
+ is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_scale < 9);
+ is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_yscale < 7);
+ is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_rotation < 12);
+ is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_spacing < 12);
+ is_valid = is_valid && (mii_v3.appearance_bits5.eyebrow_y_position < 19);
+
+ is_valid = is_valid && (mii_v3.appearance_bits6.nose_type < 18);
+ is_valid = is_valid && (mii_v3.appearance_bits6.nose_scale < 9);
+ is_valid = is_valid && (mii_v3.appearance_bits6.nose_y_position < 19);
+
+ is_valid = is_valid && (mii_v3.appearance_bits7.mouth_type < 36);
+ is_valid = is_valid && (mii_v3.appearance_bits7.mouth_color < 5);
+ is_valid = is_valid && (mii_v3.appearance_bits7.mouth_scale < 9);
+ is_valid = is_valid && (mii_v3.appearance_bits7.mouth_horizontal_stretch < 7);
+ is_valid = is_valid && (mii_v3.appearance_bits8.mouth_y_position < 19);
+
+ is_valid = is_valid && (mii_v3.appearance_bits8.mustache_type < 6);
+ is_valid = is_valid && (mii_v3.appearance_bits9.mustache_scale < 7);
+ is_valid = is_valid && (mii_v3.appearance_bits9.mustache_y_position < 17);
+
+ is_valid = is_valid && (mii_v3.appearance_bits9.bear_type < 6);
+ is_valid = is_valid && (mii_v3.appearance_bits9.facial_hair_color < 8);
+
+ is_valid = is_valid && (mii_v3.appearance_bits10.glasses_type < 9);
+ is_valid = is_valid && (mii_v3.appearance_bits10.glasses_color < 6);
+ is_valid = is_valid && (mii_v3.appearance_bits10.glasses_scale < 8);
+ is_valid = is_valid && (mii_v3.appearance_bits10.glasses_y_position < 21);
+
+ is_valid = is_valid && (mii_v3.appearance_bits11.mole_enabled < 2);
+ is_valid = is_valid && (mii_v3.appearance_bits11.mole_scale < 9);
+ is_valid = is_valid && (mii_v3.appearance_bits11.mole_x_position < 17);
+ is_valid = is_valid && (mii_v3.appearance_bits11.mole_y_position < 31);
+
+ return is_valid;
+}
+
ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_flag) {
std::vector<MiiInfoElement> result;
@@ -443,7 +669,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_
return result;
}
-ResultCode MiiManager::GetIndex([[maybe_unused]] const MiiInfo& info, u32& index) {
+Result MiiManager::GetIndex([[maybe_unused]] const CharInfo& info, u32& index) {
constexpr u32 INVALID_INDEX{0xFFFFFFFF};
index = INVALID_INDEX;
diff --git a/src/core/hle/service/mii/mii_manager.h b/src/core/hle/service/mii/mii_manager.h
index 6999d15b1..83ad3d343 100644
--- a/src/core/hle/service/mii/mii_manager.h
+++ b/src/core/hle/service/mii/mii_manager.h
@@ -1,315 +1,15 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <array>
#include <vector>
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
-#include "common/uuid.h"
+
#include "core/hle/result.h"
#include "core/hle/service/mii/types.h"
namespace Service::Mii {
-enum class Source : u32 {
- Database = 0,
- Default = 1,
- Account = 2,
- Friend = 3,
-};
-
-enum class SourceFlag : u32 {
- None = 0,
- Database = 1 << 0,
- Default = 1 << 1,
-};
-DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
-
-struct MiiInfo {
- Common::UUID uuid;
- std::array<char16_t, 11> name;
- u8 font_region;
- u8 favorite_color;
- u8 gender;
- u8 height;
- u8 build;
- u8 type;
- u8 region_move;
- u8 faceline_type;
- u8 faceline_color;
- u8 faceline_wrinkle;
- u8 faceline_make;
- u8 hair_type;
- u8 hair_color;
- u8 hair_flip;
- u8 eye_type;
- u8 eye_color;
- u8 eye_scale;
- u8 eye_aspect;
- u8 eye_rotate;
- u8 eye_x;
- u8 eye_y;
- u8 eyebrow_type;
- u8 eyebrow_color;
- u8 eyebrow_scale;
- u8 eyebrow_aspect;
- u8 eyebrow_rotate;
- u8 eyebrow_x;
- u8 eyebrow_y;
- u8 nose_type;
- u8 nose_scale;
- u8 nose_y;
- u8 mouth_type;
- u8 mouth_color;
- u8 mouth_scale;
- u8 mouth_aspect;
- u8 mouth_y;
- u8 beard_color;
- u8 beard_type;
- u8 mustache_type;
- u8 mustache_scale;
- u8 mustache_y;
- u8 glasses_type;
- u8 glasses_color;
- u8 glasses_scale;
- u8 glasses_y;
- u8 mole_type;
- u8 mole_scale;
- u8 mole_x;
- u8 mole_y;
- u8 padding;
-
- std::u16string Name() const;
-};
-static_assert(sizeof(MiiInfo) == 0x58, "MiiInfo has incorrect size.");
-static_assert(std::has_unique_object_representations_v<MiiInfo>,
- "All bits of MiiInfo must contribute to its value.");
-
-#pragma pack(push, 4)
-
-struct MiiInfoElement {
- MiiInfoElement(const MiiInfo& info_, Source source_) : info{info_}, source{source_} {}
-
- MiiInfo info{};
- Source source{};
-};
-static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
-
-struct MiiStoreBitFields {
- union {
- u32 word_0{};
-
- BitField<0, 8, u32> hair_type;
- BitField<8, 7, u32> height;
- BitField<15, 1, u32> mole_type;
- BitField<16, 7, u32> build;
- BitField<23, 1, HairFlip> hair_flip;
- BitField<24, 7, u32> hair_color;
- BitField<31, 1, u32> type;
- };
-
- union {
- u32 word_1{};
-
- BitField<0, 7, u32> eye_color;
- BitField<7, 1, Gender> gender;
- BitField<8, 7, u32> eyebrow_color;
- BitField<16, 7, u32> mouth_color;
- BitField<24, 7, u32> beard_color;
- };
-
- union {
- u32 word_2{};
-
- BitField<0, 7, u32> glasses_color;
- BitField<8, 6, u32> eye_type;
- BitField<14, 2, u32> region_move;
- BitField<16, 6, u32> mouth_type;
- BitField<22, 2, FontRegion> font_region;
- BitField<24, 5, u32> eye_y;
- BitField<29, 3, u32> glasses_scale;
- };
-
- union {
- u32 word_3{};
-
- BitField<0, 5, u32> eyebrow_type;
- BitField<5, 3, MustacheType> mustache_type;
- BitField<8, 5, u32> nose_type;
- BitField<13, 3, BeardType> beard_type;
- BitField<16, 5, u32> nose_y;
- BitField<21, 3, u32> mouth_aspect;
- BitField<24, 5, u32> mouth_y;
- BitField<29, 3, u32> eyebrow_aspect;
- };
-
- union {
- u32 word_4{};
-
- BitField<0, 5, u32> mustache_y;
- BitField<5, 3, u32> eye_rotate;
- BitField<8, 5, u32> glasses_y;
- BitField<13, 3, u32> eye_aspect;
- BitField<16, 5, u32> mole_x;
- BitField<21, 3, u32> eye_scale;
- BitField<24, 5, u32> mole_y;
- };
-
- union {
- u32 word_5{};
-
- BitField<0, 5, u32> glasses_type;
- BitField<8, 4, u32> favorite_color;
- BitField<12, 4, u32> faceline_type;
- BitField<16, 4, u32> faceline_color;
- BitField<20, 4, u32> faceline_wrinkle;
- BitField<24, 4, u32> faceline_makeup;
- BitField<28, 4, u32> eye_x;
- };
-
- union {
- u32 word_6{};
-
- BitField<0, 4, u32> eyebrow_scale;
- BitField<4, 4, u32> eyebrow_rotate;
- BitField<8, 4, u32> eyebrow_x;
- BitField<12, 4, u32> eyebrow_y;
- BitField<16, 4, u32> nose_scale;
- BitField<20, 4, u32> mouth_scale;
- BitField<24, 4, u32> mustache_scale;
- BitField<28, 4, u32> mole_scale;
- };
-};
-static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
-static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
- "MiiStoreBitFields is not trivially copyable.");
-
-struct MiiStoreData {
- using Name = std::array<char16_t, 10>;
-
- MiiStoreData();
- MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
- const Common::UUID& user_id);
-
- // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
- // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
- // not suitable for our uses.
- struct {
- std::array<u8, 0x1C> data{};
- static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
-
- Name name{};
- Common::UUID uuid{};
- } data;
-
- u16 data_crc{};
- u16 device_crc{};
-};
-static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
-
-struct MiiStoreDataElement {
- MiiStoreData data{};
- Source source{};
-};
-static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
-
-struct MiiDatabase {
- u32 magic{}; // 'NFDB'
- std::array<MiiStoreData, 0x64> miis{};
- INSERT_PADDING_BYTES(1);
- u8 count{};
- u16 crc{};
-};
-static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
-
-struct RandomMiiValues {
- std::array<u8, 0xbc> values{};
-};
-static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
-
-struct RandomMiiData4 {
- Gender gender{};
- Age age{};
- Race race{};
- u32 values_count{};
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
-
-struct RandomMiiData3 {
- u32 arg_1;
- u32 arg_2;
- u32 values_count;
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
-
-struct RandomMiiData2 {
- u32 arg_1;
- u32 values_count;
- std::array<u32, 47> values{};
-};
-static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
-
-struct DefaultMii {
- u32 face_type{};
- u32 face_color{};
- u32 face_wrinkle{};
- u32 face_makeup{};
- u32 hair_type{};
- u32 hair_color{};
- u32 hair_flip{};
- u32 eye_type{};
- u32 eye_color{};
- u32 eye_scale{};
- u32 eye_aspect{};
- u32 eye_rotate{};
- u32 eye_x{};
- u32 eye_y{};
- u32 eyebrow_type{};
- u32 eyebrow_color{};
- u32 eyebrow_scale{};
- u32 eyebrow_aspect{};
- u32 eyebrow_rotate{};
- u32 eyebrow_x{};
- u32 eyebrow_y{};
- u32 nose_type{};
- u32 nose_scale{};
- u32 nose_y{};
- u32 mouth_type{};
- u32 mouth_color{};
- u32 mouth_scale{};
- u32 mouth_aspect{};
- u32 mouth_y{};
- u32 mustache_type{};
- u32 beard_type{};
- u32 beard_color{};
- u32 mustache_scale{};
- u32 mustache_y{};
- u32 glasses_type{};
- u32 glasses_color{};
- u32 glasses_scale{};
- u32 glasses_y{};
- u32 mole_type{};
- u32 mole_scale{};
- u32 mole_x{};
- u32 mole_y{};
- u32 height{};
- u32 weight{};
- Gender gender{};
- u32 favorite_color{};
- u32 region{};
- FontRegion font_region{};
- u32 type{};
- INSERT_PADDING_WORDS(5);
-};
-static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
-
-#pragma pack(pop)
-
// The Mii manager is responsible for loading and storing the Miis to the database in NAND along
// with providing an easy interface for HLE emulation of the mii service.
class MiiManager {
@@ -319,11 +19,14 @@ public:
bool CheckAndResetUpdateCounter(SourceFlag source_flag, u64& current_update_counter);
bool IsFullDatabase() const;
u32 GetCount(SourceFlag source_flag) const;
- ResultVal<MiiInfo> UpdateLatest(const MiiInfo& info, SourceFlag source_flag);
- MiiInfo BuildRandom(Age age, Gender gender, Race race);
- MiiInfo BuildDefault(std::size_t index);
+ ResultVal<CharInfo> UpdateLatest(const CharInfo& info, SourceFlag source_flag);
+ CharInfo BuildRandom(Age age, Gender gender, Race race);
+ CharInfo BuildDefault(std::size_t index);
+ CharInfo ConvertV3ToCharInfo(const Ver3StoreData& mii_v3) const;
+ Ver3StoreData ConvertCharInfoToV3(const CharInfo& mii) const;
+ bool ValidateV3Info(const Ver3StoreData& mii_v3) const;
ResultVal<std::vector<MiiInfoElement>> GetDefault(SourceFlag source_flag);
- ResultCode GetIndex(const MiiInfo& info, u32& index);
+ Result GetIndex(const CharInfo& info, u32& index);
private:
const Common::UUID user_id{};
diff --git a/src/core/hle/service/mii/raw_data.cpp b/src/core/hle/service/mii/raw_data.cpp
index 9d3c8017a..1442280c8 100644
--- a/src/core/hle/service/mii/raw_data.cpp
+++ b/src/core/hle/service/mii/raw_data.cpp
@@ -1,22 +1,5 @@
-// MIT License
-//
-// Copyright (c) Ryujinx Team and Contributors
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
-// associated documentation files (the "Software"), to deal in the Software without restriction,
-// including without limitation the rights to use, copy, modify, merge, publish, distribute,
-// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all copies or
-// substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
-// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
+// SPDX-FileCopyrightText: Ryujinx Team and Contributors
+// SPDX-License-Identifier: MIT
#include "core/hle/service/mii/raw_data.h"
diff --git a/src/core/hle/service/mii/raw_data.h b/src/core/hle/service/mii/raw_data.h
index bd90c2162..c2bec68d4 100644
--- a/src/core/hle/service/mii/raw_data.h
+++ b/src/core/hle/service/mii/raw_data.h
@@ -1,12 +1,11 @@
-// Copyright 2020 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
-#include "core/hle/service/mii/mii_manager.h"
+#include "core/hle/service/mii/types.h"
namespace Service::Mii::RawData {
diff --git a/src/core/hle/service/mii/types.h b/src/core/hle/service/mii/types.h
index d65a1055e..9e3247397 100644
--- a/src/core/hle/service/mii/types.h
+++ b/src/core/hle/service/mii/types.h
@@ -1,11 +1,15 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <array>
+#include <type_traits>
+
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
+#include "common/uuid.h"
namespace Service::Mii {
@@ -25,7 +29,11 @@ enum class BeardType : u32 {
Beard5,
};
-enum class BeardAndMustacheFlag : u32 { Beard = 1, Mustache, All = Beard | Mustache };
+enum class BeardAndMustacheFlag : u32 {
+ Beard = 1,
+ Mustache,
+ All = Beard | Mustache,
+};
DECLARE_ENUM_FLAG_OPERATORS(BeardAndMustacheFlag);
enum class FontRegion : u32 {
@@ -64,4 +72,424 @@ enum class Race : u32 {
All,
};
+enum class Source : u32 {
+ Database = 0,
+ Default = 1,
+ Account = 2,
+ Friend = 3,
+};
+
+enum class SourceFlag : u32 {
+ None = 0,
+ Database = 1 << 0,
+ Default = 1 << 1,
+};
+DECLARE_ENUM_FLAG_OPERATORS(SourceFlag);
+
+// nn::mii::CharInfo
+struct CharInfo {
+ Common::UUID uuid;
+ std::array<char16_t, 11> name;
+ u8 font_region;
+ u8 favorite_color;
+ u8 gender;
+ u8 height;
+ u8 build;
+ u8 type;
+ u8 region_move;
+ u8 faceline_type;
+ u8 faceline_color;
+ u8 faceline_wrinkle;
+ u8 faceline_make;
+ u8 hair_type;
+ u8 hair_color;
+ u8 hair_flip;
+ u8 eye_type;
+ u8 eye_color;
+ u8 eye_scale;
+ u8 eye_aspect;
+ u8 eye_rotate;
+ u8 eye_x;
+ u8 eye_y;
+ u8 eyebrow_type;
+ u8 eyebrow_color;
+ u8 eyebrow_scale;
+ u8 eyebrow_aspect;
+ u8 eyebrow_rotate;
+ u8 eyebrow_x;
+ u8 eyebrow_y;
+ u8 nose_type;
+ u8 nose_scale;
+ u8 nose_y;
+ u8 mouth_type;
+ u8 mouth_color;
+ u8 mouth_scale;
+ u8 mouth_aspect;
+ u8 mouth_y;
+ u8 beard_color;
+ u8 beard_type;
+ u8 mustache_type;
+ u8 mustache_scale;
+ u8 mustache_y;
+ u8 glasses_type;
+ u8 glasses_color;
+ u8 glasses_scale;
+ u8 glasses_y;
+ u8 mole_type;
+ u8 mole_scale;
+ u8 mole_x;
+ u8 mole_y;
+ u8 padding;
+};
+static_assert(sizeof(CharInfo) == 0x58, "CharInfo has incorrect size.");
+static_assert(std::has_unique_object_representations_v<CharInfo>,
+ "All bits of CharInfo must contribute to its value.");
+
+#pragma pack(push, 4)
+
+struct MiiInfoElement {
+ MiiInfoElement(const CharInfo& info_, Source source_) : info{info_}, source{source_} {}
+
+ CharInfo info{};
+ Source source{};
+};
+static_assert(sizeof(MiiInfoElement) == 0x5c, "MiiInfoElement has incorrect size.");
+
+struct MiiStoreBitFields {
+ union {
+ u32 word_0{};
+
+ BitField<0, 8, u32> hair_type;
+ BitField<8, 7, u32> height;
+ BitField<15, 1, u32> mole_type;
+ BitField<16, 7, u32> build;
+ BitField<23, 1, HairFlip> hair_flip;
+ BitField<24, 7, u32> hair_color;
+ BitField<31, 1, u32> type;
+ };
+
+ union {
+ u32 word_1{};
+
+ BitField<0, 7, u32> eye_color;
+ BitField<7, 1, Gender> gender;
+ BitField<8, 7, u32> eyebrow_color;
+ BitField<16, 7, u32> mouth_color;
+ BitField<24, 7, u32> beard_color;
+ };
+
+ union {
+ u32 word_2{};
+
+ BitField<0, 7, u32> glasses_color;
+ BitField<8, 6, u32> eye_type;
+ BitField<14, 2, u32> region_move;
+ BitField<16, 6, u32> mouth_type;
+ BitField<22, 2, FontRegion> font_region;
+ BitField<24, 5, u32> eye_y;
+ BitField<29, 3, u32> glasses_scale;
+ };
+
+ union {
+ u32 word_3{};
+
+ BitField<0, 5, u32> eyebrow_type;
+ BitField<5, 3, MustacheType> mustache_type;
+ BitField<8, 5, u32> nose_type;
+ BitField<13, 3, BeardType> beard_type;
+ BitField<16, 5, u32> nose_y;
+ BitField<21, 3, u32> mouth_aspect;
+ BitField<24, 5, u32> mouth_y;
+ BitField<29, 3, u32> eyebrow_aspect;
+ };
+
+ union {
+ u32 word_4{};
+
+ BitField<0, 5, u32> mustache_y;
+ BitField<5, 3, u32> eye_rotate;
+ BitField<8, 5, u32> glasses_y;
+ BitField<13, 3, u32> eye_aspect;
+ BitField<16, 5, u32> mole_x;
+ BitField<21, 3, u32> eye_scale;
+ BitField<24, 5, u32> mole_y;
+ };
+
+ union {
+ u32 word_5{};
+
+ BitField<0, 5, u32> glasses_type;
+ BitField<8, 4, u32> favorite_color;
+ BitField<12, 4, u32> faceline_type;
+ BitField<16, 4, u32> faceline_color;
+ BitField<20, 4, u32> faceline_wrinkle;
+ BitField<24, 4, u32> faceline_makeup;
+ BitField<28, 4, u32> eye_x;
+ };
+
+ union {
+ u32 word_6{};
+
+ BitField<0, 4, u32> eyebrow_scale;
+ BitField<4, 4, u32> eyebrow_rotate;
+ BitField<8, 4, u32> eyebrow_x;
+ BitField<12, 4, u32> eyebrow_y;
+ BitField<16, 4, u32> nose_scale;
+ BitField<20, 4, u32> mouth_scale;
+ BitField<24, 4, u32> mustache_scale;
+ BitField<28, 4, u32> mole_scale;
+ };
+};
+static_assert(sizeof(MiiStoreBitFields) == 0x1c, "MiiStoreBitFields has incorrect size.");
+static_assert(std::is_trivially_copyable_v<MiiStoreBitFields>,
+ "MiiStoreBitFields is not trivially copyable.");
+
+// This is nn::mii::Ver3StoreData
+// Based on citra HLE::Applets::MiiData and PretendoNetwork.
+// https://github.com/citra-emu/citra/blob/master/src/core/hle/applets/mii_selector.h#L48
+// https://github.com/PretendoNetwork/mii-js/blob/master/mii.js#L299
+struct Ver3StoreData {
+ u8 version;
+ union {
+ u8 raw;
+
+ BitField<0, 1, u8> allow_copying;
+ BitField<1, 1, u8> profanity_flag;
+ BitField<2, 2, u8> region_lock;
+ BitField<4, 2, u8> character_set;
+ } region_information;
+ u16_be mii_id;
+ u64_be system_id;
+ u32_be specialness_and_creation_date;
+ std::array<u8, 0x6> creator_mac;
+ u16_be padding;
+ union {
+ u16 raw;
+
+ BitField<0, 1, u16> gender;
+ BitField<1, 4, u16> birth_month;
+ BitField<5, 5, u16> birth_day;
+ BitField<10, 4, u16> favorite_color;
+ BitField<14, 1, u16> favorite;
+ } mii_information;
+ std::array<char16_t, 0xA> mii_name;
+ u8 height;
+ u8 build;
+ union {
+ u8 raw;
+
+ BitField<0, 1, u8> disable_sharing;
+ BitField<1, 4, u8> face_shape;
+ BitField<5, 3, u8> skin_color;
+ } appearance_bits1;
+ union {
+ u8 raw;
+
+ BitField<0, 4, u8> wrinkles;
+ BitField<4, 4, u8> makeup;
+ } appearance_bits2;
+ u8 hair_style;
+ union {
+ u8 raw;
+
+ BitField<0, 3, u8> hair_color;
+ BitField<3, 1, u8> flip_hair;
+ } appearance_bits3;
+ union {
+ u32 raw;
+
+ BitField<0, 6, u32> eye_type;
+ BitField<6, 3, u32> eye_color;
+ BitField<9, 4, u32> eye_scale;
+ BitField<13, 3, u32> eye_vertical_stretch;
+ BitField<16, 5, u32> eye_rotation;
+ BitField<21, 4, u32> eye_spacing;
+ BitField<25, 5, u32> eye_y_position;
+ } appearance_bits4;
+ union {
+ u32 raw;
+
+ BitField<0, 5, u32> eyebrow_style;
+ BitField<5, 3, u32> eyebrow_color;
+ BitField<8, 4, u32> eyebrow_scale;
+ BitField<12, 3, u32> eyebrow_yscale;
+ BitField<16, 4, u32> eyebrow_rotation;
+ BitField<21, 4, u32> eyebrow_spacing;
+ BitField<25, 5, u32> eyebrow_y_position;
+ } appearance_bits5;
+ union {
+ u16 raw;
+
+ BitField<0, 5, u16> nose_type;
+ BitField<5, 4, u16> nose_scale;
+ BitField<9, 5, u16> nose_y_position;
+ } appearance_bits6;
+ union {
+ u16 raw;
+
+ BitField<0, 6, u16> mouth_type;
+ BitField<6, 3, u16> mouth_color;
+ BitField<9, 4, u16> mouth_scale;
+ BitField<13, 3, u16> mouth_horizontal_stretch;
+ } appearance_bits7;
+ union {
+ u8 raw;
+
+ BitField<0, 5, u8> mouth_y_position;
+ BitField<5, 3, u8> mustache_type;
+ } appearance_bits8;
+ u8 allow_copying;
+ union {
+ u16 raw;
+
+ BitField<0, 3, u16> bear_type;
+ BitField<3, 3, u16> facial_hair_color;
+ BitField<6, 4, u16> mustache_scale;
+ BitField<10, 5, u16> mustache_y_position;
+ } appearance_bits9;
+ union {
+ u16 raw;
+
+ BitField<0, 4, u16> glasses_type;
+ BitField<4, 3, u16> glasses_color;
+ BitField<7, 4, u16> glasses_scale;
+ BitField<11, 5, u16> glasses_y_position;
+ } appearance_bits10;
+ union {
+ u16 raw;
+
+ BitField<0, 1, u16> mole_enabled;
+ BitField<1, 4, u16> mole_scale;
+ BitField<5, 5, u16> mole_x_position;
+ BitField<10, 5, u16> mole_y_position;
+ } appearance_bits11;
+
+ std::array<u16_le, 0xA> author_name;
+ INSERT_PADDING_BYTES(0x4);
+};
+static_assert(sizeof(Ver3StoreData) == 0x60, "Ver3StoreData is an invalid size");
+
+struct MiiStoreData {
+ using Name = std::array<char16_t, 10>;
+
+ MiiStoreData();
+ MiiStoreData(const Name& name, const MiiStoreBitFields& bit_fields,
+ const Common::UUID& user_id);
+
+ // This corresponds to the above structure MiiStoreBitFields. I did it like this because the
+ // BitField<> type makes this (and any thing that contains it) not trivially copyable, which is
+ // not suitable for our uses.
+ struct {
+ std::array<u8, 0x1C> data{};
+ static_assert(sizeof(MiiStoreBitFields) == sizeof(data), "data field has incorrect size.");
+
+ Name name{};
+ Common::UUID uuid{};
+ } data;
+
+ u16 data_crc{};
+ u16 device_crc{};
+};
+static_assert(sizeof(MiiStoreData) == 0x44, "MiiStoreData has incorrect size.");
+
+struct MiiStoreDataElement {
+ MiiStoreData data{};
+ Source source{};
+};
+static_assert(sizeof(MiiStoreDataElement) == 0x48, "MiiStoreDataElement has incorrect size.");
+
+struct MiiDatabase {
+ u32 magic{}; // 'NFDB'
+ std::array<MiiStoreData, 0x64> miis{};
+ INSERT_PADDING_BYTES(1);
+ u8 count{};
+ u16 crc{};
+};
+static_assert(sizeof(MiiDatabase) == 0x1A98, "MiiDatabase has incorrect size.");
+
+struct RandomMiiValues {
+ std::array<u8, 0xbc> values{};
+};
+static_assert(sizeof(RandomMiiValues) == 0xbc, "RandomMiiValues has incorrect size.");
+
+struct RandomMiiData4 {
+ Gender gender{};
+ Age age{};
+ Race race{};
+ u32 values_count{};
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData4) == 0xcc, "RandomMiiData4 has incorrect size.");
+
+struct RandomMiiData3 {
+ u32 arg_1;
+ u32 arg_2;
+ u32 values_count;
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData3) == 0xc8, "RandomMiiData3 has incorrect size.");
+
+struct RandomMiiData2 {
+ u32 arg_1;
+ u32 values_count;
+ std::array<u32, 47> values{};
+};
+static_assert(sizeof(RandomMiiData2) == 0xc4, "RandomMiiData2 has incorrect size.");
+
+struct DefaultMii {
+ u32 face_type{};
+ u32 face_color{};
+ u32 face_wrinkle{};
+ u32 face_makeup{};
+ u32 hair_type{};
+ u32 hair_color{};
+ u32 hair_flip{};
+ u32 eye_type{};
+ u32 eye_color{};
+ u32 eye_scale{};
+ u32 eye_aspect{};
+ u32 eye_rotate{};
+ u32 eye_x{};
+ u32 eye_y{};
+ u32 eyebrow_type{};
+ u32 eyebrow_color{};
+ u32 eyebrow_scale{};
+ u32 eyebrow_aspect{};
+ u32 eyebrow_rotate{};
+ u32 eyebrow_x{};
+ u32 eyebrow_y{};
+ u32 nose_type{};
+ u32 nose_scale{};
+ u32 nose_y{};
+ u32 mouth_type{};
+ u32 mouth_color{};
+ u32 mouth_scale{};
+ u32 mouth_aspect{};
+ u32 mouth_y{};
+ u32 mustache_type{};
+ u32 beard_type{};
+ u32 beard_color{};
+ u32 mustache_scale{};
+ u32 mustache_y{};
+ u32 glasses_type{};
+ u32 glasses_color{};
+ u32 glasses_scale{};
+ u32 glasses_y{};
+ u32 mole_type{};
+ u32 mole_scale{};
+ u32 mole_x{};
+ u32 mole_y{};
+ u32 height{};
+ u32 weight{};
+ Gender gender{};
+ u32 favorite_color{};
+ u32 region{};
+ FontRegion font_region{};
+ u32 type{};
+ INSERT_PADDING_WORDS(5);
+};
+static_assert(sizeof(DefaultMii) == 0xd8, "MiiStoreData has incorrect size.");
+
+#pragma pack(pop)
+
} // namespace Service::Mii
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index 0ccfe91cd..ba8c0e230 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
@@ -47,7 +46,7 @@ private:
IPC::RequestParser rp{ctx};
min = rp.Pop<u32>();
max = rp.Pop<u32>();
- LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
+ LOG_DEBUG(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
current = min;
IPC::ResponseBuilder rb{ctx, 2};
@@ -55,7 +54,7 @@ private:
}
void GetOld(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_MM, "(STUBBED) called");
+ LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -82,8 +81,8 @@ private:
u32 input_id = rp.Pop<u32>();
min = rp.Pop<u32>();
max = rp.Pop<u32>();
- LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
- input_id, min, max);
+ LOG_DEBUG(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", input_id,
+ min, max);
current = min;
IPC::ResponseBuilder rb{ctx, 2};
@@ -91,7 +90,7 @@ private:
}
void Get(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_MM, "(STUBBED) called");
+ LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/mm/mm_u.h b/src/core/hle/service/mm/mm_u.h
index 49b6a3355..b40941e35 100644
--- a/src/core/hle/service/mm/mm_u.h
+++ b/src/core/hle/service/mm/mm_u.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/mnpp/mnpp_app.cpp b/src/core/hle/service/mnpp/mnpp_app.cpp
new file mode 100644
index 000000000..c3aad5714
--- /dev/null
+++ b/src/core/hle/service/mnpp/mnpp_app.cpp
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/mnpp/mnpp_app.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::MNPP {
+
+class MNPP_APP final : public ServiceFramework<MNPP_APP> {
+public:
+ explicit MNPP_APP(Core::System& system_) : ServiceFramework{system_, "mnpp:app"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &MNPP_APP::Unknown0, "unknown0"},
+ {1, &MNPP_APP::Unknown1, "unknown1"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Unknown0(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_MNPP, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void Unknown1(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_MNPP, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+};
+
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+ std::make_shared<MNPP_APP>(system)->InstallAsService(service_manager);
+}
+
+} // namespace Service::MNPP
diff --git a/src/core/hle/service/mnpp/mnpp_app.h b/src/core/hle/service/mnpp/mnpp_app.h
new file mode 100644
index 000000000..eec75fe0e
--- /dev/null
+++ b/src/core/hle/service/mnpp/mnpp_app.h
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::MNPP {
+
+/// Registers all MNPP services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
+
+} // namespace Service::MNPP
diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp
index 2dcda16f6..68210a108 100644
--- a/src/core/hle/service/ncm/ncm.cpp
+++ b/src/core/hle/service/ncm/ncm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/ncm/ncm.h b/src/core/hle/service/ncm/ncm.h
index ee01eddc0..de3971437 100644
--- a/src/core/hle/service/ncm/ncm.h
+++ b/src/core/hle/service/ncm/ncm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index f77037842..046c5f18f 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
@@ -107,10 +106,10 @@ public:
{1, &IUser::FinalizeOld, "FinalizeOld"},
{2, &IUser::GetStateOld, "GetStateOld"},
{3, &IUser::IsNfcEnabledOld, "IsNfcEnabledOld"},
- {400, nullptr, "Initialize"},
- {401, nullptr, "Finalize"},
- {402, nullptr, "GetState"},
- {403, nullptr, "IsNfcEnabled"},
+ {400, &IUser::InitializeOld, "Initialize"},
+ {401, &IUser::FinalizeOld, "Finalize"},
+ {402, &IUser::GetStateOld, "GetState"},
+ {403, &IUser::IsNfcEnabledOld, "IsNfcEnabled"},
{404, nullptr, "ListDevices"},
{405, nullptr, "GetDeviceState"},
{406, nullptr, "GetNpadId"},
diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h
index 5a94b076d..0107b696c 100644
--- a/src/core/hle/service/nfc/nfc.h
+++ b/src/core/hle/service/nfc/nfc.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/nfp/amiibo_crypto.cpp b/src/core/hle/service/nfp/amiibo_crypto.cpp
new file mode 100644
index 000000000..c32a6816b
--- /dev/null
+++ b/src/core/hle/service/nfp/amiibo_crypto.cpp
@@ -0,0 +1,388 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+// SPDX-FileCopyrightText: Copyright 2017 socram8888/amiitool
+// SPDX-License-Identifier: MIT
+
+#include <array>
+#include <mbedtls/aes.h>
+#include <mbedtls/hmac_drbg.h>
+
+#include "common/fs/file.h"
+#include "common/fs/path_util.h"
+#include "common/logging/log.h"
+#include "core/hle/service/mii/mii_manager.h"
+#include "core/hle/service/nfp/amiibo_crypto.h"
+
+namespace Service::NFP::AmiiboCrypto {
+
+bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file) {
+ const auto& amiibo_data = ntag_file.user_memory;
+ LOG_DEBUG(Service_NFP, "uuid_lock=0x{0:x}", ntag_file.static_lock);
+ LOG_DEBUG(Service_NFP, "compability_container=0x{0:x}", ntag_file.compability_container);
+ LOG_DEBUG(Service_NFP, "write_count={}", static_cast<u16>(amiibo_data.write_counter));
+
+ LOG_DEBUG(Service_NFP, "character_id=0x{0:x}", amiibo_data.model_info.character_id);
+ LOG_DEBUG(Service_NFP, "character_variant={}", amiibo_data.model_info.character_variant);
+ LOG_DEBUG(Service_NFP, "amiibo_type={}", amiibo_data.model_info.amiibo_type);
+ LOG_DEBUG(Service_NFP, "model_number=0x{0:x}",
+ static_cast<u16>(amiibo_data.model_info.model_number));
+ LOG_DEBUG(Service_NFP, "series={}", amiibo_data.model_info.series);
+ LOG_DEBUG(Service_NFP, "tag_type=0x{0:x}", amiibo_data.model_info.tag_type);
+
+ LOG_DEBUG(Service_NFP, "tag_dynamic_lock=0x{0:x}", ntag_file.dynamic_lock);
+ LOG_DEBUG(Service_NFP, "tag_CFG0=0x{0:x}", ntag_file.CFG0);
+ LOG_DEBUG(Service_NFP, "tag_CFG1=0x{0:x}", ntag_file.CFG1);
+
+ // Validate UUID
+ constexpr u8 CT = 0x88; // As defined in `ISO / IEC 14443 - 3`
+ if ((CT ^ ntag_file.uuid.uid[0] ^ ntag_file.uuid.uid[1] ^ ntag_file.uuid.uid[2]) !=
+ ntag_file.uuid.uid[3]) {
+ return false;
+ }
+ if ((ntag_file.uuid.uid[4] ^ ntag_file.uuid.uid[5] ^ ntag_file.uuid.uid[6] ^
+ ntag_file.uuid.nintendo_id) != ntag_file.uuid.lock_bytes[0]) {
+ return false;
+ }
+
+ // Check against all know constants on an amiibo binary
+ if (ntag_file.static_lock != 0xE00F) {
+ return false;
+ }
+ if (ntag_file.compability_container != 0xEEFF10F1U) {
+ return false;
+ }
+ if (amiibo_data.constant_value != 0xA5) {
+ return false;
+ }
+ if (amiibo_data.model_info.tag_type != PackedTagType::Type2) {
+ return false;
+ }
+ if ((ntag_file.dynamic_lock & 0xFFFFFF) != 0x0F0001U) {
+ return false;
+ }
+ if (ntag_file.CFG0 != 0x04000000U) {
+ return false;
+ }
+ if (ntag_file.CFG1 != 0x5F) {
+ return false;
+ }
+ return true;
+}
+
+NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data) {
+ NTAG215File encoded_data{};
+
+ encoded_data.uid = nfc_data.uuid.uid;
+ encoded_data.nintendo_id = nfc_data.uuid.nintendo_id;
+ encoded_data.static_lock = nfc_data.static_lock;
+ encoded_data.compability_container = nfc_data.compability_container;
+ encoded_data.hmac_data = nfc_data.user_memory.hmac_data;
+ encoded_data.constant_value = nfc_data.user_memory.constant_value;
+ encoded_data.write_counter = nfc_data.user_memory.write_counter;
+ encoded_data.settings = nfc_data.user_memory.settings;
+ encoded_data.owner_mii = nfc_data.user_memory.owner_mii;
+ encoded_data.title_id = nfc_data.user_memory.title_id;
+ encoded_data.applicaton_write_counter = nfc_data.user_memory.applicaton_write_counter;
+ encoded_data.application_area_id = nfc_data.user_memory.application_area_id;
+ encoded_data.unknown = nfc_data.user_memory.unknown;
+ encoded_data.unknown2 = nfc_data.user_memory.unknown2;
+ encoded_data.application_area = nfc_data.user_memory.application_area;
+ encoded_data.hmac_tag = nfc_data.user_memory.hmac_tag;
+ encoded_data.lock_bytes = nfc_data.uuid.lock_bytes;
+ encoded_data.model_info = nfc_data.user_memory.model_info;
+ encoded_data.keygen_salt = nfc_data.user_memory.keygen_salt;
+ encoded_data.dynamic_lock = nfc_data.dynamic_lock;
+ encoded_data.CFG0 = nfc_data.CFG0;
+ encoded_data.CFG1 = nfc_data.CFG1;
+ encoded_data.password = nfc_data.password;
+
+ return encoded_data;
+}
+
+EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data) {
+ EncryptedNTAG215File nfc_data{};
+
+ nfc_data.uuid.uid = encoded_data.uid;
+ nfc_data.uuid.nintendo_id = encoded_data.nintendo_id;
+ nfc_data.uuid.lock_bytes = encoded_data.lock_bytes;
+ nfc_data.static_lock = encoded_data.static_lock;
+ nfc_data.compability_container = encoded_data.compability_container;
+ nfc_data.user_memory.hmac_data = encoded_data.hmac_data;
+ nfc_data.user_memory.constant_value = encoded_data.constant_value;
+ nfc_data.user_memory.write_counter = encoded_data.write_counter;
+ nfc_data.user_memory.settings = encoded_data.settings;
+ nfc_data.user_memory.owner_mii = encoded_data.owner_mii;
+ nfc_data.user_memory.title_id = encoded_data.title_id;
+ nfc_data.user_memory.applicaton_write_counter = encoded_data.applicaton_write_counter;
+ nfc_data.user_memory.application_area_id = encoded_data.application_area_id;
+ nfc_data.user_memory.unknown = encoded_data.unknown;
+ nfc_data.user_memory.unknown2 = encoded_data.unknown2;
+ nfc_data.user_memory.application_area = encoded_data.application_area;
+ nfc_data.user_memory.hmac_tag = encoded_data.hmac_tag;
+ nfc_data.user_memory.model_info = encoded_data.model_info;
+ nfc_data.user_memory.keygen_salt = encoded_data.keygen_salt;
+ nfc_data.dynamic_lock = encoded_data.dynamic_lock;
+ nfc_data.CFG0 = encoded_data.CFG0;
+ nfc_data.CFG1 = encoded_data.CFG1;
+ nfc_data.password = encoded_data.password;
+
+ return nfc_data;
+}
+
+u32 GetTagPassword(const TagUuid& uuid) {
+ // Verifiy that the generated password is correct
+ u32 password = 0xAA ^ (uuid.uid[1] ^ uuid.uid[3]);
+ password &= (0x55 ^ (uuid.uid[2] ^ uuid.uid[4])) << 8;
+ password &= (0xAA ^ (uuid.uid[3] ^ uuid.uid[5])) << 16;
+ password &= (0x55 ^ (uuid.uid[4] ^ uuid.uid[6])) << 24;
+ return password;
+}
+
+HashSeed GetSeed(const NTAG215File& data) {
+ HashSeed seed{
+ .magic = data.write_counter,
+ .padding = {},
+ .uid_1 = data.uid,
+ .nintendo_id_1 = data.nintendo_id,
+ .uid_2 = data.uid,
+ .nintendo_id_2 = data.nintendo_id,
+ .keygen_salt = data.keygen_salt,
+ };
+
+ return seed;
+}
+
+std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed) {
+ const std::size_t seedPart1Len = sizeof(key.magic_bytes) - key.magic_length;
+ const std::size_t string_size = key.type_string.size();
+ std::vector<u8> output(string_size + seedPart1Len);
+
+ // Copy whole type string
+ memccpy(output.data(), key.type_string.data(), '\0', string_size);
+
+ // Append (16 - magic_length) from the input seed
+ memcpy(output.data() + string_size, &seed, seedPart1Len);
+
+ // Append all bytes from magicBytes
+ output.insert(output.end(), key.magic_bytes.begin(),
+ key.magic_bytes.begin() + key.magic_length);
+
+ output.insert(output.end(), seed.uid_1.begin(), seed.uid_1.end());
+ output.emplace_back(seed.nintendo_id_1);
+ output.insert(output.end(), seed.uid_2.begin(), seed.uid_2.end());
+ output.emplace_back(seed.nintendo_id_2);
+
+ for (std::size_t i = 0; i < sizeof(seed.keygen_salt); i++) {
+ output.emplace_back(static_cast<u8>(seed.keygen_salt[i] ^ key.xor_pad[i]));
+ }
+
+ return output;
+}
+
+void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
+ const std::vector<u8>& seed) {
+ // Initialize context
+ ctx.used = false;
+ ctx.counter = 0;
+ ctx.buffer_size = sizeof(ctx.counter) + seed.size();
+ memcpy(ctx.buffer.data() + sizeof(u16), seed.data(), seed.size());
+
+ // Initialize HMAC context
+ mbedtls_md_init(&hmac_ctx);
+ mbedtls_md_setup(&hmac_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
+ mbedtls_md_hmac_starts(&hmac_ctx, hmac_key.data(), hmac_key.size());
+}
+
+void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output) {
+ // If used at least once, reinitialize the HMAC
+ if (ctx.used) {
+ mbedtls_md_hmac_reset(&hmac_ctx);
+ }
+
+ ctx.used = true;
+
+ // Store counter in big endian, and increment it
+ ctx.buffer[0] = static_cast<u8>(ctx.counter >> 8);
+ ctx.buffer[1] = static_cast<u8>(ctx.counter >> 0);
+ ctx.counter++;
+
+ // Do HMAC magic
+ mbedtls_md_hmac_update(&hmac_ctx, reinterpret_cast<const unsigned char*>(ctx.buffer.data()),
+ ctx.buffer_size);
+ mbedtls_md_hmac_finish(&hmac_ctx, output.data());
+}
+
+DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data) {
+ const auto seed = GetSeed(data);
+
+ // Generate internal seed
+ const std::vector<u8> internal_key = GenerateInternalKey(key, seed);
+
+ // Initialize context
+ CryptoCtx ctx{};
+ mbedtls_md_context_t hmac_ctx;
+ CryptoInit(ctx, hmac_ctx, key.hmac_key, internal_key);
+
+ // Generate derived keys
+ DerivedKeys derived_keys{};
+ std::array<DrgbOutput, 2> temp{};
+ CryptoStep(ctx, hmac_ctx, temp[0]);
+ CryptoStep(ctx, hmac_ctx, temp[1]);
+ memcpy(&derived_keys, temp.data(), sizeof(DerivedKeys));
+
+ // Cleanup context
+ mbedtls_md_free(&hmac_ctx);
+
+ return derived_keys;
+}
+
+void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data) {
+ mbedtls_aes_context aes;
+ std::size_t nc_off = 0;
+ std::array<u8, sizeof(keys.aes_iv)> nonce_counter{};
+ std::array<u8, sizeof(keys.aes_iv)> stream_block{};
+
+ const auto aes_key_size = static_cast<u32>(keys.aes_key.size() * 8);
+ mbedtls_aes_setkey_enc(&aes, keys.aes_key.data(), aes_key_size);
+ memcpy(nonce_counter.data(), keys.aes_iv.data(), sizeof(keys.aes_iv));
+
+ constexpr std::size_t encrypted_data_size = HMAC_TAG_START - SETTINGS_START;
+ mbedtls_aes_crypt_ctr(&aes, encrypted_data_size, &nc_off, nonce_counter.data(),
+ stream_block.data(),
+ reinterpret_cast<const unsigned char*>(&in_data.settings),
+ reinterpret_cast<unsigned char*>(&out_data.settings));
+
+ // Copy the rest of the data directly
+ out_data.uid = in_data.uid;
+ out_data.nintendo_id = in_data.nintendo_id;
+ out_data.lock_bytes = in_data.lock_bytes;
+ out_data.static_lock = in_data.static_lock;
+ out_data.compability_container = in_data.compability_container;
+
+ out_data.constant_value = in_data.constant_value;
+ out_data.write_counter = in_data.write_counter;
+
+ out_data.model_info = in_data.model_info;
+ out_data.keygen_salt = in_data.keygen_salt;
+ out_data.dynamic_lock = in_data.dynamic_lock;
+ out_data.CFG0 = in_data.CFG0;
+ out_data.CFG1 = in_data.CFG1;
+ out_data.password = in_data.password;
+}
+
+bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info) {
+ const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
+
+ const Common::FS::IOFile keys_file{yuzu_keys_dir / "key_retail.bin",
+ Common::FS::FileAccessMode::Read,
+ Common::FS::FileType::BinaryFile};
+
+ if (!keys_file.IsOpen()) {
+ LOG_ERROR(Service_NFP, "No keys detected");
+ return false;
+ }
+
+ if (keys_file.Read(unfixed_info) != 1) {
+ LOG_ERROR(Service_NFP, "Failed to read unfixed_info");
+ return false;
+ }
+ if (keys_file.Read(locked_secret) != 1) {
+ LOG_ERROR(Service_NFP, "Failed to read locked-secret");
+ return false;
+ }
+
+ return true;
+}
+
+bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data) {
+ InternalKey locked_secret{};
+ InternalKey unfixed_info{};
+
+ if (!LoadKeys(locked_secret, unfixed_info)) {
+ return false;
+ }
+
+ // Generate keys
+ NTAG215File encoded_data = NfcDataToEncodedData(encrypted_tag_data);
+ const auto data_keys = GenerateKey(unfixed_info, encoded_data);
+ const auto tag_keys = GenerateKey(locked_secret, encoded_data);
+
+ // Decrypt
+ Cipher(data_keys, encoded_data, tag_data);
+
+ // Regenerate tag HMAC. Note: order matters, data HMAC depends on tag HMAC!
+ constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
+ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
+ sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
+ input_length, reinterpret_cast<unsigned char*>(&tag_data.hmac_tag));
+
+ // Regenerate data HMAC
+ constexpr std::size_t input_length2 = DYNAMIC_LOCK_START - WRITE_COUNTER_START;
+ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), data_keys.hmac_key.data(),
+ sizeof(HmacKey),
+ reinterpret_cast<const unsigned char*>(&tag_data.write_counter), input_length2,
+ reinterpret_cast<unsigned char*>(&tag_data.hmac_data));
+
+ if (tag_data.hmac_data != encrypted_tag_data.user_memory.hmac_data) {
+ LOG_ERROR(Service_NFP, "hmac_data doesn't match");
+ return false;
+ }
+
+ if (tag_data.hmac_tag != encrypted_tag_data.user_memory.hmac_tag) {
+ LOG_ERROR(Service_NFP, "hmac_tag doesn't match");
+ return false;
+ }
+
+ return true;
+}
+
+bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data) {
+ InternalKey locked_secret{};
+ InternalKey unfixed_info{};
+
+ if (!LoadKeys(locked_secret, unfixed_info)) {
+ return false;
+ }
+
+ // Generate keys
+ const auto data_keys = GenerateKey(unfixed_info, tag_data);
+ const auto tag_keys = GenerateKey(locked_secret, tag_data);
+
+ NTAG215File encoded_tag_data{};
+
+ // Generate tag HMAC
+ constexpr std::size_t input_length = DYNAMIC_LOCK_START - UUID_START;
+ constexpr std::size_t input_length2 = HMAC_TAG_START - WRITE_COUNTER_START;
+ mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), tag_keys.hmac_key.data(),
+ sizeof(HmacKey), reinterpret_cast<const unsigned char*>(&tag_data.uid),
+ input_length, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag));
+
+ // Init mbedtls HMAC context
+ mbedtls_md_context_t ctx;
+ mbedtls_md_init(&ctx);
+ mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1);
+
+ // Generate data HMAC
+ mbedtls_md_hmac_starts(&ctx, data_keys.hmac_key.data(), sizeof(HmacKey));
+ mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.write_counter),
+ input_length2); // Data
+ mbedtls_md_hmac_update(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_tag),
+ sizeof(HashData)); // Tag HMAC
+ mbedtls_md_hmac_update(&ctx, reinterpret_cast<const unsigned char*>(&tag_data.uid),
+ input_length);
+ mbedtls_md_hmac_finish(&ctx, reinterpret_cast<unsigned char*>(&encoded_tag_data.hmac_data));
+
+ // HMAC cleanup
+ mbedtls_md_free(&ctx);
+
+ // Encrypt
+ Cipher(data_keys, tag_data, encoded_tag_data);
+
+ // Convert back to hardware
+ encrypted_tag_data = EncodedDataToNfcData(encoded_tag_data);
+
+ return true;
+}
+
+} // namespace Service::NFP::AmiiboCrypto
diff --git a/src/core/hle/service/nfp/amiibo_crypto.h b/src/core/hle/service/nfp/amiibo_crypto.h
new file mode 100644
index 000000000..0175ced91
--- /dev/null
+++ b/src/core/hle/service/nfp/amiibo_crypto.h
@@ -0,0 +1,100 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "core/hle/service/nfp/nfp_types.h"
+
+struct mbedtls_md_context_t;
+
+namespace Service::NFP::AmiiboCrypto {
+// Byte locations in Service::NFP::NTAG215File
+constexpr std::size_t HMAC_DATA_START = 0x8;
+constexpr std::size_t SETTINGS_START = 0x2c;
+constexpr std::size_t WRITE_COUNTER_START = 0x29;
+constexpr std::size_t HMAC_TAG_START = 0x1B4;
+constexpr std::size_t UUID_START = 0x1D4;
+constexpr std::size_t DYNAMIC_LOCK_START = 0x208;
+
+using HmacKey = std::array<u8, 0x10>;
+using DrgbOutput = std::array<u8, 0x20>;
+
+struct HashSeed {
+ u16_be magic;
+ std::array<u8, 0xE> padding;
+ UniqueSerialNumber uid_1;
+ u8 nintendo_id_1;
+ UniqueSerialNumber uid_2;
+ u8 nintendo_id_2;
+ std::array<u8, 0x20> keygen_salt;
+};
+static_assert(sizeof(HashSeed) == 0x40, "HashSeed is an invalid size");
+
+struct InternalKey {
+ HmacKey hmac_key;
+ std::array<char, 0xE> type_string;
+ u8 reserved;
+ u8 magic_length;
+ std::array<u8, 0x10> magic_bytes;
+ std::array<u8, 0x20> xor_pad;
+};
+static_assert(sizeof(InternalKey) == 0x50, "InternalKey is an invalid size");
+static_assert(std::is_trivially_copyable_v<InternalKey>, "InternalKey must be trivially copyable.");
+
+struct CryptoCtx {
+ std::array<char, 480> buffer;
+ bool used;
+ std::size_t buffer_size;
+ s16 counter;
+};
+
+struct DerivedKeys {
+ std::array<u8, 0x10> aes_key;
+ std::array<u8, 0x10> aes_iv;
+ std::array<u8, 0x10> hmac_key;
+};
+static_assert(sizeof(DerivedKeys) == 0x30, "DerivedKeys is an invalid size");
+
+/// Validates that the amiibo file is not corrupted
+bool IsAmiiboValid(const EncryptedNTAG215File& ntag_file);
+
+/// Converts from encrypted file format to encoded file format
+NTAG215File NfcDataToEncodedData(const EncryptedNTAG215File& nfc_data);
+
+/// Converts from encoded file format to encrypted file format
+EncryptedNTAG215File EncodedDataToNfcData(const NTAG215File& encoded_data);
+
+/// Returns password needed to allow write access to protected memory
+u32 GetTagPassword(const TagUuid& uuid);
+
+// Generates Seed needed for key derivation
+HashSeed GetSeed(const NTAG215File& data);
+
+// Middle step on the generation of derived keys
+std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed);
+
+// Initializes mbedtls context
+void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key,
+ const std::vector<u8>& seed);
+
+// Feeds data to mbedtls context to generate the derived key
+void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output);
+
+// Generates the derived key from amiibo data
+DerivedKeys GenerateKey(const InternalKey& key, const NTAG215File& data);
+
+// Encodes or decodes amiibo data
+void Cipher(const DerivedKeys& keys, const NTAG215File& in_data, NTAG215File& out_data);
+
+/// Loads both amiibo keys from key_retail.bin
+bool LoadKeys(InternalKey& locked_secret, InternalKey& unfixed_info);
+
+/// Decodes encripted amiibo data returns true if output is valid
+bool DecodeAmiibo(const EncryptedNTAG215File& encrypted_tag_data, NTAG215File& tag_data);
+
+/// Encodes plain amiibo data returns true if output is valid
+bool EncodeAmiibo(const NTAG215File& tag_data, EncryptedNTAG215File& encrypted_tag_data);
+
+} // namespace Service::NFP::AmiiboCrypto
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 761d0d3c6..0cb55ca49 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -1,361 +1,43 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <array>
-#include <atomic>
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/k_event.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
namespace Service::NFP {
-namespace ErrCodes {
-constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
-} // namespace ErrCodes
-
-Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
- const char* name)
- : ServiceFramework{system_, name}, module{std::move(module_)}, service_context{system_,
- "NFP::IUser"} {
- nfc_tag_load = service_context.CreateEvent("NFP::IUser:NFCTagDetected");
-}
-
-Module::Interface::~Interface() {
- service_context.CloseEvent(nfc_tag_load);
-}
-class IUser final : public ServiceFramework<IUser> {
+class IUserManager final : public ServiceFramework<IUserManager> {
public:
- explicit IUser(Module::Interface& nfp_interface_, Core::System& system_,
- KernelHelpers::ServiceContext& service_context_)
- : ServiceFramework{system_, "NFP::IUser"}, nfp_interface{nfp_interface_},
- service_context{service_context_} {
+ explicit IUserManager(Core::System& system_) : ServiceFramework{system_, "nfp:user"} {
+ // clang-format off
static const FunctionInfo functions[] = {
- {0, &IUser::Initialize, "Initialize"},
- {1, &IUser::Finalize, "Finalize"},
- {2, &IUser::ListDevices, "ListDevices"},
- {3, &IUser::StartDetection, "StartDetection"},
- {4, &IUser::StopDetection, "StopDetection"},
- {5, &IUser::Mount, "Mount"},
- {6, &IUser::Unmount, "Unmount"},
- {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
- {8, &IUser::GetApplicationArea, "GetApplicationArea"},
- {9, nullptr, "SetApplicationArea"},
- {10, nullptr, "Flush"},
- {11, nullptr, "Restore"},
- {12, nullptr, "CreateApplicationArea"},
- {13, &IUser::GetTagInfo, "GetTagInfo"},
- {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
- {15, &IUser::GetCommonInfo, "GetCommonInfo"},
- {16, &IUser::GetModelInfo, "GetModelInfo"},
- {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
- {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
- {19, &IUser::GetState, "GetState"},
- {20, &IUser::GetDeviceState, "GetDeviceState"},
- {21, &IUser::GetNpadId, "GetNpadId"},
- {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
- {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
- {24, nullptr, "RecreateApplicationArea"},
+ {0, &IUserManager::CreateUserInterface, "CreateUserInterface"},
};
- RegisterHandlers(functions);
-
- deactivate_event = service_context.CreateEvent("NFP::IUser:DeactivateEvent");
- availability_change_event =
- service_context.CreateEvent("NFP::IUser:AvailabilityChangeEvent");
- }
+ // clang-format on
- ~IUser() override {
- service_context.CloseEvent(deactivate_event);
- service_context.CloseEvent(availability_change_event);
+ RegisterHandlers(functions);
}
private:
- struct TagInfo {
- std::array<u8, 10> uuid;
- u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
- // mean something else
- std::array<u8, 0x15> padding_1;
- u32_le protocol;
- u32_le tag_type;
- std::array<u8, 0x2c> padding_2;
- };
- static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
-
- enum class State : u32 {
- NonInitialized = 0,
- Initialized = 1,
- };
-
- enum class DeviceState : u32 {
- Initialized = 0,
- SearchingForTag = 1,
- TagFound = 2,
- TagRemoved = 3,
- TagNearby = 4,
- Unknown5 = 5,
- Finalized = 6
- };
-
- struct CommonInfo {
- u16_be last_write_year;
- u8 last_write_month;
- u8 last_write_day;
- u16_be write_counter;
- u16_be version;
- u32_be application_area_size;
- INSERT_PADDING_BYTES(0x34);
- };
- static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
-
- void Initialize(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFC, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0};
- rb.Push(ResultSuccess);
-
- state = State::Initialized;
- }
-
- void GetState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFC, "called");
-
- IPC::ResponseBuilder rb{ctx, 3, 0};
- rb.Push(ResultSuccess);
- rb.PushRaw<u32>(static_cast<u32>(state));
- }
-
- void ListDevices(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u32 array_size = rp.Pop<u32>();
- LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
-
- ctx.WriteBuffer(device_handle);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(1);
- }
-
- void GetNpadId(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 dev_handle = rp.Pop<u64>();
- LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(npad_id);
- }
-
- void AttachActivateEvent(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 dev_handle = rp.Pop<u64>();
- LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(nfp_interface.GetNFCEvent());
- has_attached_handle = true;
- }
-
- void AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const u64 dev_handle = rp.Pop<u64>();
- LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(deactivate_event->GetReadableEvent());
- }
-
- void StopDetection(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
-
- switch (device_state) {
- case DeviceState::TagFound:
- case DeviceState::TagNearby:
- deactivate_event->GetWritableEvent().Signal();
- device_state = DeviceState::Initialized;
- break;
- case DeviceState::SearchingForTag:
- case DeviceState::TagRemoved:
- device_state = DeviceState::Initialized;
- break;
- default:
- break;
- }
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetDeviceState(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(static_cast<u32>(device_state));
- }
-
- void StartDetection(Kernel::HLERequestContext& ctx) {
+ void CreateUserInterface(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
- if (device_state == DeviceState::Initialized || device_state == DeviceState::TagRemoved) {
- device_state = DeviceState::SearchingForTag;
+ if (user_interface == nullptr) {
+ user_interface = std::make_shared<IUser>(system);
}
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetTagInfo(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- const auto& amiibo = nfp_interface.GetAmiiboBuffer();
- const TagInfo tag_info{
- .uuid = amiibo.uuid,
- .uuid_length = static_cast<u8>(amiibo.uuid.size()),
- .padding_1 = {},
- .protocol = 1, // TODO(ogniK): Figure out actual values
- .tag_type = 2,
- .padding_2 = {},
- };
- ctx.WriteBuffer(tag_info);
- rb.Push(ResultSuccess);
- }
-
- void Mount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
-
- device_state = DeviceState::TagNearby;
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void GetModelInfo(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
-
- IPC::ResponseBuilder rb{ctx, 2};
- const auto& amiibo = nfp_interface.GetAmiiboBuffer();
- ctx.WriteBuffer(amiibo.model_info);
- rb.Push(ResultSuccess);
- }
-
- void Unmount(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
-
- device_state = DeviceState::TagFound;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void Finalize(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
-
- device_state = DeviceState::Finalized;
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
-
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(ResultSuccess);
- rb.PushCopyObjects(availability_change_event->GetReadableEvent());
- }
-
- void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
-
- // TODO(ogniK): Pull Mii and owner data from amiibo
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IUser>(user_interface);
}
- void GetCommonInfo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
-
- // TODO(ogniK): Pull common information from amiibo
-
- CommonInfo common_info{};
- common_info.application_area_size = 0;
- ctx.WriteBuffer(common_info);
-
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
-
- void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA);
- }
-
- void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
- // We don't need to worry about this since we can just open the file
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
- }
-
- void GetApplicationArea(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NFP, "(STUBBED) called");
-
- // TODO(ogniK): Pull application area from amiibo
-
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw<u32>(0); // This is from the GetCommonInfo stub
- }
-
- Module::Interface& nfp_interface;
- KernelHelpers::ServiceContext& service_context;
-
- bool has_attached_handle{};
- const u64 device_handle{0}; // Npad device 1
- const u32 npad_id{0}; // Player 1 controller
- State state{State::NonInitialized};
- DeviceState device_state{DeviceState::Initialized};
- Kernel::KEvent* deactivate_event;
- Kernel::KEvent* availability_change_event;
+ std::shared_ptr<IUser> user_interface;
};
-void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
-
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IUser>(*this, system, service_context);
-}
-
-bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
- if (buffer.size() < sizeof(AmiiboFile)) {
- return false;
- }
-
- std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
- nfc_tag_load->GetWritableEvent().Signal();
- return true;
-}
-
-Kernel::KReadableEvent& Module::Interface::GetNFCEvent() {
- return nfc_tag_load->GetReadableEvent();
-}
-
-const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
- return amiibo;
-}
-
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
- auto module = std::make_shared<Module>();
- std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);
+ std::make_shared<IUserManager>(system)->InstallAsService(service_manager);
}
} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 95c127efb..a25c362b8 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -1,57 +1,12 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#include <array>
-#include <vector>
-
-#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/service.h"
-namespace Kernel {
-class KEvent;
-}
-
namespace Service::NFP {
-class Module final {
-public:
- class Interface : public ServiceFramework<Interface> {
- public:
- explicit Interface(std::shared_ptr<Module> module_, Core::System& system_,
- const char* name);
- ~Interface() override;
-
- struct ModelInfo {
- std::array<u8, 0x8> amiibo_identification_block;
- INSERT_PADDING_BYTES(0x38);
- };
- static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
-
- struct AmiiboFile {
- std::array<u8, 10> uuid;
- INSERT_PADDING_BYTES(0x4a);
- ModelInfo model_info;
- };
- static_assert(sizeof(AmiiboFile) == 0x94, "AmiiboFile is an invalid size");
-
- void CreateUserInterface(Kernel::HLERequestContext& ctx);
- bool LoadAmiibo(const std::vector<u8>& buffer);
- Kernel::KReadableEvent& GetNFCEvent();
- const AmiiboFile& GetAmiiboBuffer() const;
-
- protected:
- std::shared_ptr<Module> module;
-
- private:
- KernelHelpers::ServiceContext service_context;
- Kernel::KEvent* nfc_tag_load;
- AmiiboFile amiibo{};
- };
-};
-
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_device.cpp b/src/core/hle/service/nfp/nfp_device.cpp
new file mode 100644
index 000000000..ec895ac01
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_device.cpp
@@ -0,0 +1,681 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <array>
+#include <atomic>
+
+#include "common/fs/file.h"
+#include "common/fs/path_util.h"
+#include "common/input.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "common/tiny_mt.h"
+#include "core/core.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hid/hid_types.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/mii/mii_manager.h"
+#include "core/hle/service/nfp/amiibo_crypto.h"
+#include "core/hle/service/nfp/nfp.h"
+#include "core/hle/service/nfp/nfp_device.h"
+#include "core/hle/service/nfp/nfp_result.h"
+#include "core/hle/service/nfp/nfp_user.h"
+#include "core/hle/service/time/time_manager.h"
+#include "core/hle/service/time/time_zone_content_manager.h"
+#include "core/hle/service/time/time_zone_types.h"
+
+namespace Service::NFP {
+NfpDevice::NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
+ KernelHelpers::ServiceContext& service_context_,
+ Kernel::KEvent* availability_change_event_)
+ : npad_id{npad_id_}, system{system_}, service_context{service_context_},
+ availability_change_event{availability_change_event_} {
+ activate_event = service_context.CreateEvent("IUser:NFPActivateEvent");
+ deactivate_event = service_context.CreateEvent("IUser:NFPDeactivateEvent");
+ npad_device = system.HIDCore().GetEmulatedController(npad_id);
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { NpadUpdate(type); },
+ .is_npad_service = false,
+ };
+ is_controller_set = true;
+ callback_key = npad_device->SetCallback(engine_callback);
+
+ auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()};
+ current_posix_time = standard_steady_clock.GetCurrentTimePoint(system).time_point;
+}
+
+NfpDevice::~NfpDevice() {
+ if (!is_controller_set) {
+ return;
+ }
+ npad_device->DeleteCallback(callback_key);
+ is_controller_set = false;
+};
+
+void NfpDevice::NpadUpdate(Core::HID::ControllerTriggerType type) {
+ if (type == Core::HID::ControllerTriggerType::Connected ||
+ type == Core::HID::ControllerTriggerType::Disconnected) {
+ availability_change_event->GetWritableEvent().Signal();
+ return;
+ }
+
+ if (type != Core::HID::ControllerTriggerType::Nfc) {
+ return;
+ }
+
+ if (!npad_device->IsConnected()) {
+ return;
+ }
+
+ const auto nfc_status = npad_device->GetNfc();
+ switch (nfc_status.state) {
+ case Common::Input::NfcState::NewAmiibo:
+ LoadAmiibo(nfc_status.data);
+ break;
+ case Common::Input::NfcState::AmiiboRemoved:
+ if (device_state != DeviceState::SearchingForTag) {
+ CloseAmiibo();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+bool NfpDevice::LoadAmiibo(std::span<const u8> data) {
+ if (device_state != DeviceState::SearchingForTag) {
+ LOG_ERROR(Service_NFP, "Game is not looking for amiibos, current state {}", device_state);
+ return false;
+ }
+
+ if (data.size() != sizeof(EncryptedNTAG215File)) {
+ LOG_ERROR(Service_NFP, "Not an amiibo, size={}", data.size());
+ return false;
+ }
+
+ memcpy(&encrypted_tag_data, data.data(), sizeof(EncryptedNTAG215File));
+
+ device_state = DeviceState::TagFound;
+ deactivate_event->GetReadableEvent().Clear();
+ activate_event->GetWritableEvent().Signal();
+ return true;
+}
+
+void NfpDevice::CloseAmiibo() {
+ LOG_INFO(Service_NFP, "Remove amiibo");
+
+ if (device_state == DeviceState::TagMounted) {
+ Unmount();
+ }
+
+ device_state = DeviceState::TagRemoved;
+ encrypted_tag_data = {};
+ tag_data = {};
+ activate_event->GetReadableEvent().Clear();
+ deactivate_event->GetWritableEvent().Signal();
+}
+
+Kernel::KReadableEvent& NfpDevice::GetActivateEvent() const {
+ return activate_event->GetReadableEvent();
+}
+
+Kernel::KReadableEvent& NfpDevice::GetDeactivateEvent() const {
+ return deactivate_event->GetReadableEvent();
+}
+
+void NfpDevice::Initialize() {
+ device_state = npad_device->HasNfc() ? DeviceState::Initialized : DeviceState::Unavailable;
+ encrypted_tag_data = {};
+ tag_data = {};
+}
+
+void NfpDevice::Finalize() {
+ if (device_state == DeviceState::TagMounted) {
+ Unmount();
+ }
+ if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
+ StopDetection();
+ }
+ device_state = DeviceState::Unavailable;
+}
+
+Result NfpDevice::StartDetection(s32 protocol_) {
+ if (device_state != DeviceState::Initialized && device_state != DeviceState::TagRemoved) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ return WrongDeviceState;
+ }
+
+ if (!npad_device->SetPollingMode(Common::Input::PollingMode::NFC)) {
+ LOG_ERROR(Service_NFP, "Nfc not supported");
+ return NfcDisabled;
+ }
+
+ device_state = DeviceState::SearchingForTag;
+ protocol = protocol_;
+ return ResultSuccess;
+}
+
+Result NfpDevice::StopDetection() {
+ npad_device->SetPollingMode(Common::Input::PollingMode::Active);
+
+ if (device_state == DeviceState::Initialized) {
+ return ResultSuccess;
+ }
+
+ if (device_state == DeviceState::TagFound || device_state == DeviceState::TagMounted) {
+ CloseAmiibo();
+ return ResultSuccess;
+ }
+ if (device_state == DeviceState::SearchingForTag || device_state == DeviceState::TagRemoved) {
+ device_state = DeviceState::Initialized;
+ return ResultSuccess;
+ }
+
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ return WrongDeviceState;
+}
+
+Result NfpDevice::Flush() {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ auto& settings = tag_data.settings;
+
+ const auto& current_date = GetAmiiboDate(current_posix_time);
+ if (settings.write_date.raw_date != current_date.raw_date) {
+ settings.write_date = current_date;
+ settings.crc_counter++;
+ // TODO: Find how to calculate the crc check
+ // settings.crc = CalculateCRC(settings);
+ }
+
+ tag_data.write_counter++;
+
+ if (!AmiiboCrypto::EncodeAmiibo(tag_data, encrypted_tag_data)) {
+ LOG_ERROR(Service_NFP, "Failed to encode data");
+ return WriteAmiiboFailed;
+ }
+
+ std::vector<u8> data(sizeof(encrypted_tag_data));
+ memcpy(data.data(), &encrypted_tag_data, sizeof(encrypted_tag_data));
+
+ if (!npad_device->WriteNfc(data)) {
+ LOG_ERROR(Service_NFP, "Error writing to file");
+ return WriteAmiiboFailed;
+ }
+
+ is_data_moddified = false;
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::Mount(MountTarget mount_target_) {
+ if (device_state != DeviceState::TagFound) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ return WrongDeviceState;
+ }
+
+ if (!AmiiboCrypto::IsAmiiboValid(encrypted_tag_data)) {
+ LOG_ERROR(Service_NFP, "Not an amiibo");
+ return NotAnAmiibo;
+ }
+
+ if (!AmiiboCrypto::DecodeAmiibo(encrypted_tag_data, tag_data)) {
+ LOG_ERROR(Service_NFP, "Can't decode amiibo {}", device_state);
+ return CorruptedData;
+ }
+
+ device_state = DeviceState::TagMounted;
+ mount_target = mount_target_;
+ return ResultSuccess;
+}
+
+Result NfpDevice::Unmount() {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ // Save data before unloading the amiibo
+ if (is_data_moddified) {
+ Flush();
+ }
+
+ device_state = DeviceState::TagFound;
+ mount_target = MountTarget::None;
+ is_app_area_open = false;
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::GetTagInfo(TagInfo& tag_info) const {
+ if (device_state != DeviceState::TagFound && device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ tag_info = {
+ .uuid = encrypted_tag_data.uuid.uid,
+ .uuid_length = static_cast<u8>(encrypted_tag_data.uuid.uid.size()),
+ .protocol = TagProtocol::TypeA,
+ .tag_type = TagType::Type2,
+ };
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::GetCommonInfo(CommonInfo& common_info) const {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ const auto& settings = tag_data.settings;
+
+ // TODO: Validate this data
+ common_info = {
+ .last_write_date = settings.write_date.GetWriteDate(),
+ .write_counter = tag_data.write_counter,
+ .version = 0,
+ .application_area_size = sizeof(ApplicationArea),
+ };
+ return ResultSuccess;
+}
+
+Result NfpDevice::GetModelInfo(ModelInfo& model_info) const {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ const auto& model_info_data = encrypted_tag_data.user_memory.model_info;
+ model_info = {
+ .character_id = model_info_data.character_id,
+ .character_variant = model_info_data.character_variant,
+ .amiibo_type = model_info_data.amiibo_type,
+ .model_number = model_info_data.model_number,
+ .series = model_info_data.series,
+ };
+ return ResultSuccess;
+}
+
+Result NfpDevice::GetRegisterInfo(RegisterInfo& register_info) const {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ if (tag_data.settings.settings.amiibo_initialized == 0) {
+ return RegistrationIsNotInitialized;
+ }
+
+ Service::Mii::MiiManager manager;
+ const auto& settings = tag_data.settings;
+
+ // TODO: Validate this data
+ register_info = {
+ .mii_char_info = manager.ConvertV3ToCharInfo(tag_data.owner_mii),
+ .creation_date = settings.init_date.GetWriteDate(),
+ .amiibo_name = GetAmiiboName(settings),
+ .font_region = {},
+ };
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::SetNicknameAndOwner(const AmiiboName& amiibo_name) {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ Service::Mii::MiiManager manager;
+ auto& settings = tag_data.settings;
+
+ settings.init_date = GetAmiiboDate(current_posix_time);
+ settings.write_date = GetAmiiboDate(current_posix_time);
+ settings.crc_counter++;
+ // TODO: Find how to calculate the crc check
+ // settings.crc = CalculateCRC(settings);
+
+ SetAmiiboName(settings, amiibo_name);
+ tag_data.owner_mii = manager.ConvertCharInfoToV3(manager.BuildDefault(0));
+ settings.settings.amiibo_initialized.Assign(1);
+
+ return Flush();
+}
+
+Result NfpDevice::RestoreAmiibo() {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ // TODO: Load amiibo from backup on system
+ LOG_ERROR(Service_NFP, "Not Implemented");
+ return ResultSuccess;
+}
+
+Result NfpDevice::DeleteAllData() {
+ const auto result = DeleteApplicationArea();
+ if (result.IsError()) {
+ return result;
+ }
+
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ Common::TinyMT rng{};
+ rng.GenerateRandomBytes(&tag_data.owner_mii, sizeof(tag_data.owner_mii));
+ tag_data.settings.settings.amiibo_initialized.Assign(0);
+
+ return Flush();
+}
+
+Result NfpDevice::OpenApplicationArea(u32 access_id) {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
+ LOG_WARNING(Service_NFP, "Application area is not initialized");
+ return ApplicationAreaIsNotInitialized;
+ }
+
+ if (tag_data.application_area_id != access_id) {
+ LOG_WARNING(Service_NFP, "Wrong application area id");
+ return WrongApplicationAreaId;
+ }
+
+ is_app_area_open = true;
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::GetApplicationArea(std::vector<u8>& data) const {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ if (!is_app_area_open) {
+ LOG_ERROR(Service_NFP, "Application area is not open");
+ return WrongDeviceState;
+ }
+
+ if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
+ LOG_ERROR(Service_NFP, "Application area is not initialized");
+ return ApplicationAreaIsNotInitialized;
+ }
+
+ if (data.size() > sizeof(ApplicationArea)) {
+ data.resize(sizeof(ApplicationArea));
+ }
+
+ memcpy(data.data(), tag_data.application_area.data(), data.size());
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::SetApplicationArea(std::span<const u8> data) {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ if (!is_app_area_open) {
+ LOG_ERROR(Service_NFP, "Application area is not open");
+ return WrongDeviceState;
+ }
+
+ if (tag_data.settings.settings.appdata_initialized.Value() == 0) {
+ LOG_ERROR(Service_NFP, "Application area is not initialized");
+ return ApplicationAreaIsNotInitialized;
+ }
+
+ if (data.size() > sizeof(ApplicationArea)) {
+ LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
+ return ResultUnknown;
+ }
+
+ Common::TinyMT rng{};
+ std::memcpy(tag_data.application_area.data(), data.data(), data.size());
+ // Fill remaining data with random numbers
+ rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
+ sizeof(ApplicationArea) - data.size());
+
+ tag_data.applicaton_write_counter++;
+ is_data_moddified = true;
+
+ return ResultSuccess;
+}
+
+Result NfpDevice::CreateApplicationArea(u32 access_id, std::span<const u8> data) {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (tag_data.settings.settings.appdata_initialized.Value() != 0) {
+ LOG_ERROR(Service_NFP, "Application area already exist");
+ return ApplicationAreaExist;
+ }
+
+ return RecreateApplicationArea(access_id, data);
+}
+
+Result NfpDevice::RecreateApplicationArea(u32 access_id, std::span<const u8> data) {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ if (data.size() > sizeof(ApplicationArea)) {
+ LOG_ERROR(Service_NFP, "Wrong data size {}", data.size());
+ return WrongApplicationAreaSize;
+ }
+
+ Common::TinyMT rng{};
+ std::memcpy(tag_data.application_area.data(), data.data(), data.size());
+ // Fill remaining data with random numbers
+ rng.GenerateRandomBytes(tag_data.application_area.data() + data.size(),
+ sizeof(ApplicationArea) - data.size());
+
+ // TODO: Investigate why the title id needs to be moddified
+ tag_data.title_id = system.GetCurrentProcessProgramID();
+ tag_data.title_id = tag_data.title_id | 0x30000000ULL;
+ tag_data.settings.settings.appdata_initialized.Assign(1);
+ tag_data.application_area_id = access_id;
+ tag_data.applicaton_write_counter++;
+ tag_data.unknown = {};
+
+ return Flush();
+}
+
+Result NfpDevice::DeleteApplicationArea() {
+ if (device_state != DeviceState::TagMounted) {
+ LOG_ERROR(Service_NFP, "Wrong device state {}", device_state);
+ if (device_state == DeviceState::TagRemoved) {
+ return TagRemoved;
+ }
+ return WrongDeviceState;
+ }
+
+ if (mount_target == MountTarget::None || mount_target == MountTarget::Rom) {
+ LOG_ERROR(Service_NFP, "Amiibo is read only", device_state);
+ return WrongDeviceState;
+ }
+
+ Common::TinyMT rng{};
+ rng.GenerateRandomBytes(tag_data.application_area.data(), sizeof(ApplicationArea));
+ rng.GenerateRandomBytes(&tag_data.title_id, sizeof(u64));
+ rng.GenerateRandomBytes(&tag_data.application_area_id, sizeof(u32));
+ tag_data.settings.settings.appdata_initialized.Assign(0);
+ tag_data.applicaton_write_counter++;
+ tag_data.unknown = {};
+
+ return Flush();
+}
+
+u64 NfpDevice::GetHandle() const {
+ // Generate a handle based of the npad id
+ return static_cast<u64>(npad_id);
+}
+
+u32 NfpDevice::GetApplicationAreaSize() const {
+ return sizeof(ApplicationArea);
+}
+
+DeviceState NfpDevice::GetCurrentState() const {
+ return device_state;
+}
+
+Core::HID::NpadIdType NfpDevice::GetNpadId() const {
+ return npad_id;
+}
+
+AmiiboName NfpDevice::GetAmiiboName(const AmiiboSettings& settings) const {
+ std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
+ AmiiboName amiibo_name{};
+
+ // Convert from big endian to little endian
+ for (std::size_t i = 0; i < amiibo_name_length; i++) {
+ settings_amiibo_name[i] = static_cast<u16>(settings.amiibo_name[i]);
+ }
+
+ // Convert from utf16 to utf8
+ const auto amiibo_name_utf8 = Common::UTF16ToUTF8(settings_amiibo_name.data());
+ memcpy(amiibo_name.data(), amiibo_name_utf8.data(), amiibo_name_utf8.size());
+
+ return amiibo_name;
+}
+
+void NfpDevice::SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name) {
+ std::array<char16_t, amiibo_name_length> settings_amiibo_name{};
+
+ // Convert from utf8 to utf16
+ const auto amiibo_name_utf16 = Common::UTF8ToUTF16(amiibo_name.data());
+ memcpy(settings_amiibo_name.data(), amiibo_name_utf16.data(),
+ amiibo_name_utf16.size() * sizeof(char16_t));
+
+ // Convert from little endian to big endian
+ for (std::size_t i = 0; i < amiibo_name_length; i++) {
+ settings.amiibo_name[i] = static_cast<u16_be>(settings_amiibo_name[i]);
+ }
+}
+
+AmiiboDate NfpDevice::GetAmiiboDate(s64 posix_time) const {
+ const auto& time_zone_manager =
+ system.GetTimeManager().GetTimeZoneContentManager().GetTimeZoneManager();
+ Time::TimeZone::CalendarInfo calendar_info{};
+ AmiiboDate amiibo_date{};
+
+ amiibo_date.SetYear(2000);
+ amiibo_date.SetMonth(1);
+ amiibo_date.SetDay(1);
+
+ if (time_zone_manager.ToCalendarTime({}, posix_time, calendar_info) == ResultSuccess) {
+ amiibo_date.SetYear(calendar_info.time.year);
+ amiibo_date.SetMonth(calendar_info.time.month);
+ amiibo_date.SetDay(calendar_info.time.day);
+ }
+
+ return amiibo_date;
+}
+
+} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_device.h b/src/core/hle/service/nfp/nfp_device.h
new file mode 100644
index 000000000..a5b72cf19
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_device.h
@@ -0,0 +1,101 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_funcs.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/mii/types.h"
+#include "core/hle/service/nfp/nfp_types.h"
+#include "core/hle/service/service.h"
+
+namespace Kernel {
+class KEvent;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Core {
+class System;
+} // namespace Core
+
+namespace Core::HID {
+class EmulatedController;
+enum class ControllerTriggerType;
+enum class NpadIdType : u32;
+} // namespace Core::HID
+
+namespace Service::NFP {
+class NfpDevice {
+public:
+ NfpDevice(Core::HID::NpadIdType npad_id_, Core::System& system_,
+ KernelHelpers::ServiceContext& service_context_,
+ Kernel::KEvent* availability_change_event_);
+ ~NfpDevice();
+
+ void Initialize();
+ void Finalize();
+
+ Result StartDetection(s32 protocol_);
+ Result StopDetection();
+ Result Mount(MountTarget mount_target);
+ Result Unmount();
+ Result Flush();
+
+ Result GetTagInfo(TagInfo& tag_info) const;
+ Result GetCommonInfo(CommonInfo& common_info) const;
+ Result GetModelInfo(ModelInfo& model_info) const;
+ Result GetRegisterInfo(RegisterInfo& register_info) const;
+
+ Result SetNicknameAndOwner(const AmiiboName& amiibo_name);
+ Result RestoreAmiibo();
+ Result DeleteAllData();
+
+ Result OpenApplicationArea(u32 access_id);
+ Result GetApplicationArea(std::vector<u8>& data) const;
+ Result SetApplicationArea(std::span<const u8> data);
+ Result CreateApplicationArea(u32 access_id, std::span<const u8> data);
+ Result RecreateApplicationArea(u32 access_id, std::span<const u8> data);
+ Result DeleteApplicationArea();
+
+ u64 GetHandle() const;
+ u32 GetApplicationAreaSize() const;
+ DeviceState GetCurrentState() const;
+ Core::HID::NpadIdType GetNpadId() const;
+
+ Kernel::KReadableEvent& GetActivateEvent() const;
+ Kernel::KReadableEvent& GetDeactivateEvent() const;
+
+private:
+ void NpadUpdate(Core::HID::ControllerTriggerType type);
+ bool LoadAmiibo(std::span<const u8> data);
+ void CloseAmiibo();
+
+ AmiiboName GetAmiiboName(const AmiiboSettings& settings) const;
+ void SetAmiiboName(AmiiboSettings& settings, const AmiiboName& amiibo_name);
+ AmiiboDate GetAmiiboDate(s64 posix_time) const;
+
+ bool is_controller_set{};
+ int callback_key;
+ const Core::HID::NpadIdType npad_id;
+ Core::System& system;
+ Core::HID::EmulatedController* npad_device = nullptr;
+ KernelHelpers::ServiceContext& service_context;
+ Kernel::KEvent* activate_event = nullptr;
+ Kernel::KEvent* deactivate_event = nullptr;
+ Kernel::KEvent* availability_change_event = nullptr;
+
+ bool is_data_moddified{};
+ bool is_app_area_open{};
+ s32 protocol{};
+ s64 current_posix_time{};
+ MountTarget mount_target{MountTarget::None};
+ DeviceState device_state{DeviceState::Unavailable};
+
+ NTAG215File tag_data{};
+ EncryptedNTAG215File encrypted_tag_data{};
+};
+
+} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_result.h b/src/core/hle/service/nfp/nfp_result.h
new file mode 100644
index 000000000..d8e4cf094
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_result.h
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::NFP {
+
+constexpr Result DeviceNotFound(ErrorModule::NFP, 64);
+constexpr Result InvalidArgument(ErrorModule::NFP, 65);
+constexpr Result WrongApplicationAreaSize(ErrorModule::NFP, 68);
+constexpr Result WrongDeviceState(ErrorModule::NFP, 73);
+constexpr Result NfcDisabled(ErrorModule::NFP, 80);
+constexpr Result WriteAmiiboFailed(ErrorModule::NFP, 88);
+constexpr Result TagRemoved(ErrorModule::NFP, 97);
+constexpr Result RegistrationIsNotInitialized(ErrorModule::NFP, 120);
+constexpr Result ApplicationAreaIsNotInitialized(ErrorModule::NFP, 128);
+constexpr Result CorruptedData(ErrorModule::NFP, 144);
+constexpr Result WrongApplicationAreaId(ErrorModule::NFP, 152);
+constexpr Result ApplicationAreaExist(ErrorModule::NFP, 168);
+constexpr Result NotAnAmiibo(ErrorModule::NFP, 178);
+
+} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_types.h b/src/core/hle/service/nfp/nfp_types.h
new file mode 100644
index 000000000..867ea2f36
--- /dev/null
+++ b/src/core/hle/service/nfp/nfp_types.h
@@ -0,0 +1,320 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/swap.h"
+#include "core/hle/service/mii/types.h"
+
+namespace Service::NFP {
+static constexpr std::size_t amiibo_name_length = 0xA;
+
+enum class ServiceType : u32 {
+ User,
+ Debug,
+ System,
+};
+
+enum class State : u32 {
+ NonInitialized,
+ Initialized,
+};
+
+enum class DeviceState : u32 {
+ Initialized,
+ SearchingForTag,
+ TagFound,
+ TagRemoved,
+ TagMounted,
+ Unavailable,
+ Finalized,
+};
+
+enum class ModelType : u32 {
+ Amiibo,
+};
+
+enum class MountTarget : u32 {
+ None,
+ Rom,
+ Ram,
+ All,
+};
+
+enum class AmiiboType : u8 {
+ Figure,
+ Card,
+ Yarn,
+};
+
+enum class AmiiboSeries : u8 {
+ SuperSmashBros,
+ SuperMario,
+ ChibiRobo,
+ YoshiWoollyWorld,
+ Splatoon,
+ AnimalCrossing,
+ EightBitMario,
+ Skylanders,
+ Unknown8,
+ TheLegendOfZelda,
+ ShovelKnight,
+ Unknown11,
+ Kiby,
+ Pokemon,
+ MarioSportsSuperstars,
+ MonsterHunter,
+ BoxBoy,
+ Pikmin,
+ FireEmblem,
+ Metroid,
+ Others,
+ MegaMan,
+ Diablo,
+};
+
+enum class TagType : u32 {
+ None,
+ Type1, // ISO14443A RW 96-2k bytes 106kbit/s
+ Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
+ Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
+ Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
+ Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
+};
+
+enum class PackedTagType : u8 {
+ None,
+ Type1, // ISO14443A RW 96-2k bytes 106kbit/s
+ Type2, // ISO14443A RW/RO 540 bytes 106kbit/s
+ Type3, // Sony Felica RW/RO 2k bytes 212kbit/s
+ Type4, // ISO14443A RW/RO 4k-32k bytes 424kbit/s
+ Type5, // ISO15693 RW/RO 540 bytes 106kbit/s
+};
+
+enum class TagProtocol : u32 {
+ None,
+ TypeA, // ISO14443A
+ TypeB, // ISO14443B
+ TypeF, // Sony Felica
+};
+
+using UniqueSerialNumber = std::array<u8, 7>;
+using LockBytes = std::array<u8, 2>;
+using HashData = std::array<u8, 0x20>;
+using ApplicationArea = std::array<u8, 0xD8>;
+using AmiiboName = std::array<char, (amiibo_name_length * 4) + 1>;
+
+struct TagUuid {
+ UniqueSerialNumber uid;
+ u8 nintendo_id;
+ LockBytes lock_bytes;
+};
+static_assert(sizeof(TagUuid) == 10, "TagUuid is an invalid size");
+
+struct WriteDate {
+ u16 year;
+ u8 month;
+ u8 day;
+};
+static_assert(sizeof(WriteDate) == 0x4, "WriteDate is an invalid size");
+
+struct AmiiboDate {
+ u16 raw_date{};
+
+ u16 GetValue() const {
+ return Common::swap16(raw_date);
+ }
+
+ u16 GetYear() const {
+ return static_cast<u16>(((GetValue() & 0xFE00) >> 9) + 2000);
+ }
+ u8 GetMonth() const {
+ return static_cast<u8>((GetValue() & 0x01E0) >> 5);
+ }
+ u8 GetDay() const {
+ return static_cast<u8>(GetValue() & 0x001F);
+ }
+
+ WriteDate GetWriteDate() const {
+ if (!IsValidDate()) {
+ return {
+ .year = 2000,
+ .month = 1,
+ .day = 1,
+ };
+ }
+ return {
+ .year = GetYear(),
+ .month = GetMonth(),
+ .day = GetDay(),
+ };
+ }
+
+ void SetYear(u16 year) {
+ const u16 year_converted = static_cast<u16>((year - 2000) << 9);
+ raw_date = Common::swap16((GetValue() & ~0xFE00) | year_converted);
+ }
+ void SetMonth(u8 month) {
+ const u16 month_converted = static_cast<u16>(month << 5);
+ raw_date = Common::swap16((GetValue() & ~0x01E0) | month_converted);
+ }
+ void SetDay(u8 day) {
+ const u16 day_converted = static_cast<u16>(day);
+ raw_date = Common::swap16((GetValue() & ~0x001F) | day_converted);
+ }
+
+ bool IsValidDate() const {
+ const bool is_day_valid = GetDay() > 0 && GetDay() < 32;
+ const bool is_month_valid = GetMonth() >= 0 && GetMonth() < 13;
+ const bool is_year_valid = GetYear() >= 2000;
+ return is_year_valid && is_month_valid && is_day_valid;
+ }
+};
+static_assert(sizeof(AmiiboDate) == 2, "AmiiboDate is an invalid size");
+
+struct Settings {
+ union {
+ u8 raw{};
+
+ BitField<4, 1, u8> amiibo_initialized;
+ BitField<5, 1, u8> appdata_initialized;
+ };
+};
+static_assert(sizeof(Settings) == 1, "AmiiboDate is an invalid size");
+
+struct AmiiboSettings {
+ Settings settings;
+ u8 country_code_id;
+ u16_be crc_counter; // Incremented each time crc is changed
+ AmiiboDate init_date;
+ AmiiboDate write_date;
+ u32_be crc;
+ std::array<u16_be, amiibo_name_length> amiibo_name; // UTF-16 text
+};
+static_assert(sizeof(AmiiboSettings) == 0x20, "AmiiboSettings is an invalid size");
+
+struct AmiiboModelInfo {
+ u16 character_id;
+ u8 character_variant;
+ AmiiboType amiibo_type;
+ u16_be model_number;
+ AmiiboSeries series;
+ PackedTagType tag_type;
+ INSERT_PADDING_BYTES(0x4); // Unknown
+};
+static_assert(sizeof(AmiiboModelInfo) == 0xC, "AmiiboModelInfo is an invalid size");
+
+struct NTAG215Password {
+ u32 PWD; // Password to allow write access
+ u16 PACK; // Password acknowledge reply
+ u16 RFUI; // Reserved for future use
+};
+static_assert(sizeof(NTAG215Password) == 0x8, "NTAG215Password is an invalid size");
+
+#pragma pack(1)
+struct EncryptedAmiiboFile {
+ u8 constant_value; // Must be A5
+ u16_be write_counter; // Number of times the amiibo has been written?
+ INSERT_PADDING_BYTES(0x1); // Unknown 1
+ AmiiboSettings settings; // Encrypted amiibo settings
+ HashData hmac_tag; // Hash
+ AmiiboModelInfo model_info; // Encrypted amiibo model info
+ HashData keygen_salt; // Salt
+ HashData hmac_data; // Hash
+ Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data
+ u64_be title_id; // Encrypted Game id
+ u16_be applicaton_write_counter; // Encrypted Counter
+ u32_be application_area_id; // Encrypted Game id
+ std::array<u8, 0x2> unknown;
+ std::array<u32, 0x8> unknown2;
+ ApplicationArea application_area; // Encrypted Game data
+};
+static_assert(sizeof(EncryptedAmiiboFile) == 0x1F8, "AmiiboFile is an invalid size");
+
+struct NTAG215File {
+ LockBytes lock_bytes; // Tag UUID
+ u16 static_lock; // Set defined pages as read only
+ u32 compability_container; // Defines available memory
+ HashData hmac_data; // Hash
+ u8 constant_value; // Must be A5
+ u16_be write_counter; // Number of times the amiibo has been written?
+ INSERT_PADDING_BYTES(0x1); // Unknown 1
+ AmiiboSettings settings;
+ Service::Mii::Ver3StoreData owner_mii; // Encrypted Mii data
+ u64_be title_id;
+ u16_be applicaton_write_counter; // Encrypted Counter
+ u32_be application_area_id;
+ std::array<u8, 0x2> unknown;
+ std::array<u32, 0x8> unknown2;
+ ApplicationArea application_area; // Encrypted Game data
+ HashData hmac_tag; // Hash
+ UniqueSerialNumber uid; // Unique serial number
+ u8 nintendo_id; // Tag UUID
+ AmiiboModelInfo model_info;
+ HashData keygen_salt; // Salt
+ u32 dynamic_lock; // Dynamic lock
+ u32 CFG0; // Defines memory protected by password
+ u32 CFG1; // Defines number of verification attempts
+ NTAG215Password password; // Password data
+};
+static_assert(sizeof(NTAG215File) == 0x21C, "NTAG215File is an invalid size");
+static_assert(std::is_trivially_copyable_v<NTAG215File>, "NTAG215File must be trivially copyable.");
+#pragma pack()
+
+struct EncryptedNTAG215File {
+ TagUuid uuid; // Unique serial number
+ u16 static_lock; // Set defined pages as read only
+ u32 compability_container; // Defines available memory
+ EncryptedAmiiboFile user_memory; // Writable data
+ u32 dynamic_lock; // Dynamic lock
+ u32 CFG0; // Defines memory protected by password
+ u32 CFG1; // Defines number of verification attempts
+ NTAG215Password password; // Password data
+};
+static_assert(sizeof(EncryptedNTAG215File) == 0x21C, "EncryptedNTAG215File is an invalid size");
+static_assert(std::is_trivially_copyable_v<EncryptedNTAG215File>,
+ "EncryptedNTAG215File must be trivially copyable.");
+
+struct TagInfo {
+ UniqueSerialNumber uuid;
+ INSERT_PADDING_BYTES(0x3);
+ u8 uuid_length;
+ INSERT_PADDING_BYTES(0x15);
+ TagProtocol protocol;
+ TagType tag_type;
+ INSERT_PADDING_BYTES(0x30);
+};
+static_assert(sizeof(TagInfo) == 0x58, "TagInfo is an invalid size");
+
+struct CommonInfo {
+ WriteDate last_write_date;
+ u16 write_counter;
+ u8 version;
+ INSERT_PADDING_BYTES(0x1);
+ u32 application_area_size;
+ INSERT_PADDING_BYTES(0x34);
+};
+static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
+
+struct ModelInfo {
+ u16 character_id;
+ u8 character_variant;
+ AmiiboType amiibo_type;
+ u16 model_number;
+ AmiiboSeries series;
+ INSERT_PADDING_BYTES(0x39); // Unknown
+};
+static_assert(sizeof(ModelInfo) == 0x40, "ModelInfo is an invalid size");
+
+struct RegisterInfo {
+ Service::Mii::CharInfo mii_char_info;
+ WriteDate creation_date;
+ AmiiboName amiibo_name;
+ u8 font_region;
+ INSERT_PADDING_BYTES(0x7A);
+};
+static_assert(sizeof(RegisterInfo) == 0x100, "RegisterInfo is an invalid size");
+
+} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 10b0ef944..4ed53b534 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -1,19 +1,674 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <array>
+#include <atomic>
+
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
+#include "core/hid/hid_types.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/service/mii/mii_manager.h"
+#include "core/hle/service/nfp/nfp_device.h"
+#include "core/hle/service/nfp/nfp_result.h"
#include "core/hle/service/nfp/nfp_user.h"
namespace Service::NFP {
-NFP_User::NFP_User(std::shared_ptr<Module> module_, Core::System& system_)
- : Interface(std::move(module_), system_, "nfp:user") {
+IUser::IUser(Core::System& system_)
+ : ServiceFramework{system_, "NFP::IUser"}, service_context{system_, service_name} {
static const FunctionInfo functions[] = {
- {0, &NFP_User::CreateUserInterface, "CreateUserInterface"},
+ {0, &IUser::Initialize, "Initialize"},
+ {1, &IUser::Finalize, "Finalize"},
+ {2, &IUser::ListDevices, "ListDevices"},
+ {3, &IUser::StartDetection, "StartDetection"},
+ {4, &IUser::StopDetection, "StopDetection"},
+ {5, &IUser::Mount, "Mount"},
+ {6, &IUser::Unmount, "Unmount"},
+ {7, &IUser::OpenApplicationArea, "OpenApplicationArea"},
+ {8, &IUser::GetApplicationArea, "GetApplicationArea"},
+ {9, &IUser::SetApplicationArea, "SetApplicationArea"},
+ {10, &IUser::Flush, "Flush"},
+ {11, &IUser::Restore, "Restore"},
+ {12, &IUser::CreateApplicationArea, "CreateApplicationArea"},
+ {13, &IUser::GetTagInfo, "GetTagInfo"},
+ {14, &IUser::GetRegisterInfo, "GetRegisterInfo"},
+ {15, &IUser::GetCommonInfo, "GetCommonInfo"},
+ {16, &IUser::GetModelInfo, "GetModelInfo"},
+ {17, &IUser::AttachActivateEvent, "AttachActivateEvent"},
+ {18, &IUser::AttachDeactivateEvent, "AttachDeactivateEvent"},
+ {19, &IUser::GetState, "GetState"},
+ {20, &IUser::GetDeviceState, "GetDeviceState"},
+ {21, &IUser::GetNpadId, "GetNpadId"},
+ {22, &IUser::GetApplicationAreaSize, "GetApplicationAreaSize"},
+ {23, &IUser::AttachAvailabilityChangeEvent, "AttachAvailabilityChangeEvent"},
+ {24, &IUser::RecreateApplicationArea, "RecreateApplicationArea"},
};
RegisterHandlers(functions);
+
+ availability_change_event = service_context.CreateEvent("IUser:AvailabilityChangeEvent");
+
+ for (u32 device_index = 0; device_index < 10; device_index++) {
+ devices[device_index] =
+ std::make_shared<NfpDevice>(Core::HID::IndexToNpadIdType(device_index), system,
+ service_context, availability_change_event);
+ }
+}
+
+void IUser::Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_NFC, "called");
+
+ state = State::Initialized;
+
+ for (auto& device : devices) {
+ device->Initialize();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2, 0};
+ rb.Push(ResultSuccess);
+}
+
+void IUser::Finalize(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_NFP, "called");
+
+ state = State::NonInitialized;
+
+ for (auto& device : devices) {
+ device->Finalize();
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IUser::ListDevices(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_NFP, "called");
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ if (!ctx.CanWriteBuffer()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidArgument);
+ return;
+ }
+
+ if (ctx.GetWriteBufferSize() == 0) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidArgument);
+ return;
+ }
+
+ std::vector<u64> nfp_devices;
+ const std::size_t max_allowed_devices = ctx.GetWriteBufferSize() / sizeof(u64);
+
+ for (auto& device : devices) {
+ if (nfp_devices.size() >= max_allowed_devices) {
+ continue;
+ }
+ if (device->GetCurrentState() != DeviceState::Unavailable) {
+ nfp_devices.push_back(device->GetHandle());
+ }
+ }
+
+ if (nfp_devices.size() == 0) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ ctx.WriteBuffer(nfp_devices);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(static_cast<s32>(nfp_devices.size()));
+}
+
+void IUser::StartDetection(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto nfp_protocol{rp.Pop<s32>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}, nfp_protocol={}", device_handle, nfp_protocol);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->StartDetection(nfp_protocol);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::StopDetection(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->StopDetection();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::Mount(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto model_type{rp.PopEnum<ModelType>()};
+ const auto mount_target{rp.PopEnum<MountTarget>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}, model_type={}, mount_target={}", device_handle,
+ model_type, mount_target);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->Mount(mount_target);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::Unmount(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->Unmount();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::OpenApplicationArea(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto access_id{rp.Pop<u32>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}, access_id={}", device_handle, access_id);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->OpenApplicationArea(access_id);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::GetApplicationArea(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto data_size = ctx.GetWriteBufferSize();
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ if (!ctx.CanWriteBuffer()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidArgument);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ std::vector<u8> data(data_size);
+ const auto result = device.value()->GetApplicationArea(data);
+ ctx.WriteBuffer(data);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push(static_cast<u32>(data_size));
+}
+
+void IUser::SetApplicationArea(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto data{ctx.ReadBuffer()};
+ LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}", device_handle, data.size());
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ if (!ctx.CanReadBuffer()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidArgument);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->SetApplicationArea(data);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::Flush(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->Flush();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::Restore(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_WARNING(Service_NFP, "(STUBBED) called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->RestoreAmiibo();
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::CreateApplicationArea(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto access_id{rp.Pop<u32>()};
+ const auto data{ctx.ReadBuffer()};
+ LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle,
+ access_id, data.size());
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ if (!ctx.CanReadBuffer()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(InvalidArgument);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->CreateApplicationArea(access_id, data);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::GetTagInfo(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ TagInfo tag_info{};
+ const auto result = device.value()->GetTagInfo(tag_info);
+ ctx.WriteBuffer(tag_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::GetRegisterInfo(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ RegisterInfo register_info{};
+ const auto result = device.value()->GetRegisterInfo(register_info);
+ ctx.WriteBuffer(register_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::GetCommonInfo(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ CommonInfo common_info{};
+ const auto result = device.value()->GetCommonInfo(common_info);
+ ctx.WriteBuffer(common_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::GetModelInfo(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_INFO(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ ModelInfo model_info{};
+ const auto result = device.value()->GetModelInfo(model_info);
+ ctx.WriteBuffer(model_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IUser::AttachActivateEvent(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(device.value()->GetActivateEvent());
+}
+
+void IUser::AttachDeactivateEvent(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(device.value()->GetDeactivateEvent());
+}
+
+void IUser::GetState(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3, 0};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(state);
+}
+
+void IUser::GetDeviceState(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(device.value()->GetCurrentState());
+}
+
+void IUser::GetNpadId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(device.value()->GetNpadId());
+}
+
+void IUser::GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ LOG_DEBUG(Service_NFP, "called, device_handle={}", device_handle);
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(device.value()->GetApplicationAreaSize());
}
-NFP_User::~NFP_User() = default;
+void IUser::AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_NFP, "called");
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(availability_change_event->GetReadableEvent());
+}
+
+void IUser::RecreateApplicationArea(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_handle{rp.Pop<u64>()};
+ const auto access_id{rp.Pop<u32>()};
+ const auto data{ctx.ReadBuffer()};
+ LOG_INFO(Service_NFP, "called, device_handle={}, data_size={}, access_id={}", device_handle,
+ access_id, data.size());
+
+ if (state == State::NonInitialized) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(NfcDisabled);
+ return;
+ }
+
+ auto device = GetNfpDevice(device_handle);
+
+ if (!device.has_value()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(DeviceNotFound);
+ return;
+ }
+
+ const auto result = device.value()->RecreateApplicationArea(access_id, data);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+std::optional<std::shared_ptr<NfpDevice>> IUser::GetNfpDevice(u64 handle) {
+ for (auto& device : devices) {
+ if (device->GetHandle() == handle) {
+ return device;
+ }
+ }
+ return std::nullopt;
+}
} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h
index 7f3c124f6..68c60ae82 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_user.h
@@ -1,17 +1,54 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nfp/nfp.h"
+#include "core/hle/service/nfp/nfp_types.h"
namespace Service::NFP {
+class NfpDevice;
-class NFP_User final : public Module::Interface {
+class IUser final : public ServiceFramework<IUser> {
public:
- explicit NFP_User(std::shared_ptr<Module> module_, Core::System& system_);
- ~NFP_User() override;
+ explicit IUser(Core::System& system_);
+
+private:
+ void Initialize(Kernel::HLERequestContext& ctx);
+ void Finalize(Kernel::HLERequestContext& ctx);
+ void ListDevices(Kernel::HLERequestContext& ctx);
+ void StartDetection(Kernel::HLERequestContext& ctx);
+ void StopDetection(Kernel::HLERequestContext& ctx);
+ void Mount(Kernel::HLERequestContext& ctx);
+ void Unmount(Kernel::HLERequestContext& ctx);
+ void OpenApplicationArea(Kernel::HLERequestContext& ctx);
+ void GetApplicationArea(Kernel::HLERequestContext& ctx);
+ void SetApplicationArea(Kernel::HLERequestContext& ctx);
+ void Flush(Kernel::HLERequestContext& ctx);
+ void Restore(Kernel::HLERequestContext& ctx);
+ void CreateApplicationArea(Kernel::HLERequestContext& ctx);
+ void GetTagInfo(Kernel::HLERequestContext& ctx);
+ void GetRegisterInfo(Kernel::HLERequestContext& ctx);
+ void GetCommonInfo(Kernel::HLERequestContext& ctx);
+ void GetModelInfo(Kernel::HLERequestContext& ctx);
+ void AttachActivateEvent(Kernel::HLERequestContext& ctx);
+ void AttachDeactivateEvent(Kernel::HLERequestContext& ctx);
+ void GetState(Kernel::HLERequestContext& ctx);
+ void GetDeviceState(Kernel::HLERequestContext& ctx);
+ void GetNpadId(Kernel::HLERequestContext& ctx);
+ void GetApplicationAreaSize(Kernel::HLERequestContext& ctx);
+ void AttachAvailabilityChangeEvent(Kernel::HLERequestContext& ctx);
+ void RecreateApplicationArea(Kernel::HLERequestContext& ctx);
+
+ std::optional<std::shared_ptr<NfpDevice>> GetNfpDevice(u64 handle);
+
+ KernelHelpers::ServiceContext service_context;
+
+ std::array<std::shared_ptr<NfpDevice>, 10> devices{};
+
+ State state{State::NonInitialized};
+ Kernel::KEvent* availability_change_event;
};
} // namespace Service::NFP
diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp
index 8ec7d5266..8af8a835d 100644
--- a/src/core/hle/service/ngct/ngct.cpp
+++ b/src/core/hle/service/ngct/ngct.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/string_util.h"
#include "core/core.h"
diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngct/ngct.h
index 1f2a47b78..370bd4a25 100644
--- a/src/core/hle/service/ngct/ngct.h
+++ b/src/core/hle/service/ngct/ngct.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index a253dd066..e3ef06481 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -1,14 +1,11 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
-#include "core/hle/kernel/kernel.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nifm/nifm.h"
-#include "core/hle/service/service.h"
namespace {
@@ -20,8 +17,8 @@ namespace {
} // Anonymous namespace
-#include "core/network/network.h"
-#include "core/network/network_interface.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
namespace Service::NIFM {
@@ -32,6 +29,19 @@ enum class RequestState : u32 {
Connected = 3,
};
+enum class InternetConnectionType : u8 {
+ WiFi = 1,
+ Ethernet = 2,
+};
+
+enum class InternetConnectionStatus : u8 {
+ ConnectingUnknown1,
+ ConnectingUnknown2,
+ ConnectingUnknown3,
+ ConnectingUnknown4,
+ Connected,
+};
+
struct IpAddressSetting {
bool is_automatic{};
Network::IPv4Address current_address{};
@@ -260,135 +270,45 @@ public:
}
};
-class IGeneralService final : public ServiceFramework<IGeneralService> {
-public:
- explicit IGeneralService(Core::System& system_);
-
-private:
- void GetClientId(Kernel::HLERequestContext& ctx) {
- static constexpr u32 client_id = 1;
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
+ static constexpr u32 client_id = 1;
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
- }
- void CreateScanRequest(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
+}
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IScanRequest>(system);
- }
- void CreateRequest(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IScanRequest>(system);
+}
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IRequest>(system);
- }
- void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- const auto net_iface = Network::GetSelectedNetworkInterface();
-
- const SfNetworkProfileData network_profile_data = [&net_iface] {
- if (!net_iface) {
- return SfNetworkProfileData{};
- }
-
- return SfNetworkProfileData{
- .ip_setting_data{
- .ip_address_setting{
- .is_automatic{true},
- .current_address{Network::TranslateIPv4(net_iface->ip_address)},
- .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
- .gateway{Network::TranslateIPv4(net_iface->gateway)},
- },
- .dns_setting{
- .is_automatic{true},
- .primary_dns{1, 1, 1, 1},
- .secondary_dns{1, 0, 0, 1},
- },
- .proxy_setting{
- .enabled{false},
- .port{},
- .proxy_server{},
- .automatic_auth_enabled{},
- .user{},
- .password{},
- },
- .mtu{1500},
- },
- .uuid{0xdeadbeef, 0xdeadbeef},
- .network_name{"yuzu Network"},
- .wireless_setting_data{
- .ssid_length{12},
- .ssid{"yuzu Network"},
- .passphrase{"yuzupassword"},
- },
- };
- }();
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- ctx.WriteBuffer(network_profile_data);
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IRequest>(system);
+}
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
- void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
- void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ const auto net_iface = Network::GetSelectedNetworkInterface();
- auto ipv4 = Network::GetHostIPv4Address();
- if (!ipv4) {
- LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
- ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
+ SfNetworkProfileData network_profile_data = [&net_iface] {
+ if (!net_iface) {
+ return SfNetworkProfileData{};
}
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw(*ipv4);
- }
- void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
-
- ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c,
- "SfNetworkProfileData is not the correct size");
- u128 uuid{};
- auto buffer = ctx.ReadBuffer();
- std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
-
- IPC::ResponseBuilder rb{ctx, 6, 0, 1};
-
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<INetworkProfile>(system);
- rb.PushRaw<u128>(uuid);
- }
- void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
-
- struct IpConfigInfo {
- IpAddressSetting ip_address_setting{};
- DnsSetting dns_setting{};
- };
- static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
- "IpConfigInfo has incorrect size.");
-
- const auto net_iface = Network::GetSelectedNetworkInterface();
-
- const IpConfigInfo ip_config_info = [&net_iface] {
- if (!net_iface) {
- return IpConfigInfo{};
- }
-
- return IpConfigInfo{
+ return SfNetworkProfileData{
+ .ip_setting_data{
.ip_address_setting{
.is_automatic{true},
.current_address{Network::TranslateIPv4(net_iface->ip_address)},
@@ -400,46 +320,178 @@ private:
.primary_dns{1, 1, 1, 1},
.secondary_dns{1, 0, 0, 1},
},
- };
- }();
+ .proxy_setting{
+ .enabled{false},
+ .port{},
+ .proxy_server{},
+ .automatic_auth_enabled{},
+ .user{},
+ .password{},
+ },
+ .mtu{1500},
+ },
+ .uuid{0xdeadbeef, 0xdeadbeef},
+ .network_name{"yuzu Network"},
+ .wireless_setting_data{
+ .ssid_length{12},
+ .ssid{"yuzu Network"},
+ .passphrase{"yuzupassword"},
+ },
+ };
+ }();
- IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
- rb.Push(ResultSuccess);
- rb.PushRaw<IpConfigInfo>(ip_config_info);
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ network_profile_data.ip_setting_data.ip_address_setting.current_address =
+ room_member->GetFakeIpAddress();
+ }
}
- void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(0);
+ ctx.WriteBuffer(network_profile_data);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ auto ipv4 = Network::GetHostIPv4Address();
+ if (!ipv4) {
+ LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
+ ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
- void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- if (Network::GetHostIPv4Address().has_value()) {
- rb.Push<u8>(1);
- } else {
- rb.Push<u8>(0);
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ ipv4 = room_member->GetFakeIpAddress();
}
}
- void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- if (Network::GetHostIPv4Address().has_value()) {
- rb.Push<u8>(1);
- } else {
- rb.Push<u8>(0);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(*ipv4);
+}
+
+void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
+
+ ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size");
+ u128 uuid{};
+ auto buffer = ctx.ReadBuffer();
+ std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
+
+ IPC::ResponseBuilder rb{ctx, 6, 0, 1};
+
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<INetworkProfile>(system);
+ rb.PushRaw<u128>(uuid);
+}
+
+void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ struct IpConfigInfo {
+ IpAddressSetting ip_address_setting{};
+ DnsSetting dns_setting{};
+ };
+ static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
+ "IpConfigInfo has incorrect size.");
+
+ const auto net_iface = Network::GetSelectedNetworkInterface();
+
+ IpConfigInfo ip_config_info = [&net_iface] {
+ if (!net_iface) {
+ return IpConfigInfo{};
+ }
+
+ return IpConfigInfo{
+ .ip_address_setting{
+ .is_automatic{true},
+ .current_address{Network::TranslateIPv4(net_iface->ip_address)},
+ .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
+ .gateway{Network::TranslateIPv4(net_iface->gateway)},
+ },
+ .dns_setting{
+ .is_automatic{true},
+ .primary_dns{1, 1, 1, 1},
+ .secondary_dns{1, 0, 0, 1},
+ },
+ };
+ }();
+
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress();
}
}
-};
+
+ IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
+ rb.Push(ResultSuccess);
+ rb.PushRaw<IpConfigInfo>(ip_config_info);
+}
+
+void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u8>(1);
+}
+
+void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ struct Output {
+ InternetConnectionType type{InternetConnectionType::WiFi};
+ u8 wifi_strength{3};
+ InternetConnectionStatus state{InternetConnectionStatus::Connected};
+ };
+ static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
+
+ constexpr Output out{};
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(out);
+}
+
+void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ if (Network::GetHostIPv4Address().has_value()) {
+ rb.Push<u8>(1);
+ } else {
+ rb.Push<u8>(0);
+ }
+}
+
+void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
+ LOG_ERROR(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ if (Network::GetHostIPv4Address().has_value()) {
+ rb.Push<u8>(1);
+ } else {
+ rb.Push<u8>(0);
+ }
+}
IGeneralService::IGeneralService(Core::System& system_)
- : ServiceFramework{system_, "IGeneralService"} {
+ : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &IGeneralService::GetClientId, "GetClientId"},
@@ -458,7 +510,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"},
{16, nullptr, "SetWirelessCommunicationEnabled"},
{17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"},
- {18, nullptr, "GetInternetConnectionStatus"},
+ {18, &IGeneralService::GetInternetConnectionStatus, "GetInternetConnectionStatus"},
{19, nullptr, "SetEthernetCommunicationEnabled"},
{20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
{21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
@@ -490,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_)
RegisterHandlers(functions);
}
+IGeneralService::~IGeneralService() = default;
+
class NetworkInterface final : public ServiceFramework<NetworkInterface> {
public:
explicit NetworkInterface(const char* name, Core::System& system_)
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index c3dd4f386..48161be28 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -1,9 +1,13 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include "core/hle/service/service.h"
+#include "network/network.h"
+#include "network/room.h"
+#include "network/room_member.h"
+
namespace Core {
class System;
}
@@ -17,4 +21,26 @@ namespace Service::NIFM {
/// Registers all NIFM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
+class IGeneralService final : public ServiceFramework<IGeneralService> {
+public:
+ explicit IGeneralService(Core::System& system_);
+ ~IGeneralService() override;
+
+private:
+ void GetClientId(Kernel::HLERequestContext& ctx);
+ void CreateScanRequest(Kernel::HLERequestContext& ctx);
+ void CreateRequest(Kernel::HLERequestContext& ctx);
+ void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx);
+ void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
+ void GetCurrentIpAddress(Kernel::HLERequestContext& ctx);
+ void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
+ void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx);
+ void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx);
+ void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx);
+ void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx);
+ void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx);
+
+ Network::RoomNetwork& network;
+};
+
} // namespace Service::NIFM
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 4fc23a958..b2bb7426d 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <ctime>
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h
index 571153fe6..8f6ff28e8 100644
--- a/src/core/hle/service/nim/nim.h
+++ b/src/core/hle/service/nim/nim.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/npns/npns.cpp b/src/core/hle/service/npns/npns.cpp
index 32533cd94..8133711c2 100644
--- a/src/core/hle/service/npns/npns.cpp
+++ b/src/core/hle/service/npns/npns.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/npns/npns.h b/src/core/hle/service/npns/npns.h
index 3b7596b6b..84e6ec437 100644
--- a/src/core/hle/service/npns/npns.h
+++ b/src/core/hle/service/npns/npns.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/ns/errors.h b/src/core/hle/service/ns/errors.h
index f4aea8a65..8a7621798 100644
--- a/src/core/hle/service/ns/errors.h
+++ b/src/core/hle/service/ns/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,5 +7,5 @@
namespace Service::NS {
-constexpr ResultCode ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
+constexpr Result ERR_APPLICATION_LANGUAGE_NOT_FOUND{ErrorModule::NS, 300};
} \ No newline at end of file
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp
index 74cc45f1e..fd047ff26 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/iplatform_service_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -21,7 +20,7 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_memory.h"
#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/ns/pl_u.h"
+#include "core/hle/service/ns/iplatform_service_manager.h"
namespace Service::NS {
@@ -100,7 +99,7 @@ static u32 GetU32Swapped(const u8* data) {
return Common::swap32(value);
}
-struct PL_U::Impl {
+struct IPlatformServiceManager::Impl {
const FontRegion& GetSharedFontRegion(std::size_t index) const {
if (index >= shared_font_regions.size() || shared_font_regions.empty()) {
// No font fallback
@@ -135,16 +134,16 @@ struct PL_U::Impl {
std::vector<FontRegion> shared_font_regions;
};
-PL_U::PL_U(Core::System& system_)
- : ServiceFramework{system_, "pl:u"}, impl{std::make_unique<Impl>()} {
+IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_)
+ : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &PL_U::RequestLoad, "RequestLoad"},
- {1, &PL_U::GetLoadState, "GetLoadState"},
- {2, &PL_U::GetSize, "GetSize"},
- {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
- {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
- {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
+ {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"},
+ {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"},
+ {2, &IPlatformServiceManager::GetSize, "GetSize"},
+ {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
+ {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
+ {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
{6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
{100, nullptr, "RequestApplicationFunctionAuthorization"},
{101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
@@ -207,9 +206,9 @@ PL_U::PL_U(Core::System& system_)
}
}
-PL_U::~PL_U() = default;
+IPlatformServiceManager::~IPlatformServiceManager() = default;
-void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::RequestLoad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 shared_font_type{rp.Pop<u32>()};
// Games don't call this so all fonts should be loaded
@@ -219,7 +218,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetLoadState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
@@ -229,7 +228,7 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(LoadState::Done));
}
-void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
@@ -239,7 +238,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(impl->GetSharedFontRegion(font_id).size);
}
-void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
@@ -249,7 +248,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset);
}
-void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
// Map backing memory for the font data
LOG_DEBUG(Service_NS, "called");
@@ -262,7 +261,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
rb.PushCopyObjects(&kernel.GetFontSharedMem());
}
-void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code);
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/iplatform_service_manager.h
index f920c7f69..ed6eda89f 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/iplatform_service_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -37,10 +36,10 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output);
void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset);
-class PL_U final : public ServiceFramework<PL_U> {
+class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> {
public:
- explicit PL_U(Core::System& system_);
- ~PL_U() override;
+ explicit IPlatformServiceManager(Core::System& system_, const char* service_name_);
+ ~IPlatformServiceManager() override;
private:
void RequestLoad(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/ns/language.cpp b/src/core/hle/service/ns/language.cpp
index e01c6be47..036a1e9b7 100644
--- a/src/core/hle/service/ns/language.cpp
+++ b/src/core/hle/service/ns/language.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/ns/language.h"
#include "core/hle/service/set/set.h"
diff --git a/src/core/hle/service/ns/language.h b/src/core/hle/service/ns/language.h
index 2cc8e4806..ab6b71029 100644
--- a/src/core/hle/service/ns/language.h
+++ b/src/core/hle/service/ns/language.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 5eaad0474..f7318c3cb 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "common/settings.h"
@@ -10,10 +9,10 @@
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/ns/errors.h"
+#include "core/hle/service/ns/iplatform_service_manager.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/ns/pdm_qry.h"
-#include "core/hle/service/ns/pl_u.h"
#include "core/hle/service/set/set.h"
namespace Service::NS {
@@ -765,7 +764,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager);
- std::make_shared<PL_U>(system)->InstallAsService(service_manager);
+ std::make_shared<IPlatformServiceManager>(system, "pl:s")->InstallAsService(service_manager);
+ std::make_shared<IPlatformServiceManager>(system, "pl:u")->InstallAsService(service_manager);
}
} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 43540b0fb..4dc191518 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/ns/pdm_qry.cpp b/src/core/hle/service/ns/pdm_qry.cpp
index 36ce46353..aac8f573f 100644
--- a/src/core/hle/service/ns/pdm_qry.cpp
+++ b/src/core/hle/service/ns/pdm_qry.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
@@ -9,7 +8,6 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/ns/pdm_qry.h"
#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
namespace Service::NS {
diff --git a/src/core/hle/service/ns/pdm_qry.h b/src/core/hle/service/ns/pdm_qry.h
index 516136314..abcc3bef3 100644
--- a/src/core/hle/service/ns/pdm_qry.h
+++ b/src/core/hle/service/ns/pdm_qry.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/nvdrv/core/container.cpp b/src/core/hle/service/nvdrv/core/container.cpp
new file mode 100644
index 000000000..37ca24f5d
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/container.cpp
@@ -0,0 +1,50 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
+#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
+#include "video_core/host1x/host1x.h"
+
+namespace Service::Nvidia::NvCore {
+
+struct ContainerImpl {
+ explicit ContainerImpl(Tegra::Host1x::Host1x& host1x_)
+ : file{host1x_}, manager{host1x_}, device_file_data{} {}
+ NvMap file;
+ SyncpointManager manager;
+ Container::Host1xDeviceFileData device_file_data;
+};
+
+Container::Container(Tegra::Host1x::Host1x& host1x_) {
+ impl = std::make_unique<ContainerImpl>(host1x_);
+}
+
+Container::~Container() = default;
+
+NvMap& Container::GetNvMapFile() {
+ return impl->file;
+}
+
+const NvMap& Container::GetNvMapFile() const {
+ return impl->file;
+}
+
+Container::Host1xDeviceFileData& Container::Host1xDeviceFile() {
+ return impl->device_file_data;
+}
+
+const Container::Host1xDeviceFileData& Container::Host1xDeviceFile() const {
+ return impl->device_file_data;
+}
+
+SyncpointManager& Container::GetSyncpointManager() {
+ return impl->manager;
+}
+
+const SyncpointManager& Container::GetSyncpointManager() const {
+ return impl->manager;
+}
+
+} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/container.h b/src/core/hle/service/nvdrv/core/container.h
new file mode 100644
index 000000000..b4b63ac90
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/container.h
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <deque>
+#include <memory>
+#include <unordered_map>
+
+#include "core/hle/service/nvdrv/nvdata.h"
+
+namespace Tegra::Host1x {
+class Host1x;
+} // namespace Tegra::Host1x
+
+namespace Service::Nvidia::NvCore {
+
+class NvMap;
+class SyncpointManager;
+
+struct ContainerImpl;
+
+class Container {
+public:
+ explicit Container(Tegra::Host1x::Host1x& host1x);
+ ~Container();
+
+ NvMap& GetNvMapFile();
+
+ const NvMap& GetNvMapFile() const;
+
+ SyncpointManager& GetSyncpointManager();
+
+ const SyncpointManager& GetSyncpointManager() const;
+
+ struct Host1xDeviceFileData {
+ std::unordered_map<DeviceFD, u32> fd_to_id{};
+ std::deque<u32> syncpts_accumulated{};
+ u32 nvdec_next_id{};
+ u32 vic_next_id{};
+ };
+
+ Host1xDeviceFileData& Host1xDeviceFile();
+
+ const Host1xDeviceFileData& Host1xDeviceFile() const;
+
+private:
+ std::unique_ptr<ContainerImpl> impl;
+};
+
+} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/nvmap.cpp b/src/core/hle/service/nvdrv/core/nvmap.cpp
new file mode 100644
index 000000000..fbd8a74a5
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/nvmap.cpp
@@ -0,0 +1,272 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
+#include "core/memory.h"
+#include "video_core/host1x/host1x.h"
+
+using Core::Memory::YUZU_PAGESIZE;
+
+namespace Service::Nvidia::NvCore {
+NvMap::Handle::Handle(u64 size_, Id id_)
+ : size(size_), aligned_size(size), orig_size(size), id(id_) {
+ flags.raw = 0;
+}
+
+NvResult NvMap::Handle::Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress) {
+ std::scoped_lock lock(mutex);
+
+ // Handles cannot be allocated twice
+ if (allocated) {
+ return NvResult::AccessDenied;
+ }
+
+ flags = pFlags;
+ kind = pKind;
+ align = pAlign < YUZU_PAGESIZE ? YUZU_PAGESIZE : pAlign;
+
+ // This flag is only applicable for handles with an address passed
+ if (pAddress) {
+ flags.keep_uncached_after_free.Assign(0);
+ } else {
+ LOG_CRITICAL(Service_NVDRV,
+ "Mapping nvmap handles without a CPU side address is unimplemented!");
+ }
+
+ size = Common::AlignUp(size, YUZU_PAGESIZE);
+ aligned_size = Common::AlignUp(size, align);
+ address = pAddress;
+ allocated = true;
+
+ return NvResult::Success;
+}
+
+NvResult NvMap::Handle::Duplicate(bool internal_session) {
+ std::scoped_lock lock(mutex);
+ // Unallocated handles cannot be duplicated as duplication requires memory accounting (in HOS)
+ if (!allocated) [[unlikely]] {
+ return NvResult::BadValue;
+ }
+
+ // If we internally use FromId the duplication tracking of handles won't work accurately due to
+ // us not implementing per-process handle refs.
+ if (internal_session) {
+ internal_dupes++;
+ } else {
+ dupes++;
+ }
+
+ return NvResult::Success;
+}
+
+NvMap::NvMap(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {}
+
+void NvMap::AddHandle(std::shared_ptr<Handle> handle_description) {
+ std::scoped_lock lock(handles_lock);
+
+ handles.emplace(handle_description->id, std::move(handle_description));
+}
+
+void NvMap::UnmapHandle(Handle& handle_description) {
+ // Remove pending unmap queue entry if needed
+ if (handle_description.unmap_queue_entry) {
+ unmap_queue.erase(*handle_description.unmap_queue_entry);
+ handle_description.unmap_queue_entry.reset();
+ }
+
+ // Free and unmap the handle from the SMMU
+ host1x.MemoryManager().Unmap(static_cast<GPUVAddr>(handle_description.pin_virt_address),
+ handle_description.aligned_size);
+ host1x.Allocator().Free(handle_description.pin_virt_address,
+ static_cast<u32>(handle_description.aligned_size));
+ handle_description.pin_virt_address = 0;
+}
+
+bool NvMap::TryRemoveHandle(const Handle& handle_description) {
+ // No dupes left, we can remove from handle map
+ if (handle_description.dupes == 0 && handle_description.internal_dupes == 0) {
+ std::scoped_lock lock(handles_lock);
+
+ auto it{handles.find(handle_description.id)};
+ if (it != handles.end()) {
+ handles.erase(it);
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+NvResult NvMap::CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out) {
+ if (!size) [[unlikely]] {
+ return NvResult::BadValue;
+ }
+
+ u32 id{next_handle_id.fetch_add(HandleIdIncrement, std::memory_order_relaxed)};
+ auto handle_description{std::make_shared<Handle>(size, id)};
+ AddHandle(handle_description);
+
+ result_out = handle_description;
+ return NvResult::Success;
+}
+
+std::shared_ptr<NvMap::Handle> NvMap::GetHandle(Handle::Id handle) {
+ std::scoped_lock lock(handles_lock);
+ try {
+ return handles.at(handle);
+ } catch (std::out_of_range&) {
+ return nullptr;
+ }
+}
+
+VAddr NvMap::GetHandleAddress(Handle::Id handle) {
+ std::scoped_lock lock(handles_lock);
+ try {
+ return handles.at(handle)->address;
+ } catch (std::out_of_range&) {
+ return 0;
+ }
+}
+
+u32 NvMap::PinHandle(NvMap::Handle::Id handle) {
+ auto handle_description{GetHandle(handle)};
+ if (!handle_description) [[unlikely]] {
+ return 0;
+ }
+
+ std::scoped_lock lock(handle_description->mutex);
+ if (!handle_description->pins) {
+ // If we're in the unmap queue we can just remove ourselves and return since we're already
+ // mapped
+ {
+ // Lock now to prevent our queue entry from being removed for allocation in-between the
+ // following check and erase
+ std::scoped_lock queueLock(unmap_queue_lock);
+ if (handle_description->unmap_queue_entry) {
+ unmap_queue.erase(*handle_description->unmap_queue_entry);
+ handle_description->unmap_queue_entry.reset();
+
+ handle_description->pins++;
+ return handle_description->pin_virt_address;
+ }
+ }
+
+ // If not then allocate some space and map it
+ u32 address{};
+ auto& smmu_allocator = host1x.Allocator();
+ auto& smmu_memory_manager = host1x.MemoryManager();
+ while (!(address =
+ smmu_allocator.Allocate(static_cast<u32>(handle_description->aligned_size)))) {
+ // Free handles until the allocation succeeds
+ std::scoped_lock queueLock(unmap_queue_lock);
+ if (auto freeHandleDesc{unmap_queue.front()}) {
+ // Handles in the unmap queue are guaranteed not to be pinned so don't bother
+ // checking if they are before unmapping
+ std::scoped_lock freeLock(freeHandleDesc->mutex);
+ if (handle_description->pin_virt_address)
+ UnmapHandle(*freeHandleDesc);
+ } else {
+ LOG_CRITICAL(Service_NVDRV, "Ran out of SMMU address space!");
+ }
+ }
+
+ smmu_memory_manager.Map(static_cast<GPUVAddr>(address), handle_description->address,
+ handle_description->aligned_size);
+ handle_description->pin_virt_address = address;
+ }
+
+ handle_description->pins++;
+ return handle_description->pin_virt_address;
+}
+
+void NvMap::UnpinHandle(Handle::Id handle) {
+ auto handle_description{GetHandle(handle)};
+ if (!handle_description) {
+ return;
+ }
+
+ std::scoped_lock lock(handle_description->mutex);
+ if (--handle_description->pins < 0) {
+ LOG_WARNING(Service_NVDRV, "Pin count imbalance detected!");
+ } else if (!handle_description->pins) {
+ std::scoped_lock queueLock(unmap_queue_lock);
+
+ // Add to the unmap queue allowing this handle's memory to be freed if needed
+ unmap_queue.push_back(handle_description);
+ handle_description->unmap_queue_entry = std::prev(unmap_queue.end());
+ }
+}
+
+void NvMap::DuplicateHandle(Handle::Id handle, bool internal_session) {
+ auto handle_description{GetHandle(handle)};
+ if (!handle_description) {
+ LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
+ return;
+ }
+
+ auto result = handle_description->Duplicate(internal_session);
+ if (result != NvResult::Success) {
+ LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
+ }
+}
+
+std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool internal_session) {
+ std::weak_ptr<Handle> hWeak{GetHandle(handle)};
+ FreeInfo freeInfo;
+
+ // We use a weak ptr here so we can tell when the handle has been freed and report that back to
+ // guest
+ if (auto handle_description = hWeak.lock()) {
+ std::scoped_lock lock(handle_description->mutex);
+
+ if (internal_session) {
+ if (--handle_description->internal_dupes < 0)
+ LOG_WARNING(Service_NVDRV, "Internal duplicate count imbalance detected!");
+ } else {
+ if (--handle_description->dupes < 0) {
+ LOG_WARNING(Service_NVDRV, "User duplicate count imbalance detected!");
+ } else if (handle_description->dupes == 0) {
+ // Force unmap the handle
+ if (handle_description->pin_virt_address) {
+ std::scoped_lock queueLock(unmap_queue_lock);
+ UnmapHandle(*handle_description);
+ }
+
+ handle_description->pins = 0;
+ }
+ }
+
+ // Try to remove the shared ptr to the handle from the map, if nothing else is using the
+ // handle then it will now be freed when `handle_description` goes out of scope
+ if (TryRemoveHandle(*handle_description)) {
+ LOG_DEBUG(Service_NVDRV, "Removed nvmap handle: {}", handle);
+ } else {
+ LOG_DEBUG(Service_NVDRV,
+ "Tried to free nvmap handle: {} but didn't as it still has duplicates",
+ handle);
+ }
+
+ freeInfo = {
+ .address = handle_description->address,
+ .size = handle_description->size,
+ .was_uncached = handle_description->flags.map_uncached.Value() != 0,
+ };
+ } else {
+ return std::nullopt;
+ }
+
+ // Handle hasn't been freed from memory, set address to 0 to mark that the handle wasn't freed
+ if (!hWeak.expired()) {
+ LOG_DEBUG(Service_NVDRV, "nvmap handle: {} wasn't freed as it is still in use", handle);
+ freeInfo.address = 0;
+ }
+
+ return freeInfo;
+}
+
+} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/nvmap.h b/src/core/hle/service/nvdrv/core/nvmap.h
new file mode 100644
index 000000000..b9dd3801f
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/nvmap.h
@@ -0,0 +1,175 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <atomic>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <assert.h>
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+
+namespace Tegra {
+
+namespace Host1x {
+class Host1x;
+} // namespace Host1x
+
+} // namespace Tegra
+
+namespace Service::Nvidia::NvCore {
+/**
+ * @brief The nvmap core class holds the global state for nvmap and provides methods to manage
+ * handles
+ */
+class NvMap {
+public:
+ /**
+ * @brief A handle to a contiguous block of memory in an application's address space
+ */
+ struct Handle {
+ std::mutex mutex;
+
+ u64 align{}; //!< The alignment to use when pinning the handle onto the SMMU
+ u64 size; //!< Page-aligned size of the memory the handle refers to
+ u64 aligned_size; //!< `align`-aligned size of the memory the handle refers to
+ u64 orig_size; //!< Original unaligned size of the memory this handle refers to
+
+ s32 dupes{1}; //!< How many guest references there are to this handle
+ s32 internal_dupes{0}; //!< How many emulator-internal references there are to this handle
+
+ using Id = u32;
+ Id id; //!< A globally unique identifier for this handle
+
+ s32 pins{};
+ u32 pin_virt_address{};
+ std::optional<typename std::list<std::shared_ptr<Handle>>::iterator> unmap_queue_entry{};
+
+ union Flags {
+ u32 raw;
+ BitField<0, 1, u32> map_uncached; //!< If the handle should be mapped as uncached
+ BitField<2, 1, u32> keep_uncached_after_free; //!< Only applicable when the handle was
+ //!< allocated with a fixed address
+ BitField<4, 1, u32> _unk0_; //!< Passed to IOVMM for pins
+ } flags{};
+ static_assert(sizeof(Flags) == sizeof(u32));
+
+ u64 address{}; //!< The memory location in the guest's AS that this handle corresponds to,
+ //!< this can also be in the nvdrv tmem
+ bool is_shared_mem_mapped{}; //!< If this nvmap has been mapped with the MapSharedMem IPC
+ //!< call
+
+ u8 kind{}; //!< Used for memory compression
+ bool allocated{}; //!< If the handle has been allocated with `Alloc`
+
+ u64 dma_map_addr{}; //! remove me after implementing pinning.
+
+ Handle(u64 size, Id id);
+
+ /**
+ * @brief Sets up the handle with the given memory config, can allocate memory from the tmem
+ * if a 0 address is passed
+ */
+ [[nodiscard]] NvResult Alloc(Flags pFlags, u32 pAlign, u8 pKind, u64 pAddress);
+
+ /**
+ * @brief Increases the dupe counter of the handle for the given session
+ */
+ [[nodiscard]] NvResult Duplicate(bool internal_session);
+
+ /**
+ * @brief Obtains a pointer to the handle's memory and marks the handle it as having been
+ * mapped
+ */
+ u8* GetPointer() {
+ if (!address) {
+ return nullptr;
+ }
+
+ is_shared_mem_mapped = true;
+ return reinterpret_cast<u8*>(address);
+ }
+ };
+
+ /**
+ * @brief Encapsulates the result of a FreeHandle operation
+ */
+ struct FreeInfo {
+ u64 address; //!< Address the handle referred to before deletion
+ u64 size; //!< Page-aligned handle size
+ bool was_uncached; //!< If the handle was allocated as uncached
+ };
+
+ explicit NvMap(Tegra::Host1x::Host1x& host1x);
+
+ /**
+ * @brief Creates an unallocated handle of the given size
+ */
+ [[nodiscard]] NvResult CreateHandle(u64 size, std::shared_ptr<NvMap::Handle>& result_out);
+
+ std::shared_ptr<Handle> GetHandle(Handle::Id handle);
+
+ VAddr GetHandleAddress(Handle::Id handle);
+
+ /**
+ * @brief Maps a handle into the SMMU address space
+ * @note This operation is refcounted, the number of calls to this must eventually match the
+ * number of calls to `UnpinHandle`
+ * @return The SMMU virtual address that the handle has been mapped to
+ */
+ u32 PinHandle(Handle::Id handle);
+
+ /**
+ * @brief When this has been called an equal number of times to `PinHandle` for the supplied
+ * handle it will be added to a list of handles to be freed when necessary
+ */
+ void UnpinHandle(Handle::Id handle);
+
+ /**
+ * @brief Tries to duplicate a handle
+ */
+ void DuplicateHandle(Handle::Id handle, bool internal_session = false);
+
+ /**
+ * @brief Tries to free a handle and remove a single dupe
+ * @note If a handle has no dupes left and has no other users a FreeInfo struct will be returned
+ * describing the prior state of the handle
+ */
+ std::optional<FreeInfo> FreeHandle(Handle::Id handle, bool internal_session);
+
+private:
+ std::list<std::shared_ptr<Handle>> unmap_queue{};
+ std::mutex unmap_queue_lock{}; //!< Protects access to `unmap_queue`
+
+ std::unordered_map<Handle::Id, std::shared_ptr<Handle>>
+ handles{}; //!< Main owning map of handles
+ std::mutex handles_lock; //!< Protects access to `handles`
+
+ static constexpr u32 HandleIdIncrement{
+ 4}; //!< Each new handle ID is an increment of 4 from the previous
+ std::atomic<u32> next_handle_id{HandleIdIncrement};
+ Tegra::Host1x::Host1x& host1x;
+
+ void AddHandle(std::shared_ptr<Handle> handle);
+
+ /**
+ * @brief Unmaps and frees the SMMU memory region a handle is mapped to
+ * @note Both `unmap_queue_lock` and `handle_description.mutex` MUST be locked when calling this
+ */
+ void UnmapHandle(Handle& handle_description);
+
+ /**
+ * @brief Removes a handle from the map taking its dupes into account
+ * @note handle_description.mutex MUST be locked when calling this
+ * @return If the handle was removed from the map
+ */
+ bool TryRemoveHandle(const Handle& handle_description);
+};
+} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
new file mode 100644
index 000000000..eda2041a0
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.cpp
@@ -0,0 +1,121 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "common/assert.h"
+#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
+#include "video_core/host1x/host1x.h"
+
+namespace Service::Nvidia::NvCore {
+
+SyncpointManager::SyncpointManager(Tegra::Host1x::Host1x& host1x_) : host1x{host1x_} {
+ constexpr u32 VBlank0SyncpointId{26};
+ constexpr u32 VBlank1SyncpointId{27};
+
+ // Reserve both vblank syncpoints as client managed as they use Continuous Mode
+ // Refer to section 14.3.5.3 of the TRM for more information on Continuous Mode
+ // https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/drm/dc.c#L660
+ ReserveSyncpoint(VBlank0SyncpointId, true);
+ ReserveSyncpoint(VBlank1SyncpointId, true);
+
+ for (u32 syncpoint_id : channel_syncpoints) {
+ if (syncpoint_id) {
+ ReserveSyncpoint(syncpoint_id, false);
+ }
+ }
+}
+
+SyncpointManager::~SyncpointManager() = default;
+
+u32 SyncpointManager::ReserveSyncpoint(u32 id, bool client_managed) {
+ if (syncpoints.at(id).reserved) {
+ ASSERT_MSG(false, "Requested syncpoint is in use");
+ return 0;
+ }
+
+ syncpoints.at(id).reserved = true;
+ syncpoints.at(id).interface_managed = client_managed;
+
+ return id;
+}
+
+u32 SyncpointManager::FindFreeSyncpoint() {
+ for (u32 i{1}; i < syncpoints.size(); i++) {
+ if (!syncpoints[i].reserved) {
+ return i;
+ }
+ }
+ ASSERT_MSG(false, "Failed to find a free syncpoint!");
+ return 0;
+}
+
+u32 SyncpointManager::AllocateSyncpoint(bool client_managed) {
+ std::lock_guard lock(reservation_lock);
+ return ReserveSyncpoint(FindFreeSyncpoint(), client_managed);
+}
+
+void SyncpointManager::FreeSyncpoint(u32 id) {
+ std::lock_guard lock(reservation_lock);
+ ASSERT(syncpoints.at(id).reserved);
+ syncpoints.at(id).reserved = false;
+}
+
+bool SyncpointManager::IsSyncpointAllocated(u32 id) {
+ return (id <= SyncpointCount) && syncpoints[id].reserved;
+}
+
+bool SyncpointManager::HasSyncpointExpired(u32 id, u32 threshold) const {
+ const SyncpointInfo& syncpoint{syncpoints.at(id)};
+
+ if (!syncpoint.reserved) {
+ ASSERT(false);
+ return 0;
+ }
+
+ // If the interface manages counters then we don't keep track of the maximum value as it handles
+ // sanity checking the values then
+ if (syncpoint.interface_managed) {
+ return static_cast<s32>(syncpoint.counter_min - threshold) >= 0;
+ } else {
+ return (syncpoint.counter_max - threshold) >= (syncpoint.counter_min - threshold);
+ }
+}
+
+u32 SyncpointManager::IncrementSyncpointMaxExt(u32 id, u32 amount) {
+ if (!syncpoints.at(id).reserved) {
+ ASSERT(false);
+ return 0;
+ }
+
+ return syncpoints.at(id).counter_max += amount;
+}
+
+u32 SyncpointManager::ReadSyncpointMinValue(u32 id) {
+ if (!syncpoints.at(id).reserved) {
+ ASSERT(false);
+ return 0;
+ }
+
+ return syncpoints.at(id).counter_min;
+}
+
+u32 SyncpointManager::UpdateMin(u32 id) {
+ if (!syncpoints.at(id).reserved) {
+ ASSERT(false);
+ return 0;
+ }
+
+ syncpoints.at(id).counter_min = host1x.GetSyncpointManager().GetHostSyncpointValue(id);
+ return syncpoints.at(id).counter_min;
+}
+
+NvFence SyncpointManager::GetSyncpointFence(u32 id) {
+ if (!syncpoints.at(id).reserved) {
+ ASSERT(false);
+ return NvFence{};
+ }
+
+ return {.id = static_cast<s32>(id), .value = syncpoints.at(id).counter_max};
+}
+
+} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/core/syncpoint_manager.h b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
new file mode 100644
index 000000000..b76ef9032
--- /dev/null
+++ b/src/core/hle/service/nvdrv/core/syncpoint_manager.h
@@ -0,0 +1,134 @@
+// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+#include <atomic>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+
+namespace Tegra::Host1x {
+class Host1x;
+} // namespace Tegra::Host1x
+
+namespace Service::Nvidia::NvCore {
+
+enum class ChannelType : u32 {
+ MsEnc = 0,
+ VIC = 1,
+ GPU = 2,
+ NvDec = 3,
+ Display = 4,
+ NvJpg = 5,
+ TSec = 6,
+ Max = 7
+};
+
+/**
+ * @brief SyncpointManager handles allocating and accessing host1x syncpoints, these are cached
+ * versions of the HW syncpoints which are intermittently synced
+ * @note Refer to Chapter 14 of the Tegra X1 TRM for an exhaustive overview of them
+ * @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html
+ * @url
+ * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c
+ */
+class SyncpointManager final {
+public:
+ explicit SyncpointManager(Tegra::Host1x::Host1x& host1x);
+ ~SyncpointManager();
+
+ /**
+ * @brief Checks if the given syncpoint is both allocated and below the number of HW syncpoints
+ */
+ bool IsSyncpointAllocated(u32 id);
+
+ /**
+ * @brief Finds a free syncpoint and reserves it
+ * @return The ID of the reserved syncpoint
+ */
+ u32 AllocateSyncpoint(bool client_managed);
+
+ /**
+ * @url
+ * https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/8f74a72394efb871cb3f886a3de2998cd7ff2990/drivers/gpu/host1x/syncpt.c#L259
+ */
+ bool HasSyncpointExpired(u32 id, u32 threshold) const;
+
+ bool IsFenceSignalled(NvFence fence) const {
+ return HasSyncpointExpired(fence.id, fence.value);
+ }
+
+ /**
+ * @brief Atomically increments the maximum value of a syncpoint by the given amount
+ * @return The new max value of the syncpoint
+ */
+ u32 IncrementSyncpointMaxExt(u32 id, u32 amount);
+
+ /**
+ * @return The minimum value of the syncpoint
+ */
+ u32 ReadSyncpointMinValue(u32 id);
+
+ /**
+ * @brief Synchronises the minimum value of the syncpoint to with the GPU
+ * @return The new minimum value of the syncpoint
+ */
+ u32 UpdateMin(u32 id);
+
+ /**
+ * @brief Frees the usage of a syncpoint.
+ */
+ void FreeSyncpoint(u32 id);
+
+ /**
+ * @return A fence that will be signalled once this syncpoint hits its maximum value
+ */
+ NvFence GetSyncpointFence(u32 id);
+
+ static constexpr std::array<u32, static_cast<u32>(ChannelType::Max)> channel_syncpoints{
+ 0x0, // `MsEnc` is unimplemented
+ 0xC, // `VIC`
+ 0x0, // `GPU` syncpoints are allocated per-channel instead
+ 0x36, // `NvDec`
+ 0x0, // `Display` is unimplemented
+ 0x37, // `NvJpg`
+ 0x0, // `TSec` is unimplemented
+ }; //!< Maps each channel ID to a constant syncpoint
+
+private:
+ /**
+ * @note reservation_lock should be locked when calling this
+ */
+ u32 ReserveSyncpoint(u32 id, bool client_managed);
+
+ /**
+ * @return The ID of the first free syncpoint
+ */
+ u32 FindFreeSyncpoint();
+
+ struct SyncpointInfo {
+ std::atomic<u32> counter_min; //!< The least value the syncpoint can be (The value it was
+ //!< when it was last synchronized with host1x)
+ std::atomic<u32> counter_max; //!< The maximum value the syncpoint can reach according to
+ //!< the current usage
+ bool interface_managed; //!< If the syncpoint is managed by a host1x client interface, a
+ //!< client interface is a HW block that can handle host1x
+ //!< transactions on behalf of a host1x client (Which would
+ //!< otherwise need to be manually synced using PIO which is
+ //!< synchronous and requires direct cooperation of the CPU)
+ bool reserved; //!< If the syncpoint is reserved or not, not to be confused with a reserved
+ //!< value
+ };
+
+ constexpr static std::size_t SyncpointCount{192};
+ std::array<SyncpointInfo, SyncpointCount> syncpoints{};
+ std::mutex reservation_lock;
+
+ Tegra::Host1x::Host1x& host1x;
+};
+
+} // namespace Service::Nvidia::NvCore
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 3d874243a..204b0e757 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -12,6 +11,10 @@ namespace Core {
class System;
}
+namespace Kernel {
+class KEvent;
+}
+
namespace Service::Nvidia::Devices {
/// Represents an abstract nvidia device node. It is to be subclassed by concrete device nodes to
@@ -65,6 +68,10 @@ public:
*/
virtual void OnClose(DeviceFD fd) = 0;
+ virtual Kernel::KEvent* QueryEvent(u32 event_id) {
+ return nullptr;
+ }
+
protected:
Core::System& system;
};
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 68f1e9060..4122fc98d 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -1,20 +1,20 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_timing.h"
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
-#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/perf_stats.h"
#include "video_core/gpu.h"
namespace Service::Nvidia::Devices {
-nvdisp_disp0::nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_)
- : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {}
+nvdisp_disp0::nvdisp_disp0(Core::System& system_, NvCore::Container& core)
+ : nvdevice{system_}, container{core}, nvmap{core.GetNvMapFile()} {}
nvdisp_disp0::~nvdisp_disp0() = default;
NvResult nvdisp_disp0::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -38,23 +38,27 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
void nvdisp_disp0::OnOpen(DeviceFD fd) {}
void nvdisp_disp0::OnClose(DeviceFD fd) {}
-void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
- u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect) {
- const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
+void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width,
+ u32 height, u32 stride, android::BufferTransformFlags transform,
+ const Common::Rectangle<int>& crop_rect,
+ std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) {
+ const VAddr addr = nvmap.GetHandleAddress(buffer_handle);
LOG_TRACE(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
addr, offset, width, height, stride, format);
- const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format);
- const auto transform_flags = static_cast<Tegra::FramebufferConfig::TransformFlags>(transform);
- const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
- stride, pixel_format, transform_flags, crop_rect};
+ const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
+ stride, format, transform, crop_rect};
+ system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences);
system.GetPerfStats().EndSystemFrame();
- system.GPU().SwapBuffers(&framebuffer);
system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs());
system.GetPerfStats().BeginSystemFrame();
}
+Kernel::KEvent* nvdisp_disp0::QueryEvent(u32 event_id) {
+ LOG_CRITICAL(Service_NVDRV, "Unknown DISP Event {}", event_id);
+ return nullptr;
+}
+
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index de01e1d5f..04217ab12 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -9,7 +8,13 @@
#include "common/common_types.h"
#include "common/math_util.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
-#include "core/hle/service/nvflinger/buffer_queue.h"
+#include "core/hle/service/nvflinger/buffer_transform_flags.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
+
+namespace Service::Nvidia::NvCore {
+class Container;
+class NvMap;
+} // namespace Service::Nvidia::NvCore
namespace Service::Nvidia::Devices {
@@ -17,7 +22,7 @@ class nvmap;
class nvdisp_disp0 final : public nvdevice {
public:
- explicit nvdisp_disp0(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_);
+ explicit nvdisp_disp0(Core::System& system_, NvCore::Container& core);
~nvdisp_disp0() override;
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -31,12 +36,16 @@ public:
void OnClose(DeviceFD fd) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
- void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
- NVFlinger::BufferQueue::BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect);
+ void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height,
+ u32 stride, android::BufferTransformFlags transform,
+ const Common::Rectangle<int>& crop_rect,
+ std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences);
+
+ Kernel::KEvent* QueryEvent(u32 event_id) override;
private:
- std::shared_ptr<nvmap> nvmap_dev;
+ NvCore::Container& container;
+ NvCore::NvMap& nvmap;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index 85170cdb3..6411dbf43 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -1,22 +1,30 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
#include <cstring>
#include <utility>
+#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
-#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
+#include "video_core/control/channel_state.h"
+#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
namespace Service::Nvidia::Devices {
-nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_)
- : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)} {}
+nvhost_as_gpu::nvhost_as_gpu(Core::System& system_, Module& module_, NvCore::Container& core)
+ : nvdevice{system_}, module{module_}, container{core}, nvmap{core.GetNvMapFile()}, vm{},
+ gmmu{} {}
+
nvhost_as_gpu::~nvhost_as_gpu() = default;
NvResult nvhost_as_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -83,12 +91,52 @@ NvResult nvhost_as_gpu::AllocAsEx(const std::vector<u8>& input, std::vector<u8>&
IoctlAllocAsEx params{};
std::memcpy(&params, input.data(), input.size());
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
- if (params.big_page_size == 0) {
- params.big_page_size = DEFAULT_BIG_PAGE_SIZE;
+ LOG_DEBUG(Service_NVDRV, "called, big_page_size=0x{:X}", params.big_page_size);
+
+ std::scoped_lock lock(mutex);
+
+ if (vm.initialised) {
+ ASSERT_MSG(false, "Cannot initialise an address space twice!");
+ return NvResult::InvalidState;
+ }
+
+ if (params.big_page_size) {
+ if (!std::has_single_bit(params.big_page_size)) {
+ LOG_ERROR(Service_NVDRV, "Non power-of-2 big page size: 0x{:X}!", params.big_page_size);
+ return NvResult::BadValue;
+ }
+
+ if ((params.big_page_size & VM::SUPPORTED_BIG_PAGE_SIZES) == 0) {
+ LOG_ERROR(Service_NVDRV, "Unsupported big page size: 0x{:X}!", params.big_page_size);
+ return NvResult::BadValue;
+ }
+
+ vm.big_page_size = params.big_page_size;
+ vm.big_page_size_bits = static_cast<u32>(std::countr_zero(params.big_page_size));
+
+ vm.va_range_start = params.big_page_size << VM::VA_START_SHIFT;
+ }
+
+ // If this is unspecified then default values should be used
+ if (params.va_range_start) {
+ vm.va_range_start = params.va_range_start;
+ vm.va_range_split = params.va_range_split;
+ vm.va_range_end = params.va_range_end;
}
- big_page_size = params.big_page_size;
+ const auto start_pages{static_cast<u32>(vm.va_range_start >> VM::PAGE_SIZE_BITS)};
+ const auto end_pages{static_cast<u32>(vm.va_range_split >> VM::PAGE_SIZE_BITS)};
+ vm.small_page_allocator = std::make_shared<VM::Allocator>(start_pages, end_pages);
+
+ const auto start_big_pages{static_cast<u32>(vm.va_range_split >> vm.big_page_size_bits)};
+ const auto end_big_pages{
+ static_cast<u32>((vm.va_range_end - vm.va_range_split) >> vm.big_page_size_bits)};
+ vm.big_page_allocator = std::make_unique<VM::Allocator>(start_big_pages, end_big_pages);
+
+ gmmu = std::make_shared<Tegra::MemoryManager>(system, 40, vm.big_page_size_bits,
+ VM::PAGE_SIZE_BITS);
+ system.GPU().InitAddressSpace(*gmmu);
+ vm.initialised = true;
return NvResult::Success;
}
@@ -100,21 +148,76 @@ NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<
LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages,
params.page_size, params.flags);
- const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)};
- if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) {
- params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size);
+ std::scoped_lock lock(mutex);
+
+ if (!vm.initialised) {
+ return NvResult::BadValue;
+ }
+
+ if (params.page_size != VM::YUZU_PAGESIZE && params.page_size != vm.big_page_size) {
+ return NvResult::BadValue;
+ }
+
+ if (params.page_size != vm.big_page_size &&
+ ((params.flags & MappingFlags::Sparse) != MappingFlags::None)) {
+ UNIMPLEMENTED_MSG("Sparse small pages are not implemented!");
+ return NvResult::NotImplemented;
+ }
+
+ const u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS
+ : vm.big_page_size_bits};
+
+ auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator
+ : *vm.big_page_allocator};
+
+ if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) {
+ allocator.AllocateFixed(static_cast<u32>(params.offset >> page_size_bits), params.pages);
} else {
- params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
+ params.offset = static_cast<u64>(allocator.Allocate(params.pages)) << page_size_bits;
+ if (!params.offset) {
+ ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!");
+ return NvResult::InsufficientMemory;
+ }
}
- auto result = NvResult::Success;
- if (!params.offset) {
- LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
- result = NvResult::InsufficientMemory;
+ u64 size{static_cast<u64>(params.pages) * params.page_size};
+
+ if ((params.flags & MappingFlags::Sparse) != MappingFlags::None) {
+ gmmu->MapSparse(params.offset, size);
}
+ allocation_map[params.offset] = {
+ .size = size,
+ .mappings{},
+ .page_size = params.page_size,
+ .sparse = (params.flags & MappingFlags::Sparse) != MappingFlags::None,
+ .big_pages = params.page_size != VM::YUZU_PAGESIZE,
+ };
+
std::memcpy(output.data(), &params, output.size());
- return result;
+ return NvResult::Success;
+}
+
+void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
+ auto mapping{mapping_map.at(offset)};
+
+ if (!mapping->fixed) {
+ auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
+ u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
+
+ allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
+ static_cast<u32>(mapping->size >> page_size_bits));
+ }
+
+ // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
+ // Only FreeSpace can unmap them fully
+ if (mapping->sparse_alloc) {
+ gmmu->MapSparse(offset, mapping->size, mapping->big_page);
+ } else {
+ gmmu->Unmap(offset, mapping->size);
+ }
+
+ mapping_map.erase(offset);
}
NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -124,8 +227,40 @@ NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>&
LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
params.pages, params.page_size);
- system.GPU().MemoryManager().Unmap(params.offset,
- static_cast<std::size_t>(params.pages) * params.page_size);
+ std::scoped_lock lock(mutex);
+
+ if (!vm.initialised) {
+ return NvResult::BadValue;
+ }
+
+ try {
+ auto allocation{allocation_map[params.offset]};
+
+ if (allocation.page_size != params.page_size ||
+ allocation.size != (static_cast<u64>(params.pages) * params.page_size)) {
+ return NvResult::BadValue;
+ }
+
+ for (const auto& mapping : allocation.mappings) {
+ FreeMappingLocked(mapping->offset);
+ }
+
+ // Unset sparse flag if required
+ if (allocation.sparse) {
+ gmmu->Unmap(params.offset, allocation.size);
+ }
+
+ auto& allocator{params.page_size == VM::YUZU_PAGESIZE ? *vm.small_page_allocator
+ : *vm.big_page_allocator};
+ u32 page_size_bits{params.page_size == VM::YUZU_PAGESIZE ? VM::PAGE_SIZE_BITS
+ : vm.big_page_size_bits};
+
+ allocator.Free(static_cast<u32>(params.offset >> page_size_bits),
+ static_cast<u32>(allocation.size >> page_size_bits));
+ allocation_map.erase(params.offset);
+ } catch (const std::out_of_range&) {
+ return NvResult::BadValue;
+ }
std::memcpy(output.data(), &params, output.size());
return NvResult::Success;
@@ -136,35 +271,52 @@ NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& out
LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
- auto result = NvResult::Success;
std::vector<IoctlRemapEntry> entries(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
+ std::scoped_lock lock(mutex);
+
+ if (!vm.initialised) {
+ return NvResult::BadValue;
+ }
+
for (const auto& entry : entries) {
- LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}",
- entry.offset, entry.nvmap_handle, entry.pages);
+ GPUVAddr virtual_address{static_cast<u64>(entry.as_offset_big_pages)
+ << vm.big_page_size_bits};
+ u64 size{static_cast<u64>(entry.big_pages) << vm.big_page_size_bits};
- const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
- if (!object) {
- LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
- result = NvResult::InvalidState;
- break;
+ auto alloc{allocation_map.upper_bound(virtual_address)};
+
+ if (alloc-- == allocation_map.begin() ||
+ (virtual_address - alloc->first) + size > alloc->second.size) {
+ LOG_WARNING(Service_NVDRV, "Cannot remap into an unallocated region!");
+ return NvResult::BadValue;
}
- const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10};
- const auto size{static_cast<u64>(entry.pages) << 0x10};
- const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10};
- const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)};
+ if (!alloc->second.sparse) {
+ LOG_WARNING(Service_NVDRV, "Cannot remap a non-sparse mapping!");
+ return NvResult::BadValue;
+ }
- if (!addr) {
- LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
- result = NvResult::InvalidState;
- break;
+ const bool use_big_pages = alloc->second.big_pages;
+ if (!entry.handle) {
+ gmmu->MapSparse(virtual_address, size, use_big_pages);
+ } else {
+ auto handle{nvmap.GetHandle(entry.handle)};
+ if (!handle) {
+ return NvResult::BadValue;
+ }
+
+ VAddr cpu_address{static_cast<VAddr>(
+ handle->address +
+ (static_cast<u64>(entry.handle_offset_big_pages) << vm.big_page_size_bits))};
+
+ gmmu->Map(virtual_address, cpu_address, size, use_big_pages);
}
}
std::memcpy(output.data(), entries.data(), output.size());
- return result;
+ return NvResult::Success;
}
NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -174,79 +326,98 @@ NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8
LOG_DEBUG(Service_NVDRV,
"called, flags={:X}, nvmap_handle={:X}, buffer_offset={}, mapping_size={}"
", offset={}",
- params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size,
+ params.flags, params.handle, params.buffer_offset, params.mapping_size,
params.offset);
- const auto object{nvmap_dev->GetObject(params.nvmap_handle)};
- if (!object) {
- LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
- std::memcpy(output.data(), &params, output.size());
- return NvResult::InvalidState;
- }
-
- // The real nvservices doesn't make a distinction between handles and ids, and
- // object can only have one handle and it will be the same as its id. Assert that this is the
- // case to prevent unexpected behavior.
- ASSERT(object->id == params.nvmap_handle);
- auto& gpu = system.GPU();
+ std::scoped_lock lock(mutex);
- u64 page_size{params.page_size};
- if (!page_size) {
- page_size = object->align;
+ if (!vm.initialised) {
+ return NvResult::BadValue;
}
- if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) {
- if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) {
- const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)};
- const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)};
+ // Remaps a subregion of an existing mapping to a different PA
+ if ((params.flags & MappingFlags::Remap) != MappingFlags::None) {
+ try {
+ auto mapping{mapping_map.at(params.offset)};
- if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) {
- LOG_CRITICAL(Service_NVDRV,
- "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, "
- "mapping_size = {}, offset={}",
- params.flags, params.nvmap_handle, params.buffer_offset,
- params.mapping_size, params.offset);
-
- std::memcpy(output.data(), &params, output.size());
- return NvResult::InvalidState;
+ if (mapping->size < params.mapping_size) {
+ LOG_WARNING(Service_NVDRV,
+ "Cannot remap a partially mapped GPU address space region: 0x{:X}",
+ params.offset);
+ return NvResult::BadValue;
}
- std::memcpy(output.data(), &params, output.size());
- return NvResult::Success;
- } else {
- LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
+ u64 gpu_address{static_cast<u64>(params.offset + params.buffer_offset)};
+ VAddr cpu_address{mapping->ptr + params.buffer_offset};
+
+ gmmu->Map(gpu_address, cpu_address, params.mapping_size, mapping->big_page);
- std::memcpy(output.data(), &params, output.size());
- return NvResult::InvalidState;
+ return NvResult::Success;
+ } catch (const std::out_of_range&) {
+ LOG_WARNING(Service_NVDRV, "Cannot remap an unmapped GPU address space region: 0x{:X}",
+ params.offset);
+ return NvResult::BadValue;
}
}
- // We can only map objects that have already been assigned a CPU address.
- ASSERT(object->status == nvmap::Object::Status::Allocated);
-
- const auto physical_address{object->addr + params.buffer_offset};
- u64 size{params.mapping_size};
- if (!size) {
- size = object->size;
+ auto handle{nvmap.GetHandle(params.handle)};
+ if (!handle) {
+ return NvResult::BadValue;
}
- const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None};
- if (is_alloc) {
- params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size);
- } else {
- params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
- }
+ VAddr cpu_address{static_cast<VAddr>(handle->address + params.buffer_offset)};
+ u64 size{params.mapping_size ? params.mapping_size : handle->orig_size};
+
+ bool big_page{[&]() {
+ if (Common::IsAligned(handle->align, vm.big_page_size)) {
+ return true;
+ } else if (Common::IsAligned(handle->align, VM::YUZU_PAGESIZE)) {
+ return false;
+ } else {
+ ASSERT(false);
+ return false;
+ }
+ }()};
+
+ if ((params.flags & MappingFlags::Fixed) != MappingFlags::None) {
+ auto alloc{allocation_map.upper_bound(params.offset)};
- auto result = NvResult::Success;
- if (!params.offset) {
- LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
- result = NvResult::InvalidState;
+ if (alloc-- == allocation_map.begin() ||
+ (params.offset - alloc->first) + size > alloc->second.size) {
+ ASSERT_MSG(false, "Cannot perform a fixed mapping into an unallocated region!");
+ return NvResult::BadValue;
+ }
+
+ const bool use_big_pages = alloc->second.big_pages && big_page;
+ gmmu->Map(params.offset, cpu_address, size, use_big_pages);
+
+ auto mapping{std::make_shared<Mapping>(cpu_address, params.offset, size, true,
+ use_big_pages, alloc->second.sparse)};
+ alloc->second.mappings.push_back(mapping);
+ mapping_map[params.offset] = mapping;
} else {
- AddBufferMap(params.offset, size, physical_address, is_alloc);
+
+ auto& allocator{big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
+ u32 page_size{big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
+ u32 page_size_bits{big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
+
+ params.offset = static_cast<u64>(allocator.Allocate(
+ static_cast<u32>(Common::AlignUp(size, page_size) >> page_size_bits)))
+ << page_size_bits;
+ if (!params.offset) {
+ ASSERT_MSG(false, "Failed to allocate free space in the GPU AS!");
+ return NvResult::InsufficientMemory;
+ }
+
+ gmmu->Map(params.offset, cpu_address, Common::AlignUp(size, page_size), big_page);
+
+ auto mapping{
+ std::make_shared<Mapping>(cpu_address, params.offset, size, false, big_page, false)};
+ mapping_map[params.offset] = mapping;
}
std::memcpy(output.data(), &params, output.size());
- return result;
+ return NvResult::Success;
}
NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -255,47 +426,82 @@ NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8
LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset);
- if (const auto size{RemoveBufferMap(params.offset)}; size) {
- system.GPU().MemoryManager().Unmap(params.offset, *size);
- } else {
- LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset);
+ std::scoped_lock lock(mutex);
+
+ if (!vm.initialised) {
+ return NvResult::BadValue;
+ }
+
+ try {
+ auto mapping{mapping_map.at(params.offset)};
+
+ if (!mapping->fixed) {
+ auto& allocator{mapping->big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
+ u32 page_size_bits{mapping->big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
+
+ allocator.Free(static_cast<u32>(mapping->offset >> page_size_bits),
+ static_cast<u32>(mapping->size >> page_size_bits));
+ }
+
+ // Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
+ // Only FreeSpace can unmap them fully
+ if (mapping->sparse_alloc) {
+ gmmu->MapSparse(params.offset, mapping->size, mapping->big_page);
+ } else {
+ gmmu->Unmap(params.offset, mapping->size);
+ }
+
+ mapping_map.erase(params.offset);
+ } catch (const std::out_of_range&) {
+ LOG_WARNING(Service_NVDRV, "Couldn't find region to unmap at 0x{:X}", params.offset);
}
- std::memcpy(output.data(), &params, output.size());
return NvResult::Success;
}
NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd);
+ LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
- channel = params.fd;
+ auto gpu_channel_device = module.GetDevice<nvhost_gpu>(params.fd);
+ gpu_channel_device->channel_state->memory_manager = gmmu;
return NvResult::Success;
}
+void nvhost_as_gpu::GetVARegionsImpl(IoctlGetVaRegions& params) {
+ params.buf_size = 2 * sizeof(VaRegion);
+
+ params.regions = std::array<VaRegion, 2>{
+ VaRegion{
+ .offset = vm.small_page_allocator->GetVAStart() << VM::PAGE_SIZE_BITS,
+ .page_size = VM::YUZU_PAGESIZE,
+ ._pad0_{},
+ .pages = vm.small_page_allocator->GetVALimit() - vm.small_page_allocator->GetVAStart(),
+ },
+ VaRegion{
+ .offset = vm.big_page_allocator->GetVAStart() << vm.big_page_size_bits,
+ .page_size = vm.big_page_size,
+ ._pad0_{},
+ .pages = vm.big_page_allocator->GetVALimit() - vm.big_page_allocator->GetVAStart(),
+ },
+ };
+}
+
NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
- params.buf_size);
-
- params.buf_size = 0x30;
+ LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
+ params.buf_size);
- params.small = IoctlVaRegion{
- .offset = 0x04000000,
- .page_size = DEFAULT_SMALL_PAGE_SIZE,
- .pages = 0x3fbfff,
- };
+ std::scoped_lock lock(mutex);
- params.big = IoctlVaRegion{
- .offset = 0x04000000,
- .page_size = big_page_size,
- .pages = 0x1bffff,
- };
+ if (!vm.initialised) {
+ return NvResult::BadValue;
+ }
- // TODO(ogniK): This probably can stay stubbed but should add support way way later
+ GetVARegionsImpl(params);
std::memcpy(output.data(), &params, output.size());
return NvResult::Success;
@@ -306,62 +512,27 @@ NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
- params.buf_size);
-
- params.buf_size = 0x30;
+ LOG_DEBUG(Service_NVDRV, "called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
+ params.buf_size);
- params.small = IoctlVaRegion{
- .offset = 0x04000000,
- .page_size = 0x1000,
- .pages = 0x3fbfff,
- };
+ std::scoped_lock lock(mutex);
- params.big = IoctlVaRegion{
- .offset = 0x04000000,
- .page_size = big_page_size,
- .pages = 0x1bffff,
- };
+ if (!vm.initialised) {
+ return NvResult::BadValue;
+ }
- // TODO(ogniK): This probably can stay stubbed but should add support way way later
+ GetVARegionsImpl(params);
std::memcpy(output.data(), &params, output.size());
- std::memcpy(inline_output.data(), &params.small, sizeof(IoctlVaRegion));
- std::memcpy(inline_output.data() + sizeof(IoctlVaRegion), &params.big, sizeof(IoctlVaRegion));
+ std::memcpy(inline_output.data(), &params.regions[0], sizeof(VaRegion));
+ std::memcpy(inline_output.data() + sizeof(VaRegion), &params.regions[1], sizeof(VaRegion));
return NvResult::Success;
}
-std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {
- const auto end{buffer_mappings.upper_bound(gpu_addr)};
- for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) {
- if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) {
- return iter->second;
- }
- }
-
- return std::nullopt;
-}
-
-void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
- bool is_allocated) {
- buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated};
-}
-
-std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
- if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) {
- std::size_t size{};
-
- if (iter->second.IsAllocated()) {
- size = iter->second.Size();
- }
-
- buffer_mappings.erase(iter);
-
- return size;
- }
-
- return std::nullopt;
+Kernel::KEvent* nvhost_as_gpu::QueryEvent(u32 event_id) {
+ LOG_CRITICAL(Service_NVDRV, "Unknown AS GPU Event {}", event_id);
+ return nullptr;
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 24e3151cb..86fe71c75 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -1,36 +1,50 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
+#include <bit>
+#include <list>
#include <map>
#include <memory>
+#include <mutex>
#include <optional>
#include <vector>
+#include "common/address_space.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
-namespace Service::Nvidia::Devices {
+namespace Tegra {
+class MemoryManager;
+} // namespace Tegra
+
+namespace Service::Nvidia {
+class Module;
+}
-constexpr u32 DEFAULT_BIG_PAGE_SIZE = 1 << 16;
-constexpr u32 DEFAULT_SMALL_PAGE_SIZE = 1 << 12;
+namespace Service::Nvidia::NvCore {
+class Container;
+class NvMap;
+} // namespace Service::Nvidia::NvCore
-class nvmap;
+namespace Service::Nvidia::Devices {
-enum class AddressSpaceFlags : u32 {
- None = 0x0,
- FixedOffset = 0x1,
- Remap = 0x100,
+enum class MappingFlags : u32 {
+ None = 0,
+ Fixed = 1 << 0,
+ Sparse = 1 << 1,
+ Remap = 1 << 8,
};
-DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags);
+DECLARE_ENUM_FLAG_OPERATORS(MappingFlags);
class nvhost_as_gpu final : public nvdevice {
public:
- explicit nvhost_as_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_);
+ explicit nvhost_as_gpu(Core::System& system_, Module& module, NvCore::Container& core);
~nvhost_as_gpu() override;
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -43,46 +57,17 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
-private:
- class BufferMap final {
- public:
- constexpr BufferMap() = default;
-
- constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_)
- : start_addr{start_addr_}, end_addr{start_addr_ + size_} {}
-
- constexpr BufferMap(GPUVAddr start_addr_, std::size_t size_, VAddr cpu_addr_,
- bool is_allocated_)
- : start_addr{start_addr_}, end_addr{start_addr_ + size_}, cpu_addr{cpu_addr_},
- is_allocated{is_allocated_} {}
-
- constexpr VAddr StartAddr() const {
- return start_addr;
- }
-
- constexpr VAddr EndAddr() const {
- return end_addr;
- }
-
- constexpr std::size_t Size() const {
- return end_addr - start_addr;
- }
-
- constexpr VAddr CpuAddr() const {
- return cpu_addr;
- }
-
- constexpr bool IsAllocated() const {
- return is_allocated;
- }
-
- private:
- GPUVAddr start_addr{};
- GPUVAddr end_addr{};
- VAddr cpu_addr{};
- bool is_allocated{};
+ Kernel::KEvent* QueryEvent(u32 event_id) override;
+
+ struct VaRegion {
+ u64 offset;
+ u32 page_size;
+ u32 _pad0_;
+ u64 pages;
};
+ static_assert(sizeof(VaRegion) == 0x18);
+private:
struct IoctlAllocAsEx {
u32_le flags{}; // usually passes 1
s32_le as_fd{}; // ignored; passes 0
@@ -97,7 +82,7 @@ private:
struct IoctlAllocSpace {
u32_le pages{};
u32_le page_size{};
- AddressSpaceFlags flags{};
+ MappingFlags flags{};
INSERT_PADDING_WORDS(1);
union {
u64_le offset;
@@ -114,19 +99,19 @@ private:
static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
struct IoctlRemapEntry {
- u16_le flags{};
- u16_le kind{};
- u32_le nvmap_handle{};
- u32_le map_offset{};
- u32_le offset{};
- u32_le pages{};
+ u16 flags;
+ u16 kind;
+ NvCore::NvMap::Handle::Id handle;
+ u32 handle_offset_big_pages;
+ u32 as_offset_big_pages;
+ u32 big_pages;
};
static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
struct IoctlMapBufferEx {
- AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable
- u32_le kind{}; // -1 is default
- u32_le nvmap_handle{};
+ MappingFlags flags{}; // bit0: fixed_offset, bit2: cacheable
+ u32_le kind{}; // -1 is default
+ NvCore::NvMap::Handle::Id handle;
u32_le page_size{}; // 0 means don't care
s64_le buffer_offset{};
u64_le mapping_size{};
@@ -144,27 +129,15 @@ private:
};
static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
- struct IoctlVaRegion {
- u64_le offset{};
- u32_le page_size{};
- INSERT_PADDING_WORDS(1);
- u64_le pages{};
- };
- static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
-
struct IoctlGetVaRegions {
u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
u32_le reserved{};
- IoctlVaRegion small{};
- IoctlVaRegion big{};
+ std::array<VaRegion, 2> regions{};
};
- static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
+ static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(VaRegion) * 2,
"IoctlGetVaRegions is incorrect size");
- s32 channel{};
- u32 big_page_size{DEFAULT_BIG_PAGE_SIZE};
-
NvResult AllocAsEx(const std::vector<u8>& input, std::vector<u8>& output);
NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
@@ -173,18 +146,75 @@ private:
NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
+ void GetVARegionsImpl(IoctlGetVaRegions& params);
NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
std::vector<u8>& inline_output);
- std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
- void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
- std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
+ void FreeMappingLocked(u64 offset);
+
+ Module& module;
+
+ NvCore::Container& container;
+ NvCore::NvMap& nvmap;
- std::shared_ptr<nvmap> nvmap_dev;
+ struct Mapping {
+ VAddr ptr;
+ u64 offset;
+ u64 size;
+ bool fixed;
+ bool big_page; // Only valid if fixed == false
+ bool sparse_alloc;
+
+ Mapping(VAddr ptr_, u64 offset_, u64 size_, bool fixed_, bool big_page_, bool sparse_alloc_)
+ : ptr(ptr_), offset(offset_), size(size_), fixed(fixed_), big_page(big_page_),
+ sparse_alloc(sparse_alloc_) {}
+ };
+
+ struct Allocation {
+ u64 size;
+ std::list<std::shared_ptr<Mapping>> mappings;
+ u32 page_size;
+ bool sparse;
+ bool big_pages;
+ };
- // This is expected to be ordered, therefore we must use a map, not unordered_map
- std::map<GPUVAddr, BufferMap> buffer_mappings;
+ std::map<u64, std::shared_ptr<Mapping>>
+ mapping_map; //!< This maps the base addresses of mapped buffers to their total sizes and
+ //!< mapping type, this is needed as what was originally a single buffer may
+ //!< have been split into multiple GPU side buffers with the remap flag.
+ std::map<u64, Allocation> allocation_map; //!< Holds allocations created by AllocSpace from
+ //!< which fixed buffers can be mapped into
+ std::mutex mutex; //!< Locks all AS operations
+
+ struct VM {
+ static constexpr u32 YUZU_PAGESIZE{0x1000};
+ static constexpr u32 PAGE_SIZE_BITS{std::countr_zero(YUZU_PAGESIZE)};
+
+ static constexpr u32 SUPPORTED_BIG_PAGE_SIZES{0x30000};
+ static constexpr u32 DEFAULT_BIG_PAGE_SIZE{0x20000};
+ u32 big_page_size{DEFAULT_BIG_PAGE_SIZE};
+ u32 big_page_size_bits{std::countr_zero(DEFAULT_BIG_PAGE_SIZE)};
+
+ static constexpr u32 VA_START_SHIFT{10};
+ static constexpr u64 DEFAULT_VA_SPLIT{1ULL << 34};
+ static constexpr u64 DEFAULT_VA_RANGE{1ULL << 37};
+ u64 va_range_start{DEFAULT_BIG_PAGE_SIZE << VA_START_SHIFT};
+ u64 va_range_split{DEFAULT_VA_SPLIT};
+ u64 va_range_end{DEFAULT_VA_RANGE};
+
+ using Allocator = Common::FlatAllocator<u32, 0, 32>;
+
+ std::unique_ptr<Allocator> big_page_allocator;
+ std::shared_ptr<Allocator>
+ small_page_allocator; //! Shared as this is also used by nvhost::GpuChannel
+
+ bool initialised{};
+ } vm;
+ std::shared_ptr<Tegra::MemoryManager> gmmu;
+
+ // s32 channel{};
+ // u32 big_page_size{VM::DEFAULT_BIG_PAGE_SIZE};
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index f9b82b504..5bee4a3d3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -1,25 +1,39 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
+#include <bit>
#include <cstdlib>
#include <cstring>
+#include <fmt/format.h>
#include "common/assert.h"
#include "common/logging/log.h"
+#include "common/scope_exit.h"
#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_writable_event.h"
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
#include "video_core/gpu.h"
+#include "video_core/host1x/host1x.h"
namespace Service::Nvidia::Devices {
nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_,
- SyncpointManager& syncpoint_manager_)
- : nvdevice{system_}, events_interface{events_interface_}, syncpoint_manager{
- syncpoint_manager_} {}
-nvhost_ctrl::~nvhost_ctrl() = default;
+ NvCore::Container& core_)
+ : nvdevice{system_}, events_interface{events_interface_}, core{core_},
+ syncpoint_manager{core_.GetSyncpointManager()} {}
+
+nvhost_ctrl::~nvhost_ctrl() {
+ for (auto& event : events) {
+ if (!event.registered) {
+ continue;
+ }
+ events_interface.FreeEvent(event.kevent);
+ }
+}
NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
@@ -31,13 +45,15 @@ NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
case 0x1c:
return IocCtrlClearEventWait(input, output);
case 0x1d:
- return IocCtrlEventWait(input, output, false);
- case 0x1e:
return IocCtrlEventWait(input, output, true);
+ case 0x1e:
+ return IocCtrlEventWait(input, output, false);
case 0x1f:
return IocCtrlEventRegister(input, output);
case 0x20:
return IocCtrlEventUnregister(input, output);
+ case 0x21:
+ return IocCtrlEventUnregisterBatch(input, output);
}
break;
default:
@@ -61,6 +77,7 @@ NvResult nvhost_ctrl::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
}
void nvhost_ctrl::OnOpen(DeviceFD fd) {}
+
void nvhost_ctrl::OnClose(DeviceFD fd) {}
NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
@@ -72,116 +89,167 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
}
NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
- bool is_async) {
+ bool is_allocation) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
- LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
- params.syncpt_id, params.threshold, params.timeout, is_async);
+ LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
+ params.fence.id, params.fence.value, params.timeout, is_allocation);
- if (params.syncpt_id >= MaxSyncPoints) {
- return NvResult::BadParameter;
- }
+ bool must_unmark_fail = !is_allocation;
+ const u32 event_id = params.value.raw;
+ SCOPE_EXIT({
+ std::memcpy(output.data(), &params, sizeof(params));
+ if (must_unmark_fail) {
+ events[event_id].fails = 0;
+ }
+ });
- u32 event_id = params.value & 0x00FF;
+ const u32 fence_id = static_cast<u32>(params.fence.id);
- if (event_id >= MaxNvEvents) {
- std::memcpy(output.data(), &params, sizeof(params));
+ if (fence_id >= MaxSyncPoints) {
return NvResult::BadParameter;
}
- if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
- params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
- std::memcpy(output.data(), &params, sizeof(params));
- events_interface.failed[event_id] = false;
+ if (params.fence.value == 0) {
+ if (!syncpoint_manager.IsSyncpointAllocated(params.fence.id)) {
+ LOG_WARNING(Service_NVDRV,
+ "Unallocated syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
+ params.fence.id, params.fence.value, params.timeout, is_allocation);
+ } else {
+ params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id);
+ }
return NvResult::Success;
}
- if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id);
- syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
- params.value = new_value;
- std::memcpy(output.data(), &params, sizeof(params));
- events_interface.failed[event_id] = false;
+ if (syncpoint_manager.IsFenceSignalled(params.fence)) {
+ params.value.raw = syncpoint_manager.ReadSyncpointMinValue(fence_id);
return NvResult::Success;
}
- auto& event = events_interface.events[event_id];
- auto& gpu = system.GPU();
-
- // This is mostly to take into account unimplemented features. As synced
- // gpu is always synced.
- if (!gpu.IsAsync()) {
- event.event->GetWritableEvent().Signal();
- return NvResult::Success;
- }
- const u32 current_syncpoint_value = event.fence.value;
- const s32 diff = current_syncpoint_value - params.threshold;
- if (diff >= 0) {
- event.event->GetWritableEvent().Signal();
- params.value = current_syncpoint_value;
- std::memcpy(output.data(), &params, sizeof(params));
- events_interface.failed[event_id] = false;
+ if (const auto new_value = syncpoint_manager.UpdateMin(fence_id);
+ syncpoint_manager.IsFenceSignalled(params.fence)) {
+ params.value.raw = new_value;
return NvResult::Success;
}
- const u32 target_value = current_syncpoint_value - diff;
- if (!is_async) {
- params.value = 0;
+ auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager();
+ const u32 target_value = params.fence.value;
+
+ auto lock = NvEventsLock();
+
+ u32 slot = [&]() {
+ if (is_allocation) {
+ params.value.raw = 0;
+ return FindFreeNvEvent(fence_id);
+ } else {
+ return params.value.raw;
+ }
+ }();
+
+ must_unmark_fail = false;
+
+ const auto check_failing = [&]() {
+ if (events[slot].fails > 2) {
+ {
+ auto lk = system.StallProcesses();
+ host1x_syncpoint_manager.WaitHost(fence_id, target_value);
+ system.UnstallProcesses();
+ }
+ params.value.raw = target_value;
+ return true;
+ }
+ return false;
+ };
+
+ if (slot >= MaxNvEvents) {
+ return NvResult::BadParameter;
}
if (params.timeout == 0) {
- std::memcpy(output.data(), &params, sizeof(params));
+ if (check_failing()) {
+ events[slot].fails = 0;
+ return NvResult::Success;
+ }
return NvResult::Timeout;
}
- EventState status = events_interface.status[event_id];
- const bool bad_parameter = status != EventState::Free && status != EventState::Registered;
- if (bad_parameter) {
- std::memcpy(output.data(), &params, sizeof(params));
+ auto& event = events[slot];
+
+ if (!event.registered) {
return NvResult::BadParameter;
}
- events_interface.SetEventStatus(event_id, EventState::Waiting);
- events_interface.assigned_syncpt[event_id] = params.syncpt_id;
- events_interface.assigned_value[event_id] = target_value;
- if (is_async) {
- params.value = params.syncpt_id << 4;
- } else {
- params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
- }
- params.value |= event_id;
- event.event->GetWritableEvent().Clear();
- if (events_interface.failed[event_id]) {
- {
- auto lk = system.StallCPU();
- gpu.WaitFence(params.syncpt_id, target_value);
- system.UnstallCPU();
- }
- std::memcpy(output.data(), &params, sizeof(params));
- events_interface.failed[event_id] = false;
+
+ if (event.IsBeingUsed()) {
+ return NvResult::BadParameter;
+ }
+
+ if (check_failing()) {
+ event.fails = 0;
return NvResult::Success;
}
- gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
- std::memcpy(output.data(), &params, sizeof(params));
+
+ params.value.raw = 0;
+
+ event.status.store(EventState::Waiting, std::memory_order_release);
+ event.assigned_syncpt = fence_id;
+ event.assigned_value = target_value;
+ if (is_allocation) {
+ params.value.syncpoint_id_for_allocation.Assign(static_cast<u16>(fence_id));
+ params.value.event_allocated.Assign(1);
+ } else {
+ params.value.syncpoint_id.Assign(fence_id);
+ }
+ params.value.raw |= slot;
+
+ event.wait_handle =
+ host1x_syncpoint_manager.RegisterHostAction(fence_id, target_value, [this, slot]() {
+ auto& event_ = events[slot];
+ if (event_.status.exchange(EventState::Signalling, std::memory_order_acq_rel) ==
+ EventState::Waiting) {
+ event_.kevent->GetWritableEvent().Signal();
+ }
+ event_.status.store(EventState::Signalled, std::memory_order_release);
+ });
return NvResult::Timeout;
}
+NvResult nvhost_ctrl::FreeEvent(u32 slot) {
+ if (slot >= MaxNvEvents) {
+ return NvResult::BadParameter;
+ }
+
+ auto& event = events[slot];
+
+ if (!event.registered) {
+ return NvResult::Success;
+ }
+
+ if (event.IsBeingUsed()) {
+ return NvResult::Busy;
+ }
+
+ FreeNvEvent(slot);
+ return NvResult::Success;
+}
+
NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventRegisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
- const u32 event_id = params.user_event_id & 0x00FF;
+ const u32 event_id = params.user_event_id;
LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
if (event_id >= MaxNvEvents) {
return NvResult::BadParameter;
}
- if (events_interface.registered[event_id]) {
- const auto event_state = events_interface.status[event_id];
- if (event_state != EventState::Free) {
- LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event");
- events_interface.UnregisterEvent(event_id);
- } else {
- return NvResult::BadParameter;
+
+ auto lock = NvEventsLock();
+
+ if (events[event_id].registered) {
+ const auto result = FreeEvent(event_id);
+ if (result != NvResult::Success) {
+ return result;
}
}
- events_interface.RegisterEvent(event_id);
+ CreateNvEvent(event_id);
return NvResult::Success;
}
@@ -191,34 +259,142 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF;
LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
- if (event_id >= MaxNvEvents) {
- return NvResult::BadParameter;
- }
- if (!events_interface.registered[event_id]) {
- return NvResult::BadParameter;
+
+ auto lock = NvEventsLock();
+ return FreeEvent(event_id);
+}
+
+NvResult nvhost_ctrl::IocCtrlEventUnregisterBatch(const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ IocCtrlEventUnregisterBatchParams params{};
+ std::memcpy(&params, input.data(), sizeof(params));
+ u64 event_mask = params.user_events;
+ LOG_DEBUG(Service_NVDRV, " called, event_mask: {:X}", event_mask);
+
+ auto lock = NvEventsLock();
+ while (event_mask != 0) {
+ const u64 event_id = std::countr_zero(event_mask);
+ event_mask &= ~(1ULL << event_id);
+ const auto result = FreeEvent(static_cast<u32>(event_id));
+ if (result != NvResult::Success) {
+ return result;
+ }
}
- events_interface.UnregisterEvent(event_id);
return NvResult::Success;
}
NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
- IocCtrlEventSignalParams params{};
+ IocCtrlEventClearParams params{};
std::memcpy(&params, input.data(), sizeof(params));
- u32 event_id = params.event_id & 0x00FF;
- LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
+ u32 event_id = params.event_id.slot;
+ LOG_DEBUG(Service_NVDRV, "called, event_id: {:X}", event_id);
if (event_id >= MaxNvEvents) {
return NvResult::BadParameter;
}
- if (events_interface.status[event_id] == EventState::Waiting) {
- events_interface.LiberateEvent(event_id);
- }
- events_interface.failed[event_id] = true;
- syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
+ auto lock = NvEventsLock();
+
+ auto& event = events[event_id];
+ if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) ==
+ EventState::Waiting) {
+ auto& host1x_syncpoint_manager = system.Host1x().GetSyncpointManager();
+ host1x_syncpoint_manager.DeregisterHostAction(event.assigned_syncpt, event.wait_handle);
+ syncpoint_manager.UpdateMin(event.assigned_syncpt);
+ event.wait_handle = {};
+ }
+ event.fails++;
+ event.status.store(EventState::Cancelled, std::memory_order_release);
+ event.kevent->GetWritableEvent().Clear();
return NvResult::Success;
}
+Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) {
+ const auto desired_event = SyncpointEventValue{.raw = event_id};
+
+ const bool allocated = desired_event.event_allocated.Value() != 0;
+ const u32 slot{allocated ? desired_event.partial_slot.Value()
+ : static_cast<u32>(desired_event.slot)};
+ if (slot >= MaxNvEvents) {
+ ASSERT(false);
+ return nullptr;
+ }
+
+ const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value()
+ : desired_event.syncpoint_id.Value()};
+
+ auto lock = NvEventsLock();
+
+ auto& event = events[slot];
+ if (event.registered && event.assigned_syncpt == syncpoint_id) {
+ ASSERT(event.kevent);
+ return event.kevent;
+ }
+ // Is this possible in hardware?
+ ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id);
+ return nullptr;
+}
+
+std::unique_lock<std::mutex> nvhost_ctrl::NvEventsLock() {
+ return std::unique_lock<std::mutex>(events_mutex);
+}
+
+void nvhost_ctrl::CreateNvEvent(u32 event_id) {
+ auto& event = events[event_id];
+ ASSERT(!event.kevent);
+ ASSERT(!event.registered);
+ ASSERT(!event.IsBeingUsed());
+ event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id));
+ event.status = EventState::Available;
+ event.registered = true;
+ const u64 mask = 1ULL << event_id;
+ event.fails = 0;
+ events_mask |= mask;
+ event.assigned_syncpt = 0;
+}
+
+void nvhost_ctrl::FreeNvEvent(u32 event_id) {
+ auto& event = events[event_id];
+ ASSERT(event.kevent);
+ ASSERT(event.registered);
+ ASSERT(!event.IsBeingUsed());
+ events_interface.FreeEvent(event.kevent);
+ event.kevent = nullptr;
+ event.status = EventState::Available;
+ event.registered = false;
+ const u64 mask = ~(1ULL << event_id);
+ events_mask &= mask;
+}
+
+u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) {
+ u32 slot{MaxNvEvents};
+ u32 free_slot{MaxNvEvents};
+ for (u32 i = 0; i < MaxNvEvents; i++) {
+ auto& event = events[i];
+ if (event.registered) {
+ if (!event.IsBeingUsed()) {
+ slot = i;
+ if (event.assigned_syncpt == syncpoint_id) {
+ return slot;
+ }
+ }
+ } else if (free_slot == MaxNvEvents) {
+ free_slot = i;
+ }
+ }
+ if (free_slot < MaxNvEvents) {
+ CreateNvEvent(free_slot);
+ return free_slot;
+ }
+
+ if (slot < MaxNvEvents) {
+ return slot;
+ }
+
+ LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event");
+ return 0;
+}
+
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index cdf03887d..0b56d7070 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -1,21 +1,28 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <vector>
+#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/nvdrv.h"
+#include "video_core/host1x/syncpoint_manager.h"
+
+namespace Service::Nvidia::NvCore {
+class Container;
+class SyncpointManager;
+} // namespace Service::Nvidia::NvCore
namespace Service::Nvidia::Devices {
class nvhost_ctrl final : public nvdevice {
public:
explicit nvhost_ctrl(Core::System& system_, EventInterface& events_interface_,
- SyncpointManager& syncpoint_manager_);
+ NvCore::Container& core);
~nvhost_ctrl() override;
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -28,7 +35,70 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+ Kernel::KEvent* QueryEvent(u32 event_id) override;
+
+ union SyncpointEventValue {
+ u32 raw;
+
+ union {
+ BitField<0, 4, u32> partial_slot;
+ BitField<4, 28, u32> syncpoint_id;
+ };
+
+ struct {
+ u16 slot;
+ union {
+ BitField<0, 12, u16> syncpoint_id_for_allocation;
+ BitField<12, 1, u16> event_allocated;
+ };
+ };
+ };
+ static_assert(sizeof(SyncpointEventValue) == sizeof(u32));
+
private:
+ struct InternalEvent {
+ // Mask representing registered events
+
+ // Each kernel event associated to an NV event
+ Kernel::KEvent* kevent{};
+ // The status of the current NVEvent
+ std::atomic<EventState> status{};
+
+ // Tells the NVEvent that it has failed.
+ u32 fails{};
+ // When an NVEvent is waiting on GPU interrupt, this is the sync_point
+ // associated with it.
+ u32 assigned_syncpt{};
+ // This is the value of the GPU interrupt for which the NVEvent is waiting
+ // for.
+ u32 assigned_value{};
+
+ // Tells if an NVEvent is registered or not
+ bool registered{};
+
+ // Used for waiting on a syncpoint & canceling it.
+ Tegra::Host1x::SyncpointManager::ActionHandle wait_handle{};
+
+ bool IsBeingUsed() const {
+ const auto current_status = status.load(std::memory_order_acquire);
+ return current_status == EventState::Waiting ||
+ current_status == EventState::Cancelling ||
+ current_status == EventState::Signalling;
+ }
+ };
+
+ std::unique_lock<std::mutex> NvEventsLock();
+
+ void CreateNvEvent(u32 event_id);
+
+ void FreeNvEvent(u32 event_id);
+
+ u32 FindFreeNvEvent(u32 syncpoint_id);
+
+ std::array<InternalEvent, MaxNvEvents> events{};
+ std::mutex events_mutex;
+ u64 events_mask{};
+
struct IocSyncptReadParams {
u32_le id{};
u32_le value{};
@@ -84,27 +154,18 @@ private:
};
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
- struct IocCtrlEventSignalParams {
- u32_le event_id{};
+ struct IocCtrlEventClearParams {
+ SyncpointEventValue event_id{};
};
- static_assert(sizeof(IocCtrlEventSignalParams) == 4,
- "IocCtrlEventSignalParams is incorrect size");
+ static_assert(sizeof(IocCtrlEventClearParams) == 4,
+ "IocCtrlEventClearParams is incorrect size");
struct IocCtrlEventWaitParams {
- u32_le syncpt_id{};
- u32_le threshold{};
- s32_le timeout{};
- u32_le value{};
- };
- static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
-
- struct IocCtrlEventWaitAsyncParams {
- u32_le syncpt_id{};
- u32_le threshold{};
+ NvFence fence{};
u32_le timeout{};
- u32_le value{};
+ SyncpointEventValue value{};
};
- static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
+ static_assert(sizeof(IocCtrlEventWaitParams) == 16,
"IocCtrlEventWaitAsyncParams is incorrect size");
struct IocCtrlEventRegisterParams {
@@ -119,19 +180,25 @@ private:
static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
"IocCtrlEventUnregisterParams is incorrect size");
- struct IocCtrlEventKill {
+ struct IocCtrlEventUnregisterBatchParams {
u64_le user_events{};
};
- static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
+ static_assert(sizeof(IocCtrlEventUnregisterBatchParams) == 8,
+ "IocCtrlEventKill is incorrect size");
NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
- NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
+ NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
+ bool is_allocation);
NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCtrlEventUnregisterBatch(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult FreeEvent(u32 slot);
+
EventInterface& events_interface;
- SyncpointManager& syncpoint_manager;
+ NvCore::Container& core;
+ NvCore::SyncpointManager& syncpoint_manager;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 05b4e2151..ced57dfe6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/assert.h"
@@ -8,11 +7,19 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
namespace Service::Nvidia::Devices {
-nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_) : nvdevice{system_} {}
-nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
+nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_)
+ : nvdevice{system_}, events_interface{events_interface_} {
+ error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier");
+ unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent");
+}
+nvhost_ctrl_gpu::~nvhost_ctrl_gpu() {
+ events_interface.FreeEvent(error_notifier_event);
+ events_interface.FreeEvent(unknown_event);
+}
NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
@@ -287,4 +294,17 @@ NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u
return NvResult::Success;
}
+Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) {
+ switch (event_id) {
+ case 1:
+ return error_notifier_event;
+ case 2:
+ return unknown_event;
+ default: {
+ LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
+ }
+ }
+ return nullptr;
+}
+
} // namespace Service::Nvidia::Devices
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..1e8f254e2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -1,19 +1,24 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
+
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
+namespace Service::Nvidia {
+class EventInterface;
+}
+
namespace Service::Nvidia::Devices {
class nvhost_ctrl_gpu final : public nvdevice {
public:
- explicit nvhost_ctrl_gpu(Core::System& system_);
+ explicit nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_);
~nvhost_ctrl_gpu() override;
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -26,6 +31,8 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+ Kernel::KEvent* QueryEvent(u32 event_id) override;
+
private:
struct IoctlGpuCharacteristics {
u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
@@ -159,6 +166,12 @@ private:
NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
+
+ EventInterface& events_interface;
+
+ // Events
+ Kernel::KEvent* error_notifier_event;
+ Kernel::KEvent* unknown_event;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 0a043e386..45a759fa8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -1,34 +1,50 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
+#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
-#include "core/hle/service/nvdrv/syncpoint_manager.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/memory.h"
+#include "video_core/control/channel_state.h"
+#include "video_core/engines/puller.h"
#include "video_core/gpu.h"
+#include "video_core/host1x/host1x.h"
namespace Service::Nvidia::Devices {
namespace {
-Tegra::CommandHeader BuildFenceAction(Tegra::GPU::FenceOperation op, u32 syncpoint_id) {
- Tegra::GPU::FenceAction result{};
+Tegra::CommandHeader BuildFenceAction(Tegra::Engines::Puller::FenceOperation op, u32 syncpoint_id) {
+ Tegra::Engines::Puller::FenceAction result{};
result.op.Assign(op);
result.syncpoint_id.Assign(syncpoint_id);
return {result.raw};
}
} // namespace
-nvhost_gpu::nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
- SyncpointManager& syncpoint_manager_)
- : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {
- channel_fence.id = syncpoint_manager_.AllocateSyncpoint();
- channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id);
+nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_,
+ NvCore::Container& core_)
+ : nvdevice{system_}, events_interface{events_interface_}, core{core_},
+ syncpoint_manager{core_.GetSyncpointManager()}, nvmap{core.GetNvMapFile()},
+ channel_state{system.GPU().AllocateChannel()} {
+ channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
+ sm_exception_breakpoint_int_report_event =
+ events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt");
+ sm_exception_breakpoint_pause_report_event =
+ events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause");
+ error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier");
}
-nvhost_gpu::~nvhost_gpu() = default;
+nvhost_gpu::~nvhost_gpu() {
+ events_interface.FreeEvent(sm_exception_breakpoint_int_report_event);
+ events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event);
+ events_interface.FreeEvent(error_notifier_event);
+ syncpoint_manager.FreeSyncpoint(channel_syncpoint);
+}
NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) {
@@ -168,9 +184,14 @@ NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8
params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
params.unk3);
- channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
+ if (channel_state->initialized) {
+ LOG_CRITICAL(Service_NVDRV, "Already allocated!");
+ return NvResult::AlreadyAllocated;
+ }
+
+ system.GPU().InitChannel(*channel_state);
- params.fence_out = channel_fence;
+ params.fence_out = syncpoint_manager.GetSyncpointFence(channel_syncpoint);
std::memcpy(output.data(), &params, output.size());
return NvResult::Success;
@@ -187,40 +208,39 @@ NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::ve
return NvResult::Success;
}
-static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
+static std::vector<Tegra::CommandHeader> BuildWaitCommandList(NvFence fence) {
return {
- Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
Tegra::SubmissionMode::Increasing),
{fence.value},
- Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1,
Tegra::SubmissionMode::Increasing),
- BuildFenceAction(Tegra::GPU::FenceOperation::Acquire, fence.id),
+ BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Acquire, fence.id),
};
}
-static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
+static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(NvFence fence) {
std::vector<Tegra::CommandHeader> result{
- Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointPayload, 1,
Tegra::SubmissionMode::Increasing),
{}};
- for (u32 count = 0; count < add_increment; ++count) {
- result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
+ for (u32 count = 0; count < 2; ++count) {
+ result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::SyncpointOperation, 1,
Tegra::SubmissionMode::Increasing));
- result.emplace_back(BuildFenceAction(Tegra::GPU::FenceOperation::Increment, fence.id));
+ result.emplace_back(
+ BuildFenceAction(Tegra::Engines::Puller::FenceOperation::Increment, fence.id));
}
return result;
}
-static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
- u32 add_increment) {
+static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(NvFence fence) {
std::vector<Tegra::CommandHeader> result{
- Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForIdle, 1,
Tegra::SubmissionMode::Increasing),
{}};
- const std::vector<Tegra::CommandHeader> increment{
- BuildIncrementCommandList(fence, add_increment)};
+ const std::vector<Tegra::CommandHeader> increment{BuildIncrementCommandList(fence)};
result.insert(result.end(), increment.begin(), increment.end());
@@ -234,33 +254,41 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>
auto& gpu = system.GPU();
- params.fence_out.id = channel_fence.id;
+ std::scoped_lock lock(channel_mutex);
- if (params.flags.add_wait.Value() &&
- !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
- gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
- }
+ const auto bind_id = channel_state->bind_id;
- if (params.flags.add_increment.Value() || params.flags.increment.Value()) {
- const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0;
- params.fence_out.value = syncpoint_manager.IncreaseSyncpoint(
- params.fence_out.id, params.AddIncrementValue() + increment_value);
- } else {
- params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id);
+ auto& flags = params.flags;
+
+ if (flags.fence_wait.Value()) {
+ if (flags.increment_value.Value()) {
+ return NvResult::BadParameter;
+ }
+
+ if (!syncpoint_manager.IsFenceSignalled(params.fence)) {
+ gpu.PushGPUEntries(bind_id, Tegra::CommandList{BuildWaitCommandList(params.fence)});
+ }
}
- gpu.PushGPUEntries(std::move(entries));
+ params.fence.id = channel_syncpoint;
+
+ u32 increment{(flags.fence_increment.Value() != 0 ? 2 : 0) +
+ (flags.increment_value.Value() != 0 ? params.fence.value : 0)};
+ params.fence.value = syncpoint_manager.IncrementSyncpointMaxExt(channel_syncpoint, increment);
+ gpu.PushGPUEntries(bind_id, std::move(entries));
- if (params.flags.add_increment.Value()) {
- if (params.flags.suppress_wfi) {
- gpu.PushGPUEntries(Tegra::CommandList{
- BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())});
+ if (flags.fence_increment.Value()) {
+ if (flags.suppress_wfi.Value()) {
+ gpu.PushGPUEntries(bind_id,
+ Tegra::CommandList{BuildIncrementCommandList(params.fence)});
} else {
- gpu.PushGPUEntries(Tegra::CommandList{
- BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())});
+ gpu.PushGPUEntries(bind_id,
+ Tegra::CommandList{BuildIncrementWithWfiCommandList(params.fence)});
}
}
+ flags.raw = 0;
+
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
return NvResult::Success;
}
@@ -328,4 +356,19 @@ NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vect
return NvResult::Success;
}
+Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) {
+ switch (event_id) {
+ case 1:
+ return sm_exception_breakpoint_int_report_event;
+ case 2:
+ return sm_exception_breakpoint_pause_report_event;
+ case 3:
+ return error_notifier_event;
+ default: {
+ LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id);
+ }
+ }
+ return nullptr;
+}
+
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index f27a82bff..1e4ecd55b 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -1,29 +1,43 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <vector>
#include "common/bit_field.h"
+#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "video_core/dma_pusher.h"
+namespace Tegra {
+namespace Control {
+struct ChannelState;
+}
+} // namespace Tegra
+
namespace Service::Nvidia {
+
+namespace NvCore {
+class Container;
+class NvMap;
class SyncpointManager;
-}
+} // namespace NvCore
+
+class EventInterface;
+} // namespace Service::Nvidia
namespace Service::Nvidia::Devices {
+class nvhost_as_gpu;
class nvmap;
class nvhost_gpu final : public nvdevice {
public:
- explicit nvhost_gpu(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
- SyncpointManager& syncpoint_manager_);
+ explicit nvhost_gpu(Core::System& system_, EventInterface& events_interface_,
+ NvCore::Container& core);
~nvhost_gpu() override;
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -36,7 +50,10 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+ Kernel::KEvent* QueryEvent(u32 event_id) override;
+
private:
+ friend class nvhost_as_gpu;
enum class CtxObjects : u32_le {
Ctx2D = 0x902D,
Ctx3D = 0xB197,
@@ -108,7 +125,7 @@ private:
static_assert(sizeof(IoctlGetErrorNotification) == 16,
"IoctlGetErrorNotification is incorrect size");
- static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
+ static_assert(sizeof(NvFence) == 8, "Fence is incorrect size");
struct IoctlAllocGpfifoEx {
u32_le num_entries{};
@@ -126,7 +143,7 @@ private:
u32_le num_entries{}; // in
u32_le flags{}; // in
u32_le unk0{}; // in (1 works)
- Fence fence_out{}; // out
+ NvFence fence_out{}; // out
u32_le unk1{}; // in
u32_le unk2{}; // in
u32_le unk3{}; // in
@@ -146,19 +163,15 @@ private:
u32_le num_entries{}; // number of fence objects being submitted
union {
u32_le raw;
- BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
- BitField<1, 1, u32_le> add_increment; // append an increment to the list
- BitField<2, 1, u32_le> new_hw_format; // mostly ignored
- BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
- BitField<8, 1, u32_le> increment; // increment the returned fence
+ BitField<0, 1, u32_le> fence_wait; // append a wait sync_point to the list
+ BitField<1, 1, u32_le> fence_increment; // append an increment to the list
+ BitField<2, 1, u32_le> new_hw_format; // mostly ignored
+ BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
+ BitField<8, 1, u32_le> increment_value; // increment the returned fence
} flags;
- Fence fence_out{}; // returned new fence object for others to wait on
-
- u32 AddIncrementValue() const {
- return flags.add_increment.Value() << 1;
- }
+ NvFence fence{}; // returned new fence object for others to wait on
};
- static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
+ static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(NvFence),
"IoctlSubmitGpfifo is incorrect size");
struct IoctlGetWaitbase {
@@ -191,9 +204,18 @@ private:
NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
- std::shared_ptr<nvmap> nvmap_dev;
- SyncpointManager& syncpoint_manager;
- Fence channel_fence;
+ EventInterface& events_interface;
+ NvCore::Container& core;
+ NvCore::SyncpointManager& syncpoint_manager;
+ NvCore::NvMap& nvmap;
+ std::shared_ptr<Tegra::Control::ChannelState> channel_state;
+ u32 channel_syncpoint;
+ std::mutex channel_mutex;
+
+ // Events
+ Kernel::KEvent* sm_exception_breakpoint_int_report_event;
+ Kernel::KEvent* sm_exception_breakpoint_pause_report_event;
+ Kernel::KEvent* error_notifier_event;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 8314d1ec2..1703f9cc3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -1,18 +1,18 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "audio_core/audio_core.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
-nvhost_nvdec::nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
- SyncpointManager& syncpoint_manager_)
- : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {}
+nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core_)
+ : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::NvDec} {}
nvhost_nvdec::~nvhost_nvdec() = default;
NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -21,8 +21,9 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
case 0x0:
switch (command.cmd) {
case 0x1: {
- if (!fd_to_id.contains(fd)) {
- fd_to_id[fd] = next_id++;
+ auto& host1x_file = core.Host1xDeviceFile();
+ if (!host1x_file.fd_to_id.contains(fd)) {
+ host1x_file.fd_to_id[fd] = host1x_file.nvdec_next_id++;
}
return Submit(fd, input, output);
}
@@ -66,14 +67,19 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
return NvResult::NotImplemented;
}
-void nvhost_nvdec::OnOpen(DeviceFD fd) {}
+void nvhost_nvdec::OnOpen(DeviceFD fd) {
+ LOG_INFO(Service_NVDRV, "NVDEC video stream started");
+ system.AudioCore().SetNVDECActive(true);
+}
void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
- const auto iter = fd_to_id.find(fd);
- if (iter != fd_to_id.end()) {
+ auto& host1x_file = core.Host1xDeviceFile();
+ const auto iter = host1x_file.fd_to_id.find(fd);
+ if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
+ system.AudioCore().SetNVDECActive(false);
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index a507c4d0a..c1b4e53e8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -11,8 +10,7 @@ namespace Service::Nvidia::Devices {
class nvhost_nvdec final : public nvhost_nvdec_common {
public:
- explicit nvhost_nvdec(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
- SyncpointManager& syncpoint_manager_);
+ explicit nvhost_nvdec(Core::System& system_, NvCore::Container& core);
~nvhost_nvdec() override;
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -24,9 +22,6 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
-
-private:
- u32 next_id{};
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index 8a05f0668..99eede702 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -9,10 +8,12 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
+#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
-#include "core/hle/service/nvdrv/devices/nvmap.h"
-#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/memory.h"
+#include "video_core/host1x/host1x.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_base.h"
@@ -45,10 +46,22 @@ std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::s
}
} // Anonymous namespace
-nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
- SyncpointManager& syncpoint_manager_)
- : nvdevice{system_}, nvmap_dev{std::move(nvmap_dev_)}, syncpoint_manager{syncpoint_manager_} {}
-nvhost_nvdec_common::~nvhost_nvdec_common() = default;
+nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_,
+ NvCore::ChannelType channel_type_)
+ : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()},
+ nvmap{core.GetNvMapFile()}, channel_type{channel_type_} {
+ auto& syncpts_accumulated = core.Host1xDeviceFile().syncpts_accumulated;
+ if (syncpts_accumulated.empty()) {
+ channel_syncpoint = syncpoint_manager.AllocateSyncpoint(false);
+ } else {
+ channel_syncpoint = syncpts_accumulated.front();
+ syncpts_accumulated.pop_front();
+ }
+}
+
+nvhost_nvdec_common::~nvhost_nvdec_common() {
+ core.Host1xDeviceFile().syncpts_accumulated.push_back(channel_syncpoint);
+}
NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
IoctlSetNvmapFD params{};
@@ -85,16 +98,16 @@ NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
const SyncptIncr& syncpt_incr = syncpt_increments[i];
fence_thresholds[i] =
- syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments);
+ syncpoint_manager.IncrementSyncpointMaxExt(syncpt_incr.id, syncpt_incr.increments);
}
}
for (const auto& cmd_buffer : command_buffers) {
- const auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
+ const auto object = nvmap.GetHandle(cmd_buffer.memory_id);
ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
- system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
+ system.Memory().ReadBlock(object->address + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
- gpu.PushCommandBuffer(fd_to_id[fd], cmdlist);
+ gpu.PushCommandBuffer(core.Host1xDeviceFile().fd_to_id[fd], cmdlist);
}
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
// Some games expect command_buffers to be written back
@@ -113,10 +126,8 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
- if (device_syncpoints[params.param] == 0 && system.GPU().UseNvdec()) {
- device_syncpoints[params.param] = syncpoint_manager.AllocateSyncpoint();
- }
- params.value = device_syncpoints[params.param];
+ // const u32 id{NvCore::SyncpointManager::channel_syncpoints[static_cast<u32>(channel_type)]};
+ params.value = channel_syncpoint;
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return NvResult::Success;
@@ -124,6 +135,7 @@ NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::ve
NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
+ LOG_CRITICAL(Service_NVDRV, "called WAITBASE");
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
@@ -137,28 +149,8 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
- auto& gpu = system.GPU();
-
for (auto& cmd_buffer : cmd_buffer_handles) {
- auto object{nvmap_dev->GetObject(cmd_buffer.map_handle)};
- if (!object) {
- LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmd_buffer.map_handle);
- std::memcpy(output.data(), &params, output.size());
- return NvResult::InvalidState;
- }
- if (object->dma_map_addr == 0) {
- // NVDEC and VIC memory is in the 32-bit address space
- // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
- const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
- object->dma_map_addr = static_cast<u32>(low_addr);
- // Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
- ASSERT(object->dma_map_addr == low_addr);
- }
- if (!object->dma_map_addr) {
- LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
- } else {
- cmd_buffer.map_address = object->dma_map_addr;
- }
+ cmd_buffer.map_address = nvmap.PinHandle(cmd_buffer.map_handle);
}
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
@@ -168,11 +160,16 @@ NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vecto
}
NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
- // This is intntionally stubbed.
- // Skip unmapping buffers here, as to not break the continuity of the VP9 reference frame
- // addresses, and risk invalidating data before the async GPU thread is done with it
+ IoctlMapBuffer params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
+ std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
+
+ SliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
+ for (auto& cmd_buffer : cmd_buffer_handles) {
+ nvmap.UnpinHandle(cmd_buffer.map_handle);
+ }
+
std::memset(output.data(), 0, output.size());
- LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
return NvResult::Success;
}
@@ -183,4 +180,9 @@ NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
return NvResult::Success;
}
+Kernel::KEvent* nvhost_nvdec_common::QueryEvent(u32 event_id) {
+ LOG_CRITICAL(Service_NVDRV, "Unknown HOSTX1 Event {}", event_id);
+ return nullptr;
+}
+
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index e28c54df6..fe76100c8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -1,24 +1,28 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <deque>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/hle/service/nvdrv/core/syncpoint_manager.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
namespace Service::Nvidia {
-class SyncpointManager;
+
+namespace NvCore {
+class Container;
+class NvMap;
+} // namespace NvCore
namespace Devices {
-class nvmap;
class nvhost_nvdec_common : public nvdevice {
public:
- explicit nvhost_nvdec_common(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
- SyncpointManager& syncpoint_manager_);
+ explicit nvhost_nvdec_common(Core::System& system_, NvCore::Container& core,
+ NvCore::ChannelType channel_type);
~nvhost_nvdec_common() override;
protected:
@@ -111,11 +115,15 @@ protected:
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
- std::unordered_map<DeviceFD, u32> fd_to_id{};
+ Kernel::KEvent* QueryEvent(u32 event_id) override;
+
+ u32 channel_syncpoint;
s32_le nvmap_fd{};
u32_le submit_timeout{};
- std::shared_ptr<nvmap> nvmap_dev;
- SyncpointManager& syncpoint_manager;
+ NvCore::Container& core;
+ NvCore::SyncpointManager& syncpoint_manager;
+ NvCore::NvMap& nvmap;
+ NvCore::ChannelType channel_type;
std::array<u32, MaxSyncPoints> device_syncpoints{};
};
}; // namespace Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index c2be3cea7..bdbc2f9e1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 6045e5cbd..440e7d371 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 76b39806f..73f97136e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -1,17 +1,17 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
-nvhost_vic::nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
- SyncpointManager& syncpoint_manager_)
- : nvhost_nvdec_common{system_, std::move(nvmap_dev_), syncpoint_manager_} {}
+
+nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core_)
+ : nvhost_nvdec_common{system_, core_, NvCore::ChannelType::VIC} {}
nvhost_vic::~nvhost_vic() = default;
@@ -20,11 +20,13 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
switch (command.group) {
case 0x0:
switch (command.cmd) {
- case 0x1:
- if (!fd_to_id.contains(fd)) {
- fd_to_id[fd] = next_id++;
+ case 0x1: {
+ auto& host1x_file = core.Host1xDeviceFile();
+ if (!host1x_file.fd_to_id.contains(fd)) {
+ host1x_file.fd_to_id[fd] = host1x_file.vic_next_id++;
}
return Submit(fd, input, output);
+ }
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -68,8 +70,9 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
void nvhost_vic::OnOpen(DeviceFD fd) {}
void nvhost_vic::OnClose(DeviceFD fd) {
- const auto iter = fd_to_id.find(fd);
- if (iter != fd_to_id.end()) {
+ auto& host1x_file = core.Host1xDeviceFile();
+ const auto iter = host1x_file.fd_to_id.find(fd);
+ if (iter != host1x_file.fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index c9732c037..f164caafb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,8 +9,7 @@ namespace Service::Nvidia::Devices {
class nvhost_vic final : public nvhost_nvdec_common {
public:
- explicit nvhost_vic(Core::System& system_, std::shared_ptr<nvmap> nvmap_dev_,
- SyncpointManager& syncpoint_manager_);
+ explicit nvhost_vic(Core::System& system_, NvCore::Container& core);
~nvhost_vic();
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -23,8 +21,5 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
-
-private:
- u32 next_id{};
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index dc59b4494..ddf273b5e 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -1,21 +1,27 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
+#include <bit>
#include <cstring>
+#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/memory.h"
+
+using Core::Memory::YUZU_PAGESIZE;
namespace Service::Nvidia::Devices {
-nvmap::nvmap(Core::System& system_) : nvdevice{system_} {
- // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
- // represent this.
- CreateObject(0);
-}
+nvmap::nvmap(Core::System& system_, NvCore::Container& container_)
+ : nvdevice{system_}, container{container_}, file{container.GetNvMapFile()} {}
nvmap::~nvmap() = default;
@@ -63,39 +69,21 @@ NvResult nvmap::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
void nvmap::OnOpen(DeviceFD fd) {}
void nvmap::OnClose(DeviceFD fd) {}
-VAddr nvmap::GetObjectAddress(u32 handle) const {
- auto object = GetObject(handle);
- ASSERT(object);
- ASSERT(object->status == Object::Status::Allocated);
- return object->addr;
-}
-
-u32 nvmap::CreateObject(u32 size) {
- // Create a new nvmap object and obtain a handle to it.
- auto object = std::make_shared<Object>();
- object->id = next_id++;
- object->size = size;
- object->status = Object::Status::Created;
- object->refcount = 1;
-
- const u32 handle = next_handle++;
-
- handles.insert_or_assign(handle, std::move(object));
-
- return handle;
-}
-
NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
- LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
-
- if (!params.size) {
- LOG_ERROR(Service_NVDRV, "Size is 0");
- return NvResult::BadValue;
+ LOG_DEBUG(Service_NVDRV, "called, size=0x{:08X}", params.size);
+
+ std::shared_ptr<NvCore::NvMap::Handle> handle_description{};
+ auto result =
+ file.CreateHandle(Common::AlignUp(params.size, YUZU_PAGESIZE), handle_description);
+ if (result != NvResult::Success) {
+ LOG_CRITICAL(Service_NVDRV, "Failed to create Object");
+ return result;
}
-
- params.handle = CreateObject(params.size);
+ handle_description->orig_size = params.size; // Orig size is the unaligned size
+ params.handle = handle_description->id;
+ LOG_DEBUG(Service_NVDRV, "handle: {}, size: 0x{:X}", handle_description->id, params.size);
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
@@ -104,63 +92,68 @@ NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output)
NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
IocAllocParams params;
std::memcpy(&params, input.data(), sizeof(params));
- LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
+ LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.address);
if (!params.handle) {
- LOG_ERROR(Service_NVDRV, "Handle is 0");
+ LOG_CRITICAL(Service_NVDRV, "Handle is 0");
return NvResult::BadValue;
}
if ((params.align - 1) & params.align) {
- LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
+ LOG_CRITICAL(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
return NvResult::BadValue;
}
- const u32 min_alignment = 0x1000;
- if (params.align < min_alignment) {
- params.align = min_alignment;
+ // Force page size alignment at a minimum
+ if (params.align < YUZU_PAGESIZE) {
+ params.align = YUZU_PAGESIZE;
}
- auto object = GetObject(params.handle);
- if (!object) {
- LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
+ auto handle_description{file.GetHandle(params.handle)};
+ if (!handle_description) {
+ LOG_CRITICAL(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return NvResult::BadValue;
}
- if (object->status == Object::Status::Allocated) {
- LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
+ if (handle_description->allocated) {
+ LOG_CRITICAL(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
return NvResult::InsufficientMemory;
}
- object->flags = params.flags;
- object->align = params.align;
- object->kind = params.kind;
- object->addr = params.addr;
- object->status = Object::Status::Allocated;
-
+ const auto result =
+ handle_description->Alloc(params.flags, params.align, params.kind, params.address);
+ if (result != NvResult::Success) {
+ LOG_CRITICAL(Service_NVDRV, "Object failed to allocate, handle={:08X}", params.handle);
+ return result;
+ }
+ ASSERT(system.CurrentProcess()
+ ->PageTable()
+ .LockForDeviceAddressSpace(handle_description->address, handle_description->size)
+ .IsSuccess());
std::memcpy(output.data(), &params, sizeof(params));
- return NvResult::Success;
+ return result;
}
NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
- LOG_WARNING(Service_NVDRV, "called");
+ LOG_DEBUG(Service_NVDRV, "called");
+ // See the comment in FromId for extra info on this function
if (!params.handle) {
- LOG_ERROR(Service_NVDRV, "Handle is zero");
+ LOG_CRITICAL(Service_NVDRV, "Error!");
return NvResult::BadValue;
}
- auto object = GetObject(params.handle);
- if (!object) {
- LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return NvResult::BadValue;
+ auto handle_description{file.GetHandle(params.handle)};
+ if (!handle_description) {
+ LOG_CRITICAL(Service_NVDRV, "Error!");
+ return NvResult::AccessDenied; // This will always return EPERM irrespective of if the
+ // handle exists or not
}
- params.id = object->id;
-
+ params.id = handle_description->id;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
}
@@ -169,26 +162,29 @@ NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output)
IocFromIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
- LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ LOG_DEBUG(Service_NVDRV, "called, id:{}", params.id);
- auto itr = std::find_if(handles.begin(), handles.end(),
- [&](const auto& entry) { return entry.second->id == params.id; });
- if (itr == handles.end()) {
- LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
+ // Handles and IDs are always the same value in nvmap however IDs can be used globally given the
+ // right permissions.
+ // Since we don't plan on ever supporting multiprocess we can skip implementing handle refs and
+ // so this function just does simple validation and passes through the handle id.
+ if (!params.id) {
+ LOG_CRITICAL(Service_NVDRV, "Zero Id is invalid!");
return NvResult::BadValue;
}
- auto& object = itr->second;
- if (object->status != Object::Status::Allocated) {
- LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
+ auto handle_description{file.GetHandle(params.id)};
+ if (!handle_description) {
+ LOG_CRITICAL(Service_NVDRV, "Unregistered handle!");
return NvResult::BadValue;
}
- itr->second->refcount++;
-
- // Return the existing handle instead of creating a new one.
- params.handle = itr->first;
-
+ auto result = handle_description->Duplicate(false);
+ if (result != NvResult::Success) {
+ LOG_CRITICAL(Service_NVDRV, "Could not duplicate handle!");
+ return result;
+ }
+ params.handle = handle_description->id;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
}
@@ -199,35 +195,43 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
IocParamParams params;
std::memcpy(&params, input.data(), sizeof(params));
- LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
+ LOG_DEBUG(Service_NVDRV, "called type={}", params.param);
- auto object = GetObject(params.handle);
- if (!object) {
- LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
+ if (!params.handle) {
+ LOG_CRITICAL(Service_NVDRV, "Invalid handle!");
return NvResult::BadValue;
}
- if (object->status != Object::Status::Allocated) {
- LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
+ auto handle_description{file.GetHandle(params.handle)};
+ if (!handle_description) {
+ LOG_CRITICAL(Service_NVDRV, "Not registered handle!");
return NvResult::BadValue;
}
- switch (static_cast<ParamTypes>(params.param)) {
- case ParamTypes::Size:
- params.result = object->size;
+ switch (params.param) {
+ case HandleParameterType::Size:
+ params.result = static_cast<u32_le>(handle_description->orig_size);
+ break;
+ case HandleParameterType::Alignment:
+ params.result = static_cast<u32_le>(handle_description->align);
break;
- case ParamTypes::Alignment:
- params.result = object->align;
+ case HandleParameterType::Base:
+ params.result = static_cast<u32_le>(-22); // posix EINVAL
break;
- case ParamTypes::Heap:
- // TODO(Subv): Seems to be a hardcoded value?
- params.result = 0x40000000;
+ case HandleParameterType::Heap:
+ if (handle_description->allocated)
+ params.result = 0x40000000;
+ else
+ params.result = 0;
break;
- case ParamTypes::Kind:
- params.result = object->kind;
+ case HandleParameterType::Kind:
+ params.result = handle_description->kind;
+ break;
+ case HandleParameterType::IsSharedMemMapped:
+ params.result = handle_description->is_shared_mem_mapped;
break;
default:
- UNIMPLEMENTED();
+ return NvResult::BadValue;
}
std::memcpy(output.data(), &params, sizeof(params));
@@ -235,46 +239,29 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
}
NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
- // TODO(Subv): These flags are unconfirmed.
- enum FreeFlags {
- Freed = 0,
- NotFreedYet = 1,
- };
-
IocFreeParams params;
std::memcpy(&params, input.data(), sizeof(params));
- LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ LOG_DEBUG(Service_NVDRV, "called");
- auto itr = handles.find(params.handle);
- if (itr == handles.end()) {
- LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return NvResult::BadValue;
- }
- if (!itr->second->refcount) {
- LOG_ERROR(
- Service_NVDRV,
- "There is no references to this object. The object is already freed. handle={:08X}",
- params.handle);
- return NvResult::BadValue;
+ if (!params.handle) {
+ LOG_CRITICAL(Service_NVDRV, "Handle null freed?");
+ return NvResult::Success;
}
- itr->second->refcount--;
-
- params.size = itr->second->size;
-
- if (itr->second->refcount == 0) {
- params.flags = Freed;
- // The address of the nvmap is written to the output if we're finally freeing it, otherwise
- // 0 is written.
- params.address = itr->second->addr;
+ if (auto freeInfo{file.FreeHandle(params.handle, false)}) {
+ ASSERT(system.CurrentProcess()
+ ->PageTable()
+ .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size)
+ .IsSuccess());
+ params.address = freeInfo->address;
+ params.size = static_cast<u32>(freeInfo->size);
+ params.flags.raw = 0;
+ params.flags.map_uncached.Assign(freeInfo->was_uncached);
} else {
- params.flags = NotFreedYet;
- params.address = 0;
+ // This is possible when there's internel dups or other duplicates.
}
- handles.erase(params.handle);
-
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index d90b69e5a..e9bfd0358 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,15 +9,23 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
+namespace Service::Nvidia::NvCore {
+class Container;
+} // namespace Service::Nvidia::NvCore
+
namespace Service::Nvidia::Devices {
class nvmap final : public nvdevice {
public:
- explicit nvmap(Core::System& system_);
+ explicit nvmap(Core::System& system_, NvCore::Container& container);
~nvmap() override;
+ nvmap(const nvmap&) = delete;
+ nvmap& operator=(const nvmap&) = delete;
+
NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
std::vector<u8>& output) override;
NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
@@ -29,31 +36,15 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
- /// Returns the allocated address of an nvmap object given its handle.
- VAddr GetObjectAddress(u32 handle) const;
-
- /// Represents an nvmap object.
- struct Object {
- enum class Status { Created, Allocated };
- u32 id;
- u32 size;
- u32 flags;
- u32 align;
- u8 kind;
- VAddr addr;
- Status status;
- u32 refcount;
- u32 dma_map_addr;
+ enum class HandleParameterType : u32_le {
+ Size = 1,
+ Alignment = 2,
+ Base = 3,
+ Heap = 4,
+ Kind = 5,
+ IsSharedMemMapped = 6
};
- std::shared_ptr<Object> GetObject(u32 handle) const {
- auto itr = handles.find(handle);
- if (itr != handles.end()) {
- return itr->second;
- }
- return {};
- }
-
private:
/// Id to use for the next handle that is created.
u32 next_handle = 0;
@@ -61,9 +52,6 @@ private:
/// Id to use for the next object that is created.
u32 next_id = 0;
- /// Mapping of currently allocated handles to the objects they represent.
- std::unordered_map<u32, std::shared_ptr<Object>> handles;
-
struct IocCreateParams {
// Input
u32_le size{};
@@ -84,11 +72,11 @@ private:
// Input
u32_le handle{};
u32_le heap_mask{};
- u32_le flags{};
+ NvCore::NvMap::Handle::Flags flags{};
u32_le align{};
u8 kind{};
INSERT_PADDING_BYTES(7);
- u64_le addr{};
+ u64_le address{};
};
static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
@@ -97,14 +85,14 @@ private:
INSERT_PADDING_BYTES(4);
u64_le address{};
u32_le size{};
- u32_le flags{};
+ NvCore::NvMap::Handle::Flags flags{};
};
static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
struct IocParamParams {
// Input
u32_le handle{};
- u32_le param{};
+ HandleParameterType param{};
// Output
u32_le result{};
};
@@ -118,14 +106,15 @@ private:
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
- u32 CreateObject(u32 size);
-
NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
+
+ NvCore::Container& container;
+ NvCore::NvMap& file;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index 5ab221fc1..0e2f47075 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -1,6 +1,6 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -16,17 +16,11 @@ using DeviceFD = s32;
constexpr DeviceFD INVALID_NVDRV_FD = -1;
-struct Fence {
+struct NvFence {
s32 id;
u32 value;
};
-
-static_assert(sizeof(Fence) == 8, "Fence has wrong size");
-
-struct MultiFence {
- u32 num_fences;
- std::array<Fence, 4> fences;
-};
+static_assert(sizeof(NvFence) == 8, "NvFence has wrong size");
enum class NvResult : u32 {
Success = 0x0,
@@ -85,11 +79,15 @@ enum class NvResult : u32 {
ModuleNotPresent = 0xA000E,
};
+// obtained from
+// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47
enum class EventState {
- Free = 0,
- Registered = 1,
- Waiting = 2,
- Busy = 3,
+ Available = 0,
+ Waiting = 1,
+ Cancelling = 2,
+ Signalling = 3,
+ Signalled = 4,
+ Cancelled = 5,
};
union Ioctl {
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index aa7e47cbf..5e7b7468f 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -1,6 +1,6 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
#include <utility>
@@ -9,6 +9,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_writable_event.h"
+#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
@@ -16,17 +17,31 @@
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
+#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvjpg.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvdrv_interface.h"
#include "core/hle/service/nvdrv/nvmemp.h"
-#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/hle/service/nvflinger/nvflinger.h"
+#include "video_core/gpu.h"
namespace Service::Nvidia {
+EventInterface::EventInterface(Module& module_) : module{module_}, guard{}, on_signal{} {}
+
+EventInterface::~EventInterface() = default;
+
+Kernel::KEvent* EventInterface::CreateEvent(std::string name) {
+ Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name));
+ return new_event;
+}
+
+void EventInterface::FreeEvent(Kernel::KEvent* event) {
+ module.service_context.CloseEvent(event);
+}
+
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
Core::System& system) {
auto module_ = std::make_shared<Module>(system);
@@ -39,34 +54,54 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
}
Module::Module(Core::System& system)
- : syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} {
- for (u32 i = 0; i < MaxNvEvents; i++) {
- events_interface.events[i].event =
- service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i));
- events_interface.status[i] = EventState::Free;
- events_interface.registered[i] = false;
- }
- auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
- devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
- devices["/dev/nvhost-gpu"] =
- std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager);
- devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
- devices["/dev/nvmap"] = nvmap_dev;
- devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
- devices["/dev/nvhost-ctrl"] =
- std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager);
- devices["/dev/nvhost-nvdec"] =
- std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev, syncpoint_manager);
- devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
- devices["/dev/nvhost-vic"] =
- std::make_shared<Devices::nvhost_vic>(system, nvmap_dev, syncpoint_manager);
+ : service_context{system, "nvdrv"}, events_interface{*this}, container{system.Host1x()} {
+ builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device =
+ std::make_shared<Devices::nvhost_as_gpu>(system, *this, container);
+ return open_files.emplace(fd, device).first;
+ };
+ builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device =
+ std::make_shared<Devices::nvhost_gpu>(system, events_interface, container);
+ return open_files.emplace(fd, device).first;
+ };
+ builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device =
+ std::make_shared<Devices::nvhost_ctrl_gpu>(system, events_interface);
+ return open_files.emplace(fd, device).first;
+ };
+ builders["/dev/nvmap"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device =
+ std::make_shared<Devices::nvmap>(system, container);
+ return open_files.emplace(fd, device).first;
+ };
+ builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device =
+ std::make_shared<Devices::nvdisp_disp0>(system, container);
+ return open_files.emplace(fd, device).first;
+ };
+ builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device =
+ std::make_shared<Devices::nvhost_ctrl>(system, events_interface, container);
+ return open_files.emplace(fd, device).first;
+ };
+ builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device =
+ std::make_shared<Devices::nvhost_nvdec>(system, container);
+ return open_files.emplace(fd, device).first;
+ };
+ builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device = std::make_shared<Devices::nvhost_nvjpg>(system);
+ return open_files.emplace(fd, device).first;
+ };
+ builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) {
+ std::shared_ptr<Devices::nvdevice> device =
+ std::make_shared<Devices::nvhost_vic>(system, container);
+ return open_files.emplace(fd, device).first;
+ };
}
-Module::~Module() {
- for (u32 i = 0; i < MaxNvEvents; i++) {
- service_context.CloseEvent(events_interface.events[i].event);
- }
-}
+Module::~Module() {}
NvResult Module::VerifyFD(DeviceFD fd) const {
if (fd < 0) {
@@ -83,18 +118,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const {
}
DeviceFD Module::Open(const std::string& device_name) {
- if (devices.find(device_name) == devices.end()) {
+ auto it = builders.find(device_name);
+ if (it == builders.end()) {
LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
return INVALID_NVDRV_FD;
}
- auto device = devices[device_name];
const DeviceFD fd = next_fd++;
+ auto& builder = it->second;
+ auto device = builder(fd)->second;
device->OnOpen(fd);
- open_files[fd] = std::move(device);
-
return fd;
}
@@ -169,22 +204,24 @@ NvResult Module::Close(DeviceFD fd) {
return NvResult::Success;
}
-void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
- for (u32 i = 0; i < MaxNvEvents; i++) {
- if (events_interface.assigned_syncpt[i] == syncpoint_id &&
- events_interface.assigned_value[i] == value) {
- events_interface.LiberateEvent(i);
- events_interface.events[i].event->GetWritableEvent().Signal();
- }
+NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
}
-}
-Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) {
- return events_interface.events[event_id].event->GetReadableEvent();
-}
+ const auto itr = open_files.find(fd);
-Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) {
- return events_interface.events[event_id].event->GetWritableEvent();
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ event = itr->second->QueryEvent(event_id);
+ if (!event) {
+ return NvResult::BadParameter;
+ }
+ return NvResult::Success;
}
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index a5af5b785..146d046a9 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -1,17 +1,21 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
+#include <functional>
+#include <list>
#include <memory>
+#include <string>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h"
-#include "core/hle/service/nvdrv/syncpoint_manager.h"
+#include "core/hle/service/nvflinger/ui/fence.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -28,81 +32,31 @@ class NVFlinger;
namespace Service::Nvidia {
+namespace NvCore {
+class Container;
class SyncpointManager;
+} // namespace NvCore
namespace Devices {
class nvdevice;
-}
+class nvhost_ctrl;
+} // namespace Devices
-/// Represents an Nvidia event
-struct NvEvent {
- Kernel::KEvent* event{};
- Fence fence{};
-};
+class Module;
-struct EventInterface {
- // Mask representing currently busy events
- u64 events_mask{};
- // Each kernel event associated to an NV event
- std::array<NvEvent, MaxNvEvents> events;
- // The status of the current NVEvent
- std::array<EventState, MaxNvEvents> status{};
- // Tells if an NVEvent is registered or not
- std::array<bool, MaxNvEvents> registered{};
- // Tells the NVEvent that it has failed.
- std::array<bool, MaxNvEvents> failed{};
- // When an NVEvent is waiting on GPU interrupt, this is the sync_point
- // associated with it.
- std::array<u32, MaxNvEvents> assigned_syncpt{};
- // This is the value of the GPU interrupt for which the NVEvent is waiting
- // for.
- std::array<u32, MaxNvEvents> assigned_value{};
- // Constant to denote an unasigned syncpoint.
- static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
- std::optional<u32> GetFreeEvent() const {
- u64 mask = events_mask;
- for (u32 i = 0; i < MaxNvEvents; i++) {
- const bool is_free = (mask & 0x1) == 0;
- if (is_free) {
- if (status[i] == EventState::Registered || status[i] == EventState::Free) {
- return {i};
- }
- }
- mask = mask >> 1;
- }
- return std::nullopt;
- }
- void SetEventStatus(const u32 event_id, EventState new_status) {
- EventState old_status = status[event_id];
- if (old_status == new_status) {
- return;
- }
- status[event_id] = new_status;
- if (new_status == EventState::Registered) {
- registered[event_id] = true;
- }
- if (new_status == EventState::Waiting || new_status == EventState::Busy) {
- events_mask |= (1ULL << event_id);
- }
- }
- void RegisterEvent(const u32 event_id) {
- registered[event_id] = true;
- if (status[event_id] == EventState::Free) {
- status[event_id] = EventState::Registered;
- }
- }
- void UnregisterEvent(const u32 event_id) {
- registered[event_id] = false;
- if (status[event_id] == EventState::Registered) {
- status[event_id] = EventState::Free;
- }
- }
- void LiberateEvent(const u32 event_id) {
- status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
- events_mask &= ~(1ULL << event_id);
- assigned_syncpt[event_id] = unassigned_syncpt;
- assigned_value[event_id] = 0;
- }
+class EventInterface {
+public:
+ explicit EventInterface(Module& module_);
+ ~EventInterface();
+
+ Kernel::KEvent* CreateEvent(std::string name);
+
+ void FreeEvent(Kernel::KEvent* event);
+
+private:
+ Module& module;
+ std::mutex guard;
+ std::list<Devices::nvhost_ctrl*> on_signal;
};
class Module final {
@@ -112,9 +66,9 @@ public:
/// Returns a pointer to one of the available devices, identified by its name.
template <typename T>
- std::shared_ptr<T> GetDevice(const std::string& name) {
- auto itr = devices.find(name);
- if (itr == devices.end())
+ std::shared_ptr<T> GetDevice(DeviceFD fd) {
+ auto itr = open_files.find(fd);
+ if (itr == open_files.end())
return nullptr;
return std::static_pointer_cast<T>(itr->second);
}
@@ -137,28 +91,27 @@ public:
/// Closes a device file descriptor and returns operation success.
NvResult Close(DeviceFD fd);
- void SignalSyncpt(const u32 syncpoint_id, const u32 value);
-
- Kernel::KReadableEvent& GetEvent(u32 event_id);
-
- Kernel::KWritableEvent& GetEventWriteable(u32 event_id);
+ NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event);
private:
- /// Manages syncpoints on the host
- SyncpointManager syncpoint_manager;
+ friend class EventInterface;
+ friend class Service::NVFlinger::NVFlinger;
/// Id to use for the next open file descriptor.
DeviceFD next_fd = 1;
+ using FilesContainerType = std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>>;
/// Mapping of file descriptors to the devices they reference.
- std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files;
+ FilesContainerType open_files;
- /// Mapping of device node names to their implementation.
- std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
+ KernelHelpers::ServiceContext service_context;
EventInterface events_interface;
- KernelHelpers::ServiceContext service_context;
+ /// Manages syncpoints on the host
+ NvCore::Container container;
+
+ std::unordered_map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
};
/// Registers all NVDRV services with the specified service manager.
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.cpp b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
index c16babe14..edbdfee43 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.cpp
@@ -1,11 +1,12 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
+// SPDX-License-Identifier: GPL-3.0-or-later
#include <cinttypes>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/nvdrv.h"
@@ -13,10 +14,6 @@
namespace Service::Nvidia {
-void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) {
- nvdrv->SignalSyncpt(syncpoint_id, value);
-}
-
void NVDRV::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
IPC::ResponseBuilder rb{ctx, 4};
@@ -26,7 +23,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
rb.Push<DeviceFD>(0);
rb.PushEnum(NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -61,7 +58,7 @@ void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -87,7 +84,7 @@ void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -114,7 +111,7 @@ void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -139,7 +136,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
@@ -165,33 +162,28 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto fd = rp.Pop<DeviceFD>();
- const auto event_id = rp.Pop<u32>() & 0x00FF;
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
+ const auto event_id = rp.Pop<u32>();
if (!is_initialized) {
ServiceError(ctx, NvResult::NotInitialized);
- LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ LOG_ERROR(Service_NVDRV, "NvServices is not initialized!");
return;
}
- const auto nv_result = nvdrv->VerifyFD(fd);
- if (nv_result != NvResult::Success) {
- LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
- ServiceError(ctx, nv_result);
- return;
- }
+ Kernel::KEvent* event = nullptr;
+ NvResult result = nvdrv->QueryEvent(fd, event_id, event);
- if (event_id < MaxNvEvents) {
+ if (result == NvResult::Success) {
IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(ResultSuccess);
- auto& event = nvdrv->GetEvent(event_id);
- event.Clear();
- rb.PushCopyObjects(event);
+ auto& readable_event = event->GetReadableEvent();
+ rb.PushCopyObjects(readable_event);
rb.PushEnum(NvResult::Success);
} else {
+ LOG_ERROR(Service_NVDRV, "Invalid event request!");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
- rb.PushEnum(NvResult::BadParameter);
+ rb.PushEnum(result);
}
}
@@ -230,7 +222,7 @@ void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
}
NVDRV::NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name)
- : ServiceFramework{system_, name}, nvdrv{std::move(nvdrv_)} {
+ : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, nvdrv{std::move(nvdrv_)} {
static const FunctionInfo functions[] = {
{0, &NVDRV::Open, "Open"},
{1, &NVDRV::Ioctl1, "Ioctl"},
diff --git a/src/core/hle/service/nvdrv/nvdrv_interface.h b/src/core/hle/service/nvdrv/nvdrv_interface.h
index 0e764c53f..cd58a4f35 100644
--- a/src/core/hle/service/nvdrv/nvdrv_interface.h
+++ b/src/core/hle/service/nvdrv/nvdrv_interface.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -19,8 +18,6 @@ public:
explicit NVDRV(Core::System& system_, std::shared_ptr<Module> nvdrv_, const char* name);
~NVDRV() override;
- void SignalGPUInterruptSyncpt(u32 syncpoint_id, u32 value);
-
private:
void Open(Kernel::HLERequestContext& ctx);
void Ioctl1(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp
index 331c02243..e433580b1 100644
--- a/src/core/hle/service/nvdrv/nvmemp.cpp
+++ b/src/core/hle/service/nvdrv/nvmemp.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
diff --git a/src/core/hle/service/nvdrv/nvmemp.h b/src/core/hle/service/nvdrv/nvmemp.h
index 724c27ef9..3d4276327 100644
--- a/src/core/hle/service/nvdrv/nvmemp.h
+++ b/src/core/hle/service/nvdrv/nvmemp.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
deleted file mode 100644
index 3b6f55526..000000000
--- a/src/core/hle/service/nvdrv/syncpoint_manager.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include "common/assert.h"
-#include "core/hle/service/nvdrv/syncpoint_manager.h"
-#include "video_core/gpu.h"
-
-namespace Service::Nvidia {
-
-SyncpointManager::SyncpointManager(Tegra::GPU& gpu_) : gpu{gpu_} {}
-
-SyncpointManager::~SyncpointManager() = default;
-
-u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) {
- syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id);
- return GetSyncpointMin(syncpoint_id);
-}
-
-u32 SyncpointManager::AllocateSyncpoint() {
- for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) {
- if (!syncpoints[syncpoint_id].is_allocated) {
- syncpoints[syncpoint_id].is_allocated = true;
- return syncpoint_id;
- }
- }
- UNREACHABLE_MSG("No more available syncpoints!");
- return {};
-}
-
-u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) {
- for (u32 index = 0; index < value; ++index) {
- syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed);
- }
-
- return GetSyncpointMax(syncpoint_id);
-}
-
-} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h
deleted file mode 100644
index 99f286474..000000000
--- a/src/core/hle/service/nvdrv/syncpoint_manager.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <array>
-#include <atomic>
-
-#include "common/common_types.h"
-#include "core/hle/service/nvdrv/nvdata.h"
-
-namespace Tegra {
-class GPU;
-}
-
-namespace Service::Nvidia {
-
-class SyncpointManager final {
-public:
- explicit SyncpointManager(Tegra::GPU& gpu_);
- ~SyncpointManager();
-
- /**
- * Returns true if the specified syncpoint is expired for the given value.
- * @param syncpoint_id Syncpoint ID to check.
- * @param value Value to check against the specified syncpoint.
- * @returns True if the specified syncpoint is expired for the given value, otherwise False.
- */
- bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const {
- return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value);
- }
-
- /**
- * Gets the lower bound for the specified syncpoint.
- * @param syncpoint_id Syncpoint ID to get the lower bound for.
- * @returns The lower bound for the specified syncpoint.
- */
- u32 GetSyncpointMin(u32 syncpoint_id) const {
- return syncpoints.at(syncpoint_id).min.load(std::memory_order_relaxed);
- }
-
- /**
- * Gets the uper bound for the specified syncpoint.
- * @param syncpoint_id Syncpoint ID to get the upper bound for.
- * @returns The upper bound for the specified syncpoint.
- */
- u32 GetSyncpointMax(u32 syncpoint_id) const {
- return syncpoints.at(syncpoint_id).max.load(std::memory_order_relaxed);
- }
-
- /**
- * Refreshes the minimum value for the specified syncpoint.
- * @param syncpoint_id Syncpoint ID to be refreshed.
- * @returns The new syncpoint minimum value.
- */
- u32 RefreshSyncpoint(u32 syncpoint_id);
-
- /**
- * Allocates a new syncoint.
- * @returns The syncpoint ID for the newly allocated syncpoint.
- */
- u32 AllocateSyncpoint();
-
- /**
- * Increases the maximum value for the specified syncpoint.
- * @param syncpoint_id Syncpoint ID to be increased.
- * @param value Value to increase the specified syncpoint by.
- * @returns The new syncpoint maximum value.
- */
- u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value);
-
-private:
- struct Syncpoint {
- std::atomic<u32> min;
- std::atomic<u32> max;
- std::atomic<bool> is_allocated;
- };
-
- std::array<Syncpoint, MaxSyncPoints> syncpoints{};
-
- Tegra::GPU& gpu;
-};
-
-} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/binder.h b/src/core/hle/service/nvflinger/binder.h
new file mode 100644
index 000000000..157333ff8
--- /dev/null
+++ b/src/core/hle/service/nvflinger/binder.h
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/binder/IBinder.h
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Kernel {
+class HLERequestContext;
+class KReadableEvent;
+} // namespace Kernel
+
+namespace Service::android {
+
+enum class TransactionId {
+ RequestBuffer = 1,
+ SetBufferCount = 2,
+ DequeueBuffer = 3,
+ DetachBuffer = 4,
+ DetachNextBuffer = 5,
+ AttachBuffer = 6,
+ QueueBuffer = 7,
+ CancelBuffer = 8,
+ Query = 9,
+ Connect = 10,
+ Disconnect = 11,
+ AllocateBuffers = 13,
+ SetPreallocatedBuffer = 14,
+ GetBufferHistory = 17,
+};
+
+class IBinder {
+public:
+ virtual ~IBinder() = default;
+ virtual void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code,
+ u32 flags) = 0;
+ virtual Kernel::KReadableEvent& GetNativeHandle() = 0;
+};
+
+} // 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..f73dec4f1
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item.h
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItem.h
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+#include "common/math_util.h"
+#include "core/hle/service/nvflinger/ui/fence.h"
+#include "core/hle/service/nvflinger/window.h"
+
+namespace Service::android {
+
+class GraphicBuffer;
+
+class BufferItem final {
+public:
+ constexpr BufferItem() = default;
+
+ std::shared_ptr<GraphicBuffer> graphic_buffer;
+ Fence fence;
+ Common::Rectangle<s32> crop;
+ NativeWindowTransform transform{};
+ u32 scaling_mode{};
+ s64 timestamp{};
+ bool is_auto_timestamp{};
+ u64 frame_number{};
+
+ // The default value for buf, used to indicate this doesn't correspond to a slot.
+ static constexpr s32 INVALID_BUFFER_SLOT = -1;
+ union {
+ s32 slot{INVALID_BUFFER_SLOT};
+ s32 buf;
+ };
+
+ bool is_droppable{};
+ bool acquire_called{};
+ bool transform_to_display_inverse{};
+ s32 swap_interval{};
+};
+
+} // 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..6d2c92a2c
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.cpp
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferItemConsumer.cpp
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/hle/service/nvflinger/buffer_item.h"
+#include "core/hle/service/nvflinger/buffer_item_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
+
+namespace Service::android {
+
+BufferItemConsumer::BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer_)
+ : ConsumerBase{std::move(consumer_)} {}
+
+Status BufferItemConsumer::AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
+ bool wait_for_fence) {
+ if (!item) {
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{mutex};
+
+ if (const auto status = AcquireBufferLocked(item, present_when); status != Status::NoError) {
+ if (status != Status::NoBufferAvailable) {
+ LOG_ERROR(Service_NVFlinger, "Failed to acquire buffer: {}", status);
+ }
+ return status;
+ }
+
+ if (wait_for_fence) {
+ UNIMPLEMENTED();
+ }
+
+ item->graphic_buffer = slots[item->slot].graphic_buffer;
+
+ return Status::NoError;
+}
+
+Status BufferItemConsumer::ReleaseBuffer(const BufferItem& item, Fence& release_fence) {
+ std::scoped_lock lock{mutex};
+
+ if (const auto status = AddReleaseFenceLocked(item.buf, item.graphic_buffer, release_fence);
+ status != Status::NoError) {
+ LOG_ERROR(Service_NVFlinger, "Failed to add fence: {}", status);
+ }
+
+ if (const auto status = ReleaseBufferLocked(item.buf, item.graphic_buffer);
+ status != Status::NoError) {
+ LOG_WARNING(Service_NVFlinger, "Failed to release buffer: {}", status);
+ return status;
+ }
+
+ return Status::NoError;
+}
+
+} // 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..69046233d
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_item_consumer.h
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferItemConsumer.h
+
+#pragma once
+
+#include <chrono>
+#include <memory>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/consumer_base.h"
+#include "core/hle/service/nvflinger/status.h"
+
+namespace Service::android {
+
+class BufferItem;
+
+class BufferItemConsumer final : public ConsumerBase {
+public:
+ explicit BufferItemConsumer(std::unique_ptr<BufferQueueConsumer> consumer);
+ Status AcquireBuffer(BufferItem* item, std::chrono::nanoseconds present_when,
+ bool wait_for_fence = true);
+ Status ReleaseBuffer(const BufferItem& item, Fence& release_fence);
+};
+
+} // 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 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "core/core.h"
-#include "core/hle/kernel/k_writable_event.h"
-#include "core/hle/kernel/kernel.h"
-#include "core/hle/service/kernel_helpers.h"
-#include "core/hle/service/nvflinger/buffer_queue.h"
-
-namespace Service::NVFlinger {
-
-BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
- KernelHelpers::ServiceContext& service_context_)
- : id(id_), layer_id(layer_id_), service_context{service_context_} {
- buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
-}
-
-BufferQueue::~BufferQueue() {
- service_context.CloseEvent(buffer_wait_event);
-}
-
-void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
- ASSERT(slot < buffer_slots);
- LOG_WARNING(Service, "Adding graphics buffer {}", slot);
-
- {
- std::unique_lock lock{free_buffers_mutex};
- free_buffers.push_back(slot);
- }
- free_buffers_condition.notify_one();
-
- buffers[slot] = {
- .slot = slot,
- .status = Buffer::Status::Free,
- .igbp_buffer = igbp_buffer,
- .transform = {},
- .crop_rect = {},
- .swap_interval = 0,
- .multi_fence = {},
- };
-
- buffer_wait_event->GetWritableEvent().Signal();
-}
-
-std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width,
- u32 height) {
- // Wait for first request before trying to dequeue
- {
- std::unique_lock lock{free_buffers_mutex};
- free_buffers_condition.wait(lock, [this] { return !free_buffers.empty() || !is_connect; });
- }
-
- if (!is_connect) {
- // Buffer was disconnected while the thread was blocked, this is most likely due to
- // emulation being stopped
- return std::nullopt;
- }
-
- std::unique_lock lock{free_buffers_mutex};
-
- auto f_itr = free_buffers.begin();
- auto slot = buffers.size();
-
- while (f_itr != free_buffers.end()) {
- const Buffer& buffer = buffers[*f_itr];
- if (buffer.status == Buffer::Status::Free && buffer.igbp_buffer.width == width &&
- buffer.igbp_buffer.height == height) {
- slot = *f_itr;
- free_buffers.erase(f_itr);
- break;
- }
- ++f_itr;
- }
- if (slot == buffers.size()) {
- return std::nullopt;
- }
- buffers[slot].status = Buffer::Status::Dequeued;
- return {{buffers[slot].slot, &buffers[slot].multi_fence}};
-}
-
-const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
- ASSERT(buffers[slot].slot == slot);
-
- return buffers[slot].igbp_buffer;
-}
-
-void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect, u32 swap_interval,
- Service::Nvidia::MultiFence& multi_fence) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Dequeued);
- ASSERT(buffers[slot].slot == slot);
-
- buffers[slot].status = Buffer::Status::Queued;
- buffers[slot].transform = transform;
- buffers[slot].crop_rect = crop_rect;
- buffers[slot].swap_interval = swap_interval;
- buffers[slot].multi_fence = multi_fence;
- std::unique_lock lock{queue_sequence_mutex};
- queue_sequence.push_back(slot);
-}
-
-void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status != Buffer::Status::Free);
- ASSERT(buffers[slot].slot == slot);
-
- buffers[slot].status = Buffer::Status::Free;
- buffers[slot].multi_fence = multi_fence;
- buffers[slot].swap_interval = 0;
-
- {
- std::unique_lock lock{free_buffers_mutex};
- free_buffers.push_back(slot);
- }
- free_buffers_condition.notify_one();
-
- buffer_wait_event->GetWritableEvent().Signal();
-}
-
-std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
- std::unique_lock lock{queue_sequence_mutex};
- std::size_t buffer_slot = buffers.size();
- // Iterate to find a queued buffer matching the requested slot.
- while (buffer_slot == buffers.size() && !queue_sequence.empty()) {
- const auto slot = static_cast<std::size_t>(queue_sequence.front());
- ASSERT(slot < buffers.size());
- if (buffers[slot].status == Buffer::Status::Queued) {
- ASSERT(buffers[slot].slot == slot);
- buffer_slot = slot;
- }
- queue_sequence.pop_front();
- }
- if (buffer_slot == buffers.size()) {
- return std::nullopt;
- }
- buffers[buffer_slot].status = Buffer::Status::Acquired;
- return {{buffers[buffer_slot]}};
-}
-
-void BufferQueue::ReleaseBuffer(u32 slot) {
- ASSERT(slot < buffers.size());
- ASSERT(buffers[slot].status == Buffer::Status::Acquired);
- ASSERT(buffers[slot].slot == slot);
-
- buffers[slot].status = Buffer::Status::Free;
- {
- std::unique_lock lock{free_buffers_mutex};
- free_buffers.push_back(slot);
- }
- free_buffers_condition.notify_one();
-
- buffer_wait_event->GetWritableEvent().Signal();
-}
-
-void BufferQueue::Connect() {
- std::unique_lock lock{queue_sequence_mutex};
- queue_sequence.clear();
- is_connect = true;
-}
-
-void BufferQueue::Disconnect() {
- buffers.fill({});
- {
- std::unique_lock lock{queue_sequence_mutex};
- queue_sequence.clear();
- }
- buffer_wait_event->GetWritableEvent().Signal();
- is_connect = false;
- free_buffers_condition.notify_one();
-}
-
-u32 BufferQueue::Query(QueryType type) {
- LOG_WARNING(Service, "(STUBBED) called type={}", type);
-
- switch (type) {
- case QueryType::NativeWindowFormat:
- return static_cast<u32>(PixelFormat::RGBA8888);
- case QueryType::NativeWindowWidth:
- case QueryType::NativeWindowHeight:
- break;
- case QueryType::NativeWindowMinUndequeuedBuffers:
- return 0;
- case QueryType::NativeWindowConsumerUsageBits:
- return 0;
- }
- UNIMPLEMENTED_MSG("Unimplemented query type={}", type);
- return 0;
-}
-
-Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
- return buffer_wait_event->GetWritableEvent();
-}
-
-Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
- return buffer_wait_event->GetReadableEvent();
-}
-
-} // 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 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <condition_variable>
-#include <list>
-#include <mutex>
-#include <optional>
-
-#include "common/common_funcs.h"
-#include "common/math_util.h"
-#include "common/swap.h"
-#include "core/hle/kernel/k_event.h"
-#include "core/hle/kernel/k_readable_event.h"
-#include "core/hle/service/nvdrv/nvdata.h"
-
-namespace Kernel {
-class KernelCore;
-class KEvent;
-class KReadableEvent;
-class KWritableEvent;
-} // namespace Kernel
-
-namespace Service::KernelHelpers {
-class ServiceContext;
-} // namespace Service::KernelHelpers
-
-namespace Service::NVFlinger {
-
-constexpr u32 buffer_slots = 0x40;
-struct IGBPBuffer {
- u32_le magic;
- u32_le width;
- u32_le height;
- u32_le stride;
- u32_le format;
- u32_le usage;
- INSERT_PADDING_WORDS(1);
- u32_le index;
- INSERT_PADDING_WORDS(3);
- u32_le gpu_buffer_id;
- INSERT_PADDING_WORDS(6);
- u32_le external_format;
- INSERT_PADDING_WORDS(10);
- u32_le nvmap_handle;
- u32_le offset;
- INSERT_PADDING_WORDS(60);
-};
-
-static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
-
-class BufferQueue final {
-public:
- enum class QueryType {
- NativeWindowWidth = 0,
- NativeWindowHeight = 1,
- NativeWindowFormat = 2,
- /// The minimum number of buffers that must remain un-dequeued after a buffer has been
- /// queued
- NativeWindowMinUndequeuedBuffers = 3,
- /// The consumer gralloc usage bits currently set by the consumer
- NativeWindowConsumerUsageBits = 10,
- };
-
- explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
- KernelHelpers::ServiceContext& service_context_);
- ~BufferQueue();
-
- enum class BufferTransformFlags : u32 {
- /// No transform flags are set
- Unset = 0x00,
- /// Flip source image horizontally (around the vertical axis)
- FlipH = 0x01,
- /// Flip source image vertically (around the horizontal axis)
- FlipV = 0x02,
- /// Rotate source image 90 degrees clockwise
- Rotate90 = 0x04,
- /// Rotate source image 180 degrees
- Rotate180 = 0x03,
- /// Rotate source image 270 degrees clockwise
- Rotate270 = 0x07,
- };
-
- enum class PixelFormat : u32 {
- RGBA8888 = 1,
- RGBX8888 = 2,
- RGB888 = 3,
- RGB565 = 4,
- BGRA8888 = 5,
- RGBA5551 = 6,
- RRGBA4444 = 7,
- };
-
- struct Buffer {
- enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
-
- u32 slot;
- Status status = Status::Free;
- IGBPBuffer igbp_buffer;
- BufferTransformFlags transform;
- Common::Rectangle<int> crop_rect;
- u32 swap_interval;
- Service::Nvidia::MultiFence multi_fence;
- };
-
- void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
- std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> DequeueBuffer(u32 width,
- u32 height);
- const IGBPBuffer& RequestBuffer(u32 slot) const;
- void QueueBuffer(u32 slot, BufferTransformFlags transform,
- const Common::Rectangle<int>& crop_rect, u32 swap_interval,
- Service::Nvidia::MultiFence& multi_fence);
- void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
- std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
- void ReleaseBuffer(u32 slot);
- void Connect();
- void Disconnect();
- u32 Query(QueryType type);
-
- u32 GetId() const {
- return id;
- }
-
- bool IsConnected() const {
- return is_connect;
- }
-
- Kernel::KWritableEvent& GetWritableBufferWaitEvent();
-
- Kernel::KReadableEvent& GetBufferWaitEvent();
-
-private:
- BufferQueue(const BufferQueue&) = delete;
-
- u32 id{};
- u64 layer_id{};
- std::atomic_bool is_connect{};
-
- std::list<u32> free_buffers;
- std::array<Buffer, buffer_slots> buffers;
- std::list<u32> queue_sequence;
- Kernel::KEvent* buffer_wait_event{};
-
- std::mutex free_buffers_mutex;
- std::condition_variable free_buffers_condition;
-
- std::mutex queue_sequence_mutex;
-
- KernelHelpers::ServiceContext& service_context;
-};
-
-} // 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..1ce67c771
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp
@@ -0,0 +1,213 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueConsumer.cpp
+
+#include "common/logging/log.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
+#include "core/hle/service/nvflinger/buffer_item.h"
+#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/producer_listener.h"
+#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
+
+namespace Service::android {
+
+BufferQueueConsumer::BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
+ Service::Nvidia::NvCore::NvMap& nvmap_)
+ : core{std::move(core_)}, slots{core->slots}, nvmap(nvmap_) {}
+
+BufferQueueConsumer::~BufferQueueConsumer() = default;
+
+Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer,
+ std::chrono::nanoseconds expected_present) {
+ std::scoped_lock lock{core->mutex};
+
+ // Check that the consumer doesn't currently have the maximum number of buffers acquired.
+ const s32 num_acquired_buffers{
+ static_cast<s32>(std::count_if(slots.begin(), slots.end(), [](const auto& slot) {
+ return slot.buffer_state == BufferState::Acquired;
+ }))};
+
+ if (num_acquired_buffers >= core->max_acquired_buffer_count + 1) {
+ LOG_ERROR(Service_NVFlinger, "max acquired buffer count reached: {} (max {})",
+ num_acquired_buffers, core->max_acquired_buffer_count);
+ return Status::InvalidOperation;
+ }
+
+ // Check if the queue is empty.
+ if (core->queue.empty()) {
+ return Status::NoBufferAvailable;
+ }
+
+ auto front(core->queue.begin());
+
+ // If expected_present is specified, we may not want to return a buffer yet.
+ if (expected_present.count() != 0) {
+ constexpr auto MAX_REASONABLE_NSEC = 1000000000LL; // 1 second
+
+ // The expected_present argument indicates when the buffer is expected to be presented
+ // on-screen.
+ while (core->queue.size() > 1 && !core->queue[0].is_auto_timestamp) {
+ const auto& buffer_item{core->queue[1]};
+
+ // If entry[1] is timely, drop entry[0] (and repeat).
+ const auto desired_present = buffer_item.timestamp;
+ if (desired_present < expected_present.count() - MAX_REASONABLE_NSEC ||
+ desired_present > expected_present.count()) {
+ // This buffer is set to display in the near future, or desired_present is garbage.
+ LOG_DEBUG(Service_NVFlinger, "nodrop desire={} expect={}", desired_present,
+ expected_present.count());
+ break;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "drop desire={} expect={} size={}", desired_present,
+ expected_present.count(), core->queue.size());
+
+ if (core->StillTracking(*front)) {
+ // Front buffer is still in mSlots, so mark the slot as free
+ slots[front->slot].buffer_state = BufferState::Free;
+ }
+
+ core->queue.erase(front);
+ front = core->queue.begin();
+ }
+
+ // See if the front buffer is ready to be acquired.
+ const auto desired_present = front->timestamp;
+ if (desired_present > expected_present.count() &&
+ desired_present < expected_present.count() + MAX_REASONABLE_NSEC) {
+ LOG_DEBUG(Service_NVFlinger, "defer desire={} expect={}", desired_present,
+ expected_present.count());
+ return Status::PresentLater;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "accept desire={} expect={}", desired_present,
+ expected_present.count());
+ }
+
+ const auto slot = front->slot;
+ *out_buffer = *front;
+
+ LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot);
+
+ // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to
+ // avoid unnecessarily remapping this buffer on the consumer side.
+ if (out_buffer->acquire_called) {
+ out_buffer->graphic_buffer = nullptr;
+ }
+
+ core->queue.erase(front);
+
+ // We might have freed a slot while dropping old buffers, or the producer may be blocked
+ // waiting for the number of buffers in the queue to decrease.
+ core->SignalDequeueCondition();
+
+ return Status::NoError;
+}
+
+Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence) {
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "slot {} out of range", slot);
+ return Status::BadValue;
+ }
+
+ std::shared_ptr<IProducerListener> listener;
+ {
+ std::scoped_lock lock{core->mutex};
+
+ // If the frame number has changed because the buffer has been reallocated, we can ignore
+ // this ReleaseBuffer for the old buffer.
+ if (frame_number != slots[slot].frame_number) {
+ return Status::StaleBufferSlot;
+ }
+
+ // Make sure this buffer hasn't been queued while acquired by the consumer.
+ auto current(core->queue.begin());
+ while (current != core->queue.end()) {
+ if (current->slot == slot) {
+ LOG_ERROR(Service_NVFlinger, "buffer slot {} pending release is currently queued",
+ slot);
+ return Status::BadValue;
+ }
+ ++current;
+ }
+
+ slots[slot].buffer_state = BufferState::Free;
+
+ nvmap.FreeHandle(slots[slot].graphic_buffer->BufferId(), true);
+
+ listener = core->connected_producer_listener;
+
+ LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot);
+
+ core->SignalDequeueCondition();
+ }
+
+ // Call back without lock held
+ if (listener != nullptr) {
+ listener->OnBufferReleased();
+ }
+
+ return Status::NoError;
+}
+
+Status BufferQueueConsumer::Connect(std::shared_ptr<IConsumerListener> consumer_listener,
+ bool controlled_by_app) {
+ if (consumer_listener == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "consumer_listener may not be nullptr");
+ return Status::BadValue;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "controlled_by_app={}", controlled_by_app);
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ core->consumer_listener = consumer_listener;
+ core->consumer_controlled_by_app = controlled_by_app;
+
+ return Status::NoError;
+}
+
+Status BufferQueueConsumer::GetReleasedBuffers(u64* out_slot_mask) {
+ if (out_slot_mask == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "out_slot_mask may not be nullptr");
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ u64 mask = 0;
+ for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (!slots[s].acquire_called) {
+ mask |= (1ULL << s);
+ }
+ }
+
+ // Remove from the mask queued buffers for which acquire has been called, since the consumer
+ // will not receive their buffer addresses and so must retain their cached information
+ auto current(core->queue.begin());
+ while (current != core->queue.end()) {
+ if (current->acquire_called) {
+ mask &= ~(1ULL << current->slot);
+ }
+ ++current;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "returning mask {}", mask);
+ *out_slot_mask = mask;
+ return Status::NoError;
+}
+
+} // 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..4ec06ca13
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.h
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueConsumer.h
+
+#pragma once
+
+#include <chrono>
+#include <memory>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvflinger/status.h"
+
+namespace Service::Nvidia::NvCore {
+class NvMap;
+} // namespace Service::Nvidia::NvCore
+
+namespace Service::android {
+
+class BufferItem;
+class BufferQueueCore;
+class IConsumerListener;
+
+class BufferQueueConsumer final {
+public:
+ explicit BufferQueueConsumer(std::shared_ptr<BufferQueueCore> core_,
+ Service::Nvidia::NvCore::NvMap& nvmap_);
+ ~BufferQueueConsumer();
+
+ Status AcquireBuffer(BufferItem* out_buffer, std::chrono::nanoseconds expected_present);
+ Status ReleaseBuffer(s32 slot, u64 frame_number, const Fence& release_fence);
+ Status Connect(std::shared_ptr<IConsumerListener> consumer_listener, bool controlled_by_app);
+ Status GetReleasedBuffers(u64* out_slot_mask);
+
+private:
+ std::shared_ptr<BufferQueueCore> core;
+ BufferQueueDefs::SlotsType& slots;
+ Service::Nvidia::NvCore::NvMap& nvmap;
+};
+
+} // 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..ea4a14ea4
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp
@@ -0,0 +1,113 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueCore.cpp
+
+#include "common/assert.h"
+
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+
+namespace Service::android {
+
+BufferQueueCore::BufferQueueCore() = default;
+
+BufferQueueCore::~BufferQueueCore() = default;
+
+void BufferQueueCore::NotifyShutdown() {
+ std::scoped_lock lock{mutex};
+
+ is_shutting_down = true;
+
+ SignalDequeueCondition();
+}
+
+void BufferQueueCore::SignalDequeueCondition() {
+ dequeue_condition.notify_all();
+}
+
+bool BufferQueueCore::WaitForDequeueCondition() {
+ if (is_shutting_down) {
+ return false;
+ }
+
+ dequeue_condition.wait(mutex);
+
+ return true;
+}
+
+s32 BufferQueueCore::GetMinUndequeuedBufferCountLocked(bool async) const {
+ // If DequeueBuffer is allowed to error out, we don't have to add an extra buffer.
+ if (!use_async_buffer) {
+ return max_acquired_buffer_count;
+ }
+
+ if (dequeue_buffer_cannot_block || async) {
+ return max_acquired_buffer_count + 1;
+ }
+
+ return max_acquired_buffer_count;
+}
+
+s32 BufferQueueCore::GetMinMaxBufferCountLocked(bool async) const {
+ return GetMinUndequeuedBufferCountLocked(async) + 1;
+}
+
+s32 BufferQueueCore::GetMaxBufferCountLocked(bool async) const {
+ const auto min_buffer_count = GetMinMaxBufferCountLocked(async);
+ auto max_buffer_count = std::max(default_max_buffer_count, min_buffer_count);
+
+ if (override_max_buffer_count != 0) {
+ ASSERT(override_max_buffer_count >= min_buffer_count);
+ max_buffer_count = override_max_buffer_count;
+ }
+
+ // Any buffers that are dequeued by the producer or sitting in the queue waiting to be consumed
+ // need to have their slots preserved.
+ for (s32 slot = max_buffer_count; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+ const auto state = slots[slot].buffer_state;
+ if (state == BufferState::Queued || state == BufferState::Dequeued) {
+ max_buffer_count = slot + 1;
+ }
+ }
+
+ return max_buffer_count;
+}
+
+s32 BufferQueueCore::GetPreallocatedBufferCountLocked() const {
+ return static_cast<s32>(std::count_if(slots.begin(), slots.end(),
+ [](const auto& slot) { return slot.is_preallocated; }));
+}
+
+void BufferQueueCore::FreeBufferLocked(s32 slot) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ slots[slot].graphic_buffer.reset();
+
+ slots[slot].buffer_state = BufferState::Free;
+ slots[slot].frame_number = UINT32_MAX;
+ slots[slot].acquire_called = false;
+ slots[slot].fence = Fence::NoFence();
+}
+
+void BufferQueueCore::FreeAllBuffersLocked() {
+ buffer_has_been_queued = false;
+
+ for (s32 slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
+ FreeBufferLocked(slot);
+ }
+}
+
+bool BufferQueueCore::StillTracking(const BufferItem& item) const {
+ const BufferSlot& slot = slots[item.slot];
+
+ return (slot.graphic_buffer != nullptr) && (item.graphic_buffer == slot.graphic_buffer);
+}
+
+void BufferQueueCore::WaitWhileAllocatingLocked() const {
+ while (is_allocating) {
+ is_allocating_condition.wait(mutex);
+ }
+}
+
+} // 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..ca6baefaf
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_core.h
@@ -0,0 +1,79 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueCore.h
+
+#pragma once
+
+#include <condition_variable>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <vector>
+
+#include "core/hle/service/nvflinger/buffer_item.h"
+#include "core/hle/service/nvflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
+#include "core/hle/service/nvflinger/status.h"
+#include "core/hle/service/nvflinger/window.h"
+
+namespace Service::android {
+
+class IConsumerListener;
+class IProducerListener;
+
+class BufferQueueCore final {
+ friend class BufferQueueProducer;
+ friend class BufferQueueConsumer;
+
+public:
+ static constexpr s32 INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT;
+
+ BufferQueueCore();
+ ~BufferQueueCore();
+
+ void NotifyShutdown();
+
+private:
+ void SignalDequeueCondition();
+ bool WaitForDequeueCondition();
+
+ s32 GetMinUndequeuedBufferCountLocked(bool async) const;
+ s32 GetMinMaxBufferCountLocked(bool async) const;
+ s32 GetMaxBufferCountLocked(bool async) const;
+ s32 GetPreallocatedBufferCountLocked() const;
+ void FreeBufferLocked(s32 slot);
+ void FreeAllBuffersLocked();
+ bool StillTracking(const BufferItem& item) const;
+ void WaitWhileAllocatingLocked() const;
+
+private:
+ mutable std::mutex mutex;
+ bool is_abandoned{};
+ bool consumer_controlled_by_app{};
+ std::shared_ptr<IConsumerListener> consumer_listener;
+ u32 consumer_usage_bit{};
+ NativeWindowApi connected_api{NativeWindowApi::NoConnectedApi};
+ std::shared_ptr<IProducerListener> connected_producer_listener;
+ BufferQueueDefs::SlotsType slots{};
+ std::vector<BufferItem> queue;
+ s32 override_max_buffer_count{};
+ mutable std::condition_variable_any dequeue_condition;
+ const bool use_async_buffer{}; // This is always disabled on HOS
+ bool dequeue_buffer_cannot_block{};
+ PixelFormat default_buffer_format{PixelFormat::Rgba8888};
+ u32 default_width{1};
+ u32 default_height{1};
+ s32 default_max_buffer_count{2};
+ const s32 max_acquired_buffer_count{}; // This is always zero on HOS
+ bool buffer_has_been_queued{};
+ u64 frame_counter{};
+ u32 transform_hint{};
+ bool is_allocating{};
+ mutable std::condition_variable_any is_allocating_condition;
+ bool is_shutting_down{};
+};
+
+} // 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..334445213
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_defs.h
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueDefs.h
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/buffer_slot.h"
+
+namespace Service::android::BufferQueueDefs {
+
+// BufferQueue will keep track of at most this value of buffers.
+constexpr s32 NUM_BUFFER_SLOTS = 64;
+
+using SlotsType = std::array<BufferSlot, NUM_BUFFER_SLOTS>;
+
+} // 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..d4ab23a10
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp
@@ -0,0 +1,927 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/BufferQueueProducer.cpp
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/settings.h"
+#include "core/core.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/kernel/k_readable_event.h"
+#include "core/hle/kernel/k_writable_event.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/nvdrv/core/nvmap.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvflinger/consumer_listener.h"
+#include "core/hle/service/nvflinger/parcel.h"
+#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
+#include "core/hle/service/nvflinger/window.h"
+#include "core/hle/service/vi/vi.h"
+
+namespace Service::android {
+
+BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
+ std::shared_ptr<BufferQueueCore> buffer_queue_core_,
+ Service::Nvidia::NvCore::NvMap& nvmap_)
+ : service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots),
+ nvmap(nvmap_) {
+ buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
+}
+
+BufferQueueProducer::~BufferQueueProducer() {
+ service_context.CloseEvent(buffer_wait_event);
+}
+
+Status BufferQueueProducer::RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return Status::BadValue;
+ } else if (slots[slot].buffer_state != BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
+ slots[slot].buffer_state);
+ return Status::BadValue;
+ }
+
+ slots[slot].request_buffer_called = true;
+ *buf = slots[slot].graphic_buffer;
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::SetBufferCount(s32 buffer_count) {
+ LOG_DEBUG(Service_NVFlinger, "count = {}", buffer_count);
+
+ std::shared_ptr<IConsumerListener> listener;
+ {
+ std::scoped_lock lock{core->mutex};
+ core->WaitWhileAllocatingLocked();
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ if (buffer_count > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "buffer_count {} too large (max {})", buffer_count,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return Status::BadValue;
+ }
+
+ // There must be no dequeued buffers when changing the buffer count.
+ for (s32 s{}; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (slots[s].buffer_state == BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger, "buffer owned by producer");
+ return Status::BadValue;
+ }
+ }
+
+ if (buffer_count == 0) {
+ core->override_max_buffer_count = 0;
+ core->SignalDequeueCondition();
+ return Status::NoError;
+ }
+
+ const s32 min_buffer_slots = core->GetMinMaxBufferCountLocked(false);
+ if (buffer_count < min_buffer_slots) {
+ LOG_ERROR(Service_NVFlinger, "requested buffer count {} is less than minimum {}",
+ buffer_count, min_buffer_slots);
+ return Status::BadValue;
+ }
+
+ // Here we are guaranteed that the producer doesn't have any dequeued buffers and will
+ // release all of its buffer references.
+ if (core->GetPreallocatedBufferCountLocked() <= 0) {
+ core->FreeAllBuffersLocked();
+ }
+
+ core->override_max_buffer_count = buffer_count;
+ core->SignalDequeueCondition();
+ buffer_wait_event->GetWritableEvent().Signal();
+ listener = core->consumer_listener;
+ }
+
+ // Call back without lock held
+ if (listener != nullptr) {
+ listener->OnBuffersReleased();
+ }
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found,
+ Status* return_flags) const {
+ bool try_again = true;
+
+ while (try_again) {
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
+ if (async && core->override_max_buffer_count) {
+ if (core->override_max_buffer_count < max_buffer_count) {
+ LOG_ERROR(Service_NVFlinger, "async mode is invalid with buffer count override");
+ return Status::BadValue;
+ }
+ }
+
+ // Free up any buffers that are in slots beyond the max buffer count
+ for (s32 s = max_buffer_count; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ ASSERT(slots[s].buffer_state == BufferState::Free);
+ if (slots[s].graphic_buffer != nullptr) {
+ core->FreeBufferLocked(s);
+ *return_flags |= Status::ReleaseAllBuffers;
+ }
+ }
+
+ // Look for a free buffer to give to the client
+ *found = BufferQueueCore::INVALID_BUFFER_SLOT;
+ s32 dequeued_count{};
+ s32 acquired_count{};
+ for (s32 s{}; s < max_buffer_count; ++s) {
+ switch (slots[s].buffer_state) {
+ case BufferState::Dequeued:
+ ++dequeued_count;
+ break;
+ case BufferState::Acquired:
+ ++acquired_count;
+ break;
+ case BufferState::Free:
+ // We return the oldest of the free buffers to avoid stalling the producer if
+ // possible, since the consumer may still have pending reads of in-flight buffers
+ if (*found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+ slots[s].frame_number < slots[*found].frame_number) {
+ *found = s;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Producers are not allowed to dequeue more than one buffer if they did not set a buffer
+ // count
+ if (!core->override_max_buffer_count && dequeued_count) {
+ LOG_ERROR(Service_NVFlinger,
+ "can't dequeue multiple buffers without setting the buffer count");
+ return Status::InvalidOperation;
+ }
+
+ // See whether a buffer has been queued since the last SetBufferCount so we know whether to
+ // perform the min undequeued buffers check below
+ if (core->buffer_has_been_queued) {
+ // Make sure the producer is not trying to dequeue more buffers than allowed
+ const s32 new_undequeued_count = max_buffer_count - (dequeued_count + 1);
+ const s32 min_undequeued_count = core->GetMinUndequeuedBufferCountLocked(async);
+ if (new_undequeued_count < min_undequeued_count) {
+ LOG_ERROR(Service_NVFlinger,
+ "min undequeued buffer count({}) exceeded (dequeued={} undequeued={})",
+ min_undequeued_count, dequeued_count, new_undequeued_count);
+ return Status::InvalidOperation;
+ }
+ }
+
+ // If we disconnect and reconnect quickly, we can be in a state where our slots are empty
+ // but we have many buffers in the queue. This can cause us to run out of memory if we
+ // outrun the consumer. Wait here if it looks like we have too many buffers queued up.
+ const bool too_many_buffers = core->queue.size() > static_cast<size_t>(max_buffer_count);
+ if (too_many_buffers) {
+ LOG_ERROR(Service_NVFlinger, "queue size is {}, waiting", core->queue.size());
+ }
+
+ // If no buffer is found, or if the queue has too many buffers outstanding, wait for a
+ // buffer to be acquired or released, or for the max buffer count to change.
+ try_again = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) || too_many_buffers;
+ if (try_again) {
+ // Return an error if we're in non-blocking mode (producer and consumer are controlled
+ // by the application).
+ if (core->dequeue_buffer_cannot_block &&
+ (acquired_count <= core->max_acquired_buffer_count)) {
+ return Status::WouldBlock;
+ }
+
+ if (!core->WaitForDequeueCondition()) {
+ // We are no longer running
+ return Status::NoError;
+ }
+ }
+ }
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool async, u32 width,
+ u32 height, PixelFormat format, u32 usage) {
+ LOG_DEBUG(Service_NVFlinger, "async={} w={} h={} format={}, usage={}", async ? "true" : "false",
+ width, height, format, usage);
+
+ if ((width != 0 && height == 0) || (width == 0 && height != 0)) {
+ LOG_ERROR(Service_NVFlinger, "invalid size: w={} h={}", width, height);
+ return Status::BadValue;
+ }
+
+ Status return_flags = Status::NoError;
+ bool attached_by_consumer = false;
+ {
+ std::scoped_lock lock{core->mutex};
+ core->WaitWhileAllocatingLocked();
+
+ if (format == PixelFormat::NoFormat) {
+ format = core->default_buffer_format;
+ }
+
+ // Enable the usage bits the consumer requested
+ usage |= core->consumer_usage_bit;
+
+ s32 found{};
+ Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags);
+ if (status != Status::NoError) {
+ return status;
+ }
+
+ // This should not happen
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ LOG_ERROR(Service_NVFlinger, "no available buffer slots");
+ return Status::Busy;
+ }
+
+ *out_slot = found;
+
+ attached_by_consumer = slots[found].attached_by_consumer;
+
+ const bool use_default_size = !width && !height;
+ if (use_default_size) {
+ width = core->default_width;
+ height = core->default_height;
+ }
+
+ slots[found].buffer_state = BufferState::Dequeued;
+
+ const std::shared_ptr<GraphicBuffer>& buffer(slots[found].graphic_buffer);
+ if ((buffer == nullptr) || (buffer->Width() != width) || (buffer->Height() != height) ||
+ (buffer->Format() != format) || ((buffer->Usage() & usage) != usage)) {
+ slots[found].acquire_called = false;
+ slots[found].graphic_buffer = nullptr;
+ slots[found].request_buffer_called = false;
+ slots[found].fence = Fence::NoFence();
+
+ return_flags |= Status::BufferNeedsReallocation;
+ }
+
+ *out_fence = slots[found].fence;
+ slots[found].fence = Fence::NoFence();
+ }
+
+ if ((return_flags & Status::BufferNeedsReallocation) != Status::None) {
+ LOG_DEBUG(Service_NVFlinger, "allocating a new buffer for slot {}", *out_slot);
+
+ auto graphic_buffer = std::make_shared<GraphicBuffer>(width, height, format, usage);
+ if (graphic_buffer == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "creating GraphicBuffer failed");
+ return Status::NoMemory;
+ }
+
+ {
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ slots[*out_slot].frame_number = UINT32_MAX;
+ slots[*out_slot].graphic_buffer = graphic_buffer;
+ }
+ }
+
+ if (attached_by_consumer) {
+ return_flags |= Status::BufferNeedsReallocation;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "returning slot={} frame={}, flags={}", *out_slot,
+ slots[*out_slot].frame_number, return_flags);
+
+ return return_flags;
+}
+
+Status BufferQueueProducer::DetachBuffer(s32 slot) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "slot {} out of range [0, {})", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return Status::BadValue;
+ } else if (slots[slot].buffer_state != BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
+ slots[slot].buffer_state);
+ return Status::BadValue;
+ } else if (!slots[slot].request_buffer_called) {
+ LOG_ERROR(Service_NVFlinger, "buffer in slot {} has not been requested", slot);
+ return Status::BadValue;
+ }
+
+ core->FreeBufferLocked(slot);
+ core->SignalDequeueCondition();
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer,
+ Fence* out_fence) {
+ if (out_buffer == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "out_buffer must not be nullptr");
+ return Status::BadValue;
+ } else if (out_fence == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "out_fence must not be nullptr");
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{core->mutex};
+ core->WaitWhileAllocatingLocked();
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ // Find the oldest valid slot
+ int found = BufferQueueCore::INVALID_BUFFER_SLOT;
+ for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
+ if (slots[s].buffer_state == BufferState::Free && slots[s].graphic_buffer != nullptr) {
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+ slots[s].frame_number < slots[found].frame_number) {
+ found = s;
+ }
+ }
+ }
+
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ return Status::NoMemory;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "Detached slot {}", found);
+
+ *out_buffer = slots[found].graphic_buffer;
+ *out_fence = slots[found].fence;
+
+ core->FreeBufferLocked(found);
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::AttachBuffer(s32* out_slot,
+ const std::shared_ptr<GraphicBuffer>& buffer) {
+ if (out_slot == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "out_slot must not be nullptr");
+ return Status::BadValue;
+ } else if (buffer == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "Cannot attach nullptr buffer");
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{core->mutex};
+ core->WaitWhileAllocatingLocked();
+
+ Status return_flags = Status::NoError;
+ s32 found{};
+
+ const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags);
+ if (status != Status::NoError) {
+ return status;
+ }
+
+ // This should not happen
+ if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+ LOG_ERROR(Service_NVFlinger, "No available buffer slots");
+ return Status::Busy;
+ }
+
+ *out_slot = found;
+
+ LOG_DEBUG(Service_NVFlinger, "Returning slot {} flags={}", *out_slot, return_flags);
+
+ slots[*out_slot].graphic_buffer = buffer;
+ slots[*out_slot].buffer_state = BufferState::Dequeued;
+ slots[*out_slot].fence = Fence::NoFence();
+ slots[*out_slot].request_buffer_called = true;
+
+ return return_flags;
+}
+
+Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
+ QueueBufferOutput* output) {
+ s64 timestamp{};
+ bool is_auto_timestamp{};
+ Common::Rectangle<s32> crop;
+ NativeWindowScalingMode scaling_mode{};
+ NativeWindowTransform transform;
+ u32 sticky_transform_{};
+ bool async{};
+ s32 swap_interval{};
+ Fence fence{};
+
+ input.Deflate(&timestamp, &is_auto_timestamp, &crop, &scaling_mode, &transform,
+ &sticky_transform_, &async, &swap_interval, &fence);
+
+ switch (scaling_mode) {
+ case NativeWindowScalingMode::Freeze:
+ case NativeWindowScalingMode::ScaleToWindow:
+ case NativeWindowScalingMode::ScaleCrop:
+ case NativeWindowScalingMode::NoScaleCrop:
+ break;
+ default:
+ LOG_ERROR(Service_NVFlinger, "unknown scaling mode {}", scaling_mode);
+ return Status::BadValue;
+ }
+
+ std::shared_ptr<IConsumerListener> frame_available_listener;
+ std::shared_ptr<IConsumerListener> frame_replaced_listener;
+ s32 callback_ticket{};
+ BufferItem item;
+
+ {
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ const s32 max_buffer_count = core->GetMaxBufferCountLocked(async);
+ if (async && core->override_max_buffer_count) {
+ if (core->override_max_buffer_count < max_buffer_count) {
+ LOG_ERROR(Service_NVFlinger, "async mode is invalid with "
+ "buffer count override");
+ return Status::BadValue;
+ }
+ }
+
+ if (slot < 0 || slot >= max_buffer_count) {
+ LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
+ max_buffer_count);
+ return Status::BadValue;
+ } else if (slots[slot].buffer_state != BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger,
+ "slot {} is not owned by the producer "
+ "(state = {})",
+ slot, slots[slot].buffer_state);
+ return Status::BadValue;
+ } else if (!slots[slot].request_buffer_called) {
+ LOG_ERROR(Service_NVFlinger,
+ "slot {} was queued without requesting "
+ "a buffer",
+ slot);
+ return Status::BadValue;
+ }
+
+ LOG_DEBUG(Service_NVFlinger,
+ "slot={} frame={} time={} crop=[{},{},{},{}] transform={} scale={}", slot,
+ core->frame_counter + 1, timestamp, crop.Left(), crop.Top(), crop.Right(),
+ crop.Bottom(), transform, scaling_mode);
+
+ const std::shared_ptr<GraphicBuffer>& graphic_buffer(slots[slot].graphic_buffer);
+ Common::Rectangle<s32> buffer_rect(graphic_buffer->Width(), graphic_buffer->Height());
+ Common::Rectangle<s32> cropped_rect;
+ [[maybe_unused]] const bool unused = crop.Intersect(buffer_rect, &cropped_rect);
+
+ if (cropped_rect != crop) {
+ LOG_ERROR(Service_NVFlinger, "crop rect is not contained within the buffer in slot {}",
+ slot);
+ return Status::BadValue;
+ }
+
+ slots[slot].fence = fence;
+ slots[slot].buffer_state = BufferState::Queued;
+ ++core->frame_counter;
+ slots[slot].frame_number = core->frame_counter;
+
+ item.acquire_called = slots[slot].acquire_called;
+ item.graphic_buffer = slots[slot].graphic_buffer;
+ item.crop = crop;
+ item.transform = transform & ~NativeWindowTransform::InverseDisplay;
+ item.transform_to_display_inverse =
+ (transform & NativeWindowTransform::InverseDisplay) != NativeWindowTransform::None;
+ item.scaling_mode = static_cast<u32>(scaling_mode);
+ item.timestamp = timestamp;
+ item.is_auto_timestamp = is_auto_timestamp;
+ item.frame_number = core->frame_counter;
+ item.slot = slot;
+ item.fence = fence;
+ item.is_droppable = core->dequeue_buffer_cannot_block || async;
+ item.swap_interval = swap_interval;
+
+ nvmap.DuplicateHandle(item.graphic_buffer->BufferId(), true);
+
+ sticky_transform = sticky_transform_;
+
+ if (core->queue.empty()) {
+ // When the queue is empty, we can simply queue this buffer
+ core->queue.push_back(item);
+ frame_available_listener = core->consumer_listener;
+ } else {
+ // When the queue is not empty, we need to look at the front buffer
+ // state to see if we need to replace it
+ auto front(core->queue.begin());
+
+ if (front->is_droppable) {
+ // If the front queued buffer is still being tracked, we first
+ // mark it as freed
+ if (core->StillTracking(*front)) {
+ slots[front->slot].buffer_state = BufferState::Free;
+ // Reset the frame number of the freed buffer so that it is the first in line to
+ // be dequeued again
+ slots[front->slot].frame_number = 0;
+ }
+ // Overwrite the droppable buffer with the incoming one
+ *front = item;
+ frame_replaced_listener = core->consumer_listener;
+ } else {
+ core->queue.push_back(item);
+ frame_available_listener = core->consumer_listener;
+ }
+ }
+
+ core->buffer_has_been_queued = true;
+ core->SignalDequeueCondition();
+ output->Inflate(core->default_width, core->default_height, core->transform_hint,
+ static_cast<u32>(core->queue.size()));
+
+ // Take a ticket for the callback functions
+ callback_ticket = next_callback_ticket++;
+ }
+
+ // Don't send the GraphicBuffer through the callback, and don't send the slot number, since the
+ // consumer shouldn't need it
+ item.graphic_buffer.reset();
+ item.slot = BufferItem::INVALID_BUFFER_SLOT;
+
+ // Call back without the main BufferQueue lock held, but with the callback lock held so we can
+ // ensure that callbacks occur in order
+ {
+ std::scoped_lock lock{callback_mutex};
+ while (callback_ticket != current_callback_ticket) {
+ callback_condition.wait(callback_mutex);
+ }
+
+ if (frame_available_listener != nullptr) {
+ frame_available_listener->OnFrameAvailable(item);
+ } else if (frame_replaced_listener != nullptr) {
+ frame_replaced_listener->OnFrameReplaced(item);
+ }
+
+ ++current_callback_ticket;
+ callback_condition.notify_all();
+ }
+
+ return Status::NoError;
+}
+
+void BufferQueueProducer::CancelBuffer(s32 slot, const Fence& fence) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ std::scoped_lock lock{core->mutex};
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return;
+ }
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ LOG_ERROR(Service_NVFlinger, "slot index {} out of range [0, {})", slot,
+ BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return;
+ } else if (slots[slot].buffer_state != BufferState::Dequeued) {
+ LOG_ERROR(Service_NVFlinger, "slot {} is not owned by the producer (state = {})", slot,
+ slots[slot].buffer_state);
+ return;
+ }
+
+ slots[slot].buffer_state = BufferState::Free;
+ slots[slot].frame_number = 0;
+ slots[slot].fence = fence;
+
+ core->SignalDequeueCondition();
+ buffer_wait_event->GetWritableEvent().Signal();
+}
+
+Status BufferQueueProducer::Query(NativeWindow what, s32* out_value) {
+ std::scoped_lock lock{core->mutex};
+
+ if (out_value == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "outValue was nullptr");
+ return Status::BadValue;
+ }
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ u32 value{};
+ switch (what) {
+ case NativeWindow::Width:
+ value = core->default_width;
+ break;
+ case NativeWindow::Height:
+ value = core->default_height;
+ break;
+ case NativeWindow::Format:
+ value = static_cast<u32>(core->default_buffer_format);
+ break;
+ case NativeWindow::MinUndequeedBuffers:
+ value = core->GetMinUndequeuedBufferCountLocked(false);
+ break;
+ case NativeWindow::StickyTransform:
+ value = sticky_transform;
+ break;
+ case NativeWindow::ConsumerRunningBehind:
+ value = (core->queue.size() > 1);
+ break;
+ case NativeWindow::ConsumerUsageBits:
+ value = core->consumer_usage_bit;
+ break;
+ default:
+ ASSERT(false);
+ return Status::BadValue;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "what = {}, value = {}", what, value);
+
+ *out_value = static_cast<s32>(value);
+
+ return Status::NoError;
+}
+
+Status BufferQueueProducer::Connect(const std::shared_ptr<IProducerListener>& listener,
+ NativeWindowApi api, bool producer_controlled_by_app,
+ QueueBufferOutput* output) {
+ std::scoped_lock lock{core->mutex};
+
+ LOG_DEBUG(Service_NVFlinger, "api = {} producer_controlled_by_app = {}", api,
+ producer_controlled_by_app);
+
+ if (core->is_abandoned) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has been abandoned");
+ return Status::NoInit;
+ }
+
+ if (core->consumer_listener == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "BufferQueue has no consumer");
+ return Status::NoInit;
+ }
+
+ if (output == nullptr) {
+ LOG_ERROR(Service_NVFlinger, "output was nullptr");
+ return Status::BadValue;
+ }
+
+ if (core->connected_api != NativeWindowApi::NoConnectedApi) {
+ LOG_ERROR(Service_NVFlinger, "already connected (cur = {} req = {})", core->connected_api,
+ api);
+ return Status::BadValue;
+ }
+
+ Status status = Status::NoError;
+ switch (api) {
+ case NativeWindowApi::Egl:
+ case NativeWindowApi::Cpu:
+ case NativeWindowApi::Media:
+ case NativeWindowApi::Camera:
+ core->connected_api = api;
+ output->Inflate(core->default_width, core->default_height, core->transform_hint,
+ static_cast<u32>(core->queue.size()));
+ core->connected_producer_listener = listener;
+ break;
+ default:
+ LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
+ status = Status::BadValue;
+ break;
+ }
+
+ core->buffer_has_been_queued = false;
+ core->dequeue_buffer_cannot_block =
+ core->consumer_controlled_by_app && producer_controlled_by_app;
+
+ return status;
+}
+
+Status BufferQueueProducer::Disconnect(NativeWindowApi api) {
+ LOG_DEBUG(Service_NVFlinger, "api = {}", api);
+
+ Status status = Status::NoError;
+ std::shared_ptr<IConsumerListener> listener;
+
+ {
+ std::scoped_lock lock{core->mutex};
+
+ core->WaitWhileAllocatingLocked();
+
+ if (core->is_abandoned) {
+ // Disconnecting after the surface has been abandoned is a no-op.
+ return Status::NoError;
+ }
+
+ switch (api) {
+ case NativeWindowApi::Egl:
+ case NativeWindowApi::Cpu:
+ case NativeWindowApi::Media:
+ case NativeWindowApi::Camera:
+ if (core->connected_api == api) {
+ core->FreeAllBuffersLocked();
+ core->connected_producer_listener = nullptr;
+ core->connected_api = NativeWindowApi::NoConnectedApi;
+ core->SignalDequeueCondition();
+ buffer_wait_event->GetWritableEvent().Signal();
+ listener = core->consumer_listener;
+ } else {
+ LOG_ERROR(Service_NVFlinger, "still connected to another api (cur = {} req = {})",
+ core->connected_api, api);
+ status = Status::BadValue;
+ }
+ break;
+ default:
+ LOG_ERROR(Service_NVFlinger, "unknown api = {}", api);
+ status = Status::BadValue;
+ break;
+ }
+ }
+
+ // Call back without lock held
+ if (listener != nullptr) {
+ listener->OnBuffersReleased();
+ }
+
+ return status;
+}
+
+Status BufferQueueProducer::SetPreallocatedBuffer(s32 slot,
+ const std::shared_ptr<GraphicBuffer>& buffer) {
+ LOG_DEBUG(Service_NVFlinger, "slot {}", slot);
+
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ return Status::BadValue;
+ }
+
+ std::scoped_lock lock{core->mutex};
+
+ slots[slot] = {};
+ slots[slot].graphic_buffer = buffer;
+ slots[slot].frame_number = 0;
+
+ // Most games preallocate a buffer and pass a valid buffer here. However, it is possible for
+ // this to be called with an empty buffer, Naruto Ultimate Ninja Storm is a game that does this.
+ if (buffer) {
+ slots[slot].is_preallocated = true;
+
+ core->override_max_buffer_count = core->GetPreallocatedBufferCountLocked();
+ core->default_width = buffer->Width();
+ core->default_height = buffer->Height();
+ core->default_buffer_format = buffer->Format();
+ }
+
+ core->SignalDequeueCondition();
+ buffer_wait_event->GetWritableEvent().Signal();
+
+ return Status::NoError;
+}
+
+void BufferQueueProducer::Transact(Kernel::HLERequestContext& ctx, TransactionId code, u32 flags) {
+ Status status{Status::NoError};
+ Parcel parcel_in{ctx.ReadBuffer()};
+ Parcel parcel_out{};
+
+ switch (code) {
+ case TransactionId::Connect: {
+ const auto enable_listener = parcel_in.Read<bool>();
+ const auto api = parcel_in.Read<NativeWindowApi>();
+ const auto producer_controlled_by_app = parcel_in.Read<bool>();
+
+ UNIMPLEMENTED_IF_MSG(enable_listener, "Listener is unimplemented!");
+
+ std::shared_ptr<IProducerListener> listener;
+ QueueBufferOutput output{};
+
+ status = Connect(listener, api, producer_controlled_by_app, &output);
+
+ parcel_out.Write(output);
+ break;
+ }
+ case TransactionId::SetPreallocatedBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+ const auto buffer = parcel_in.ReadObject<GraphicBuffer>();
+
+ status = SetPreallocatedBuffer(slot, buffer);
+ break;
+ }
+ case TransactionId::DequeueBuffer: {
+ const auto is_async = parcel_in.Read<bool>();
+ const auto width = parcel_in.Read<u32>();
+ const auto height = parcel_in.Read<u32>();
+ const auto pixel_format = parcel_in.Read<PixelFormat>();
+ const auto usage = parcel_in.Read<u32>();
+
+ s32 slot{};
+ Fence fence{};
+
+ status = DequeueBuffer(&slot, &fence, is_async, width, height, pixel_format, usage);
+
+ parcel_out.Write(slot);
+ parcel_out.WriteObject(&fence);
+ break;
+ }
+ case TransactionId::RequestBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+
+ std::shared_ptr<GraphicBuffer> buf;
+
+ status = RequestBuffer(slot, &buf);
+
+ parcel_out.WriteObject(buf);
+ break;
+ }
+ case TransactionId::QueueBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+
+ QueueBufferInput input{parcel_in};
+ QueueBufferOutput output;
+
+ status = QueueBuffer(slot, input, &output);
+
+ parcel_out.Write(output);
+ break;
+ }
+ case TransactionId::Query: {
+ const auto what = parcel_in.Read<NativeWindow>();
+
+ s32 value{};
+
+ status = Query(what, &value);
+
+ parcel_out.Write(value);
+ break;
+ }
+ case TransactionId::CancelBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+ const auto fence = parcel_in.ReadFlattened<Fence>();
+
+ CancelBuffer(slot, fence);
+ break;
+ }
+ case TransactionId::Disconnect: {
+ const auto api = parcel_in.Read<NativeWindowApi>();
+
+ status = Disconnect(api);
+ break;
+ }
+ case TransactionId::DetachBuffer: {
+ const auto slot = parcel_in.Read<s32>();
+
+ status = DetachBuffer(slot);
+ break;
+ }
+ case TransactionId::SetBufferCount: {
+ const auto buffer_count = parcel_in.Read<s32>();
+
+ status = SetBufferCount(buffer_count);
+ break;
+ }
+ case TransactionId::GetBufferHistory:
+ LOG_WARNING(Service_NVFlinger, "(STUBBED) called, transaction=GetBufferHistory");
+ break;
+ default:
+ ASSERT_MSG(false, "Unimplemented TransactionId {}", code);
+ break;
+ }
+
+ parcel_out.Write(status);
+
+ ctx.WriteBuffer(parcel_out.Serialize());
+}
+
+Kernel::KReadableEvent& BufferQueueProducer::GetNativeHandle() {
+ return buffer_wait_event->GetReadableEvent();
+}
+
+} // 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..0ba03a568
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h
@@ -0,0 +1,90 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferQueueProducer.h
+
+#pragma once
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+
+#include "common/common_funcs.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+#include "core/hle/service/nvflinger/binder.h"
+#include "core/hle/service/nvflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvflinger/buffer_slot.h"
+#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
+#include "core/hle/service/nvflinger/status.h"
+#include "core/hle/service/nvflinger/window.h"
+
+namespace Kernel {
+class KernelCore;
+class KEvent;
+class KReadableEvent;
+class KWritableEvent;
+} // namespace Kernel
+
+namespace Service::KernelHelpers {
+class ServiceContext;
+} // namespace Service::KernelHelpers
+
+namespace Service::Nvidia::NvCore {
+class NvMap;
+} // namespace Service::Nvidia::NvCore
+
+namespace Service::android {
+
+class BufferQueueCore;
+class IProducerListener;
+
+class BufferQueueProducer final : public IBinder {
+public:
+ explicit BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
+ std::shared_ptr<BufferQueueCore> buffer_queue_core_,
+ Service::Nvidia::NvCore::NvMap& nvmap_);
+ ~BufferQueueProducer();
+
+ void Transact(Kernel::HLERequestContext& ctx, android::TransactionId code, u32 flags) override;
+
+ Kernel::KReadableEvent& GetNativeHandle() override;
+
+public:
+ Status RequestBuffer(s32 slot, std::shared_ptr<GraphicBuffer>* buf);
+ Status SetBufferCount(s32 buffer_count);
+ Status DequeueBuffer(s32* out_slot, android::Fence* out_fence, bool async, u32 width,
+ u32 height, PixelFormat format, u32 usage);
+ Status DetachBuffer(s32 slot);
+ Status DetachNextBuffer(std::shared_ptr<GraphicBuffer>* out_buffer, Fence* out_fence);
+ Status AttachBuffer(s32* outSlot, const std::shared_ptr<GraphicBuffer>& buffer);
+ Status QueueBuffer(s32 slot, const QueueBufferInput& input, QueueBufferOutput* output);
+ void CancelBuffer(s32 slot, const Fence& fence);
+ Status Query(NativeWindow what, s32* out_value);
+ Status Connect(const std::shared_ptr<IProducerListener>& listener, NativeWindowApi api,
+ bool producer_controlled_by_app, QueueBufferOutput* output);
+
+ Status Disconnect(NativeWindowApi api);
+ Status SetPreallocatedBuffer(s32 slot, const std::shared_ptr<GraphicBuffer>& buffer);
+
+private:
+ BufferQueueProducer(const BufferQueueProducer&) = delete;
+
+ Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const;
+
+ Kernel::KEvent* buffer_wait_event{};
+ Service::KernelHelpers::ServiceContext& service_context;
+
+ std::shared_ptr<BufferQueueCore> core;
+ BufferQueueDefs::SlotsType& slots;
+ u32 sticky_transform{};
+ std::mutex callback_mutex;
+ s32 next_callback_ticket{};
+ s32 current_callback_ticket{};
+ std::condition_variable_any callback_condition;
+
+ Service::Nvidia::NvCore::NvMap& nvmap;
+};
+
+} // 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..0cd0e9964
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_slot.h
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/BufferSlot.h
+
+#pragma once
+
+#include <memory>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/ui/fence.h"
+
+namespace Service::android {
+
+class GraphicBuffer;
+
+enum class BufferState : u32 {
+ Free = 0,
+ Dequeued = 1,
+ Queued = 2,
+ Acquired = 3,
+};
+
+struct BufferSlot final {
+ constexpr BufferSlot() = default;
+
+ std::shared_ptr<GraphicBuffer> graphic_buffer;
+ BufferState buffer_state{BufferState::Free};
+ bool request_buffer_called{};
+ u64 frame_number{};
+ Fence fence;
+ bool acquire_called{};
+ bool attached_by_consumer{};
+ bool is_preallocated{};
+};
+
+} // 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..67aa5dad6
--- /dev/null
+++ b/src/core/hle/service/nvflinger/buffer_transform_flags.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Service::android {
+
+enum class BufferTransformFlags : u32 {
+ /// No transform flags are set
+ Unset = 0x00,
+ /// Flip source image horizontally (around the vertical axis)
+ FlipH = 0x01,
+ /// Flip source image vertically (around the horizontal axis)
+ FlipV = 0x02,
+ /// Rotate source image 90 degrees clockwise
+ Rotate90 = 0x04,
+ /// Rotate source image 180 degrees
+ Rotate180 = 0x03,
+ /// Rotate source image 270 degrees clockwise
+ Rotate270 = 0x07,
+};
+
+} // 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..5b9995854
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.cpp
@@ -0,0 +1,133 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/ConsumerBase.cpp
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/hle/service/nvflinger/buffer_item.h"
+#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/consumer_base.h"
+#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
+
+namespace Service::android {
+
+ConsumerBase::ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_)
+ : consumer{std::move(consumer_)} {}
+
+ConsumerBase::~ConsumerBase() {
+ std::scoped_lock lock{mutex};
+
+ ASSERT_MSG(is_abandoned, "consumer is not abandoned!");
+}
+
+void ConsumerBase::Connect(bool controlled_by_app) {
+ consumer->Connect(shared_from_this(), controlled_by_app);
+}
+
+void ConsumerBase::FreeBufferLocked(s32 slot_index) {
+ LOG_DEBUG(Service_NVFlinger, "slot_index={}", slot_index);
+
+ slots[slot_index].graphic_buffer = nullptr;
+ slots[slot_index].fence = Fence::NoFence();
+ slots[slot_index].frame_number = 0;
+}
+
+void ConsumerBase::OnFrameAvailable(const BufferItem& item) {
+ LOG_DEBUG(Service_NVFlinger, "called");
+}
+
+void ConsumerBase::OnFrameReplaced(const BufferItem& item) {
+ LOG_DEBUG(Service_NVFlinger, "called");
+}
+
+void ConsumerBase::OnBuffersReleased() {
+ std::scoped_lock lock{mutex};
+
+ LOG_DEBUG(Service_NVFlinger, "called");
+
+ if (is_abandoned) {
+ // Nothing to do if we're already abandoned.
+ return;
+ }
+
+ u64 mask = 0;
+ consumer->GetReleasedBuffers(&mask);
+ for (int i = 0; i < BufferQueueDefs::NUM_BUFFER_SLOTS; i++) {
+ if (mask & (1ULL << i)) {
+ FreeBufferLocked(i);
+ }
+ }
+}
+
+void ConsumerBase::OnSidebandStreamChanged() {}
+
+Status ConsumerBase::AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when) {
+ Status err = consumer->AcquireBuffer(item, present_when);
+ if (err != Status::NoError) {
+ return err;
+ }
+
+ if (item->graphic_buffer != nullptr) {
+ slots[item->slot].graphic_buffer = item->graphic_buffer;
+ }
+
+ slots[item->slot].frame_number = item->frame_number;
+ slots[item->slot].fence = item->fence;
+
+ LOG_DEBUG(Service_NVFlinger, "slot={}", item->slot);
+
+ return Status::NoError;
+}
+
+Status ConsumerBase::AddReleaseFenceLocked(s32 slot,
+ const std::shared_ptr<GraphicBuffer> graphic_buffer,
+ const Fence& fence) {
+ LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
+
+ // If consumer no longer tracks this graphic_buffer, we can safely
+ // drop this fence, as it will never be received by the producer.
+
+ if (!StillTracking(slot, graphic_buffer)) {
+ return Status::NoError;
+ }
+
+ slots[slot].fence = fence;
+
+ return Status::NoError;
+}
+
+Status ConsumerBase::ReleaseBufferLocked(s32 slot,
+ const std::shared_ptr<GraphicBuffer> graphic_buffer) {
+ // If consumer no longer tracks this graphic_buffer (we received a new
+ // buffer on the same slot), the buffer producer is definitely no longer
+ // tracking it.
+
+ if (!StillTracking(slot, graphic_buffer)) {
+ return Status::NoError;
+ }
+
+ LOG_DEBUG(Service_NVFlinger, "slot={}", slot);
+ Status err = consumer->ReleaseBuffer(slot, slots[slot].frame_number, slots[slot].fence);
+ if (err == Status::StaleBufferSlot) {
+ FreeBufferLocked(slot);
+ }
+
+ slots[slot].fence = Fence::NoFence();
+
+ return err;
+}
+
+bool ConsumerBase::StillTracking(s32 slot,
+ const std::shared_ptr<GraphicBuffer> graphic_buffer) const {
+ if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ return false;
+ }
+
+ return (slots[slot].graphic_buffer != nullptr &&
+ slots[slot].graphic_buffer->Handle() == graphic_buffer->Handle());
+}
+
+} // 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..90ba07f45
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_base.h
@@ -0,0 +1,60 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/ConsumerBase.h
+
+#pragma once
+
+#include <array>
+#include <chrono>
+#include <memory>
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/buffer_queue_defs.h"
+#include "core/hle/service/nvflinger/consumer_listener.h"
+#include "core/hle/service/nvflinger/status.h"
+
+namespace Service::android {
+
+class BufferItem;
+class BufferQueueConsumer;
+
+class ConsumerBase : public IConsumerListener, public std::enable_shared_from_this<ConsumerBase> {
+public:
+ void Connect(bool controlled_by_app);
+
+protected:
+ explicit ConsumerBase(std::unique_ptr<BufferQueueConsumer> consumer_);
+ virtual ~ConsumerBase();
+
+ virtual void OnFrameAvailable(const BufferItem& item) override;
+ virtual void OnFrameReplaced(const BufferItem& item) override;
+ virtual void OnBuffersReleased() override;
+ virtual void OnSidebandStreamChanged() override;
+
+ void FreeBufferLocked(s32 slot_index);
+ Status AcquireBufferLocked(BufferItem* item, std::chrono::nanoseconds present_when);
+ Status ReleaseBufferLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer);
+ bool StillTracking(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer) const;
+ Status AddReleaseFenceLocked(s32 slot, const std::shared_ptr<GraphicBuffer> graphic_buffer,
+ const Fence& fence);
+
+ struct Slot final {
+ std::shared_ptr<GraphicBuffer> graphic_buffer;
+ Fence fence;
+ u64 frame_number{};
+ };
+
+protected:
+ std::array<Slot, BufferQueueDefs::NUM_BUFFER_SLOTS> slots;
+
+ bool is_abandoned{};
+
+ std::unique_ptr<BufferQueueConsumer> consumer;
+
+ mutable std::mutex mutex;
+};
+
+} // 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..74a193988
--- /dev/null
+++ b/src/core/hle/service/nvflinger/consumer_listener.h
@@ -0,0 +1,26 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IConsumerListener.h
+
+#pragma once
+
+namespace Service::android {
+
+class BufferItem;
+
+/// ConsumerListener is the interface through which the BufferQueue notifies the consumer of events
+/// that the consumer may wish to react to.
+class IConsumerListener {
+public:
+ IConsumerListener() = default;
+ virtual ~IConsumerListener() = default;
+
+ virtual void OnFrameAvailable(const BufferItem& item) = 0;
+ virtual void OnFrameReplaced(const BufferItem& item) = 0;
+ virtual void OnBuffersReleased() = 0;
+ virtual void OnSidebandStreamChanged() = 0;
+};
+
+}; // 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..4043c91f1
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.cpp
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp
+
+#include "core/hle/service/nvflinger/graphic_buffer_producer.h"
+#include "core/hle/service/nvflinger/parcel.h"
+
+namespace Service::android {
+
+QueueBufferInput::QueueBufferInput(Parcel& parcel) {
+ parcel.ReadFlattened(*this);
+}
+
+QueueBufferOutput::QueueBufferOutput() = default;
+
+} // 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..6ea327bbe
--- /dev/null
+++ b/src/core/hle/service/nvflinger/graphic_buffer_producer.h
@@ -0,0 +1,76 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2010 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/math_util.h"
+#include "core/hle/service/nvflinger/ui/fence.h"
+#include "core/hle/service/nvflinger/window.h"
+
+namespace Service::android {
+
+class Parcel;
+
+#pragma pack(push, 1)
+struct QueueBufferInput final {
+ explicit QueueBufferInput(Parcel& parcel);
+
+ void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
+ NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
+ u32* sticky_transform_, bool* async_, s32* swap_interval_, Fence* fence_) const {
+ *timestamp_ = timestamp;
+ *is_auto_timestamp_ = static_cast<bool>(is_auto_timestamp);
+ *crop_ = crop;
+ *scaling_mode_ = scaling_mode;
+ *transform_ = transform;
+ *sticky_transform_ = sticky_transform;
+ *async_ = static_cast<bool>(async);
+ *swap_interval_ = swap_interval;
+ *fence_ = fence;
+ }
+
+private:
+ s64 timestamp{};
+ s32 is_auto_timestamp{};
+ Common::Rectangle<s32> crop{};
+ NativeWindowScalingMode scaling_mode{};
+ NativeWindowTransform transform{};
+ u32 sticky_transform{};
+ s32 async{};
+ s32 swap_interval{};
+ Fence fence{};
+};
+#pragma pack(pop)
+static_assert(sizeof(QueueBufferInput) == 84, "QueueBufferInput has wrong size");
+
+struct QueueBufferOutput final {
+ QueueBufferOutput();
+
+ void Deflate(u32* width_, u32* height_, u32* transform_hint_, u32* num_pending_buffers_) const {
+ *width_ = width;
+ *height_ = height;
+ *transform_hint_ = transform_hint;
+ *num_pending_buffers_ = num_pending_buffers;
+ }
+
+ void Inflate(u32 width_, u32 height_, u32 transform_hint_, u32 num_pending_buffers_) {
+ width = width_;
+ height = height_;
+ transform_hint = transform_hint_;
+ num_pending_buffers = num_pending_buffers_;
+ }
+
+private:
+ u32 width{};
+ u32 height{};
+ u32 transform_hint{};
+ u32 num_pending_buffers{};
+};
+static_assert(sizeof(QueueBufferOutput) == 16, "QueueBufferOutput has wrong size");
+
+} // 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..dc9b2a9ec
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.cpp
@@ -0,0 +1,36 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <mutex>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
+
+namespace Service::NVFlinger {
+
+HosBinderDriverServer::HosBinderDriverServer(Core::System& system_)
+ : service_context(system_, "HosBinderDriverServer") {}
+
+HosBinderDriverServer::~HosBinderDriverServer() {}
+
+u64 HosBinderDriverServer::RegisterProducer(std::unique_ptr<android::IBinder>&& binder) {
+ std::scoped_lock lk{lock};
+
+ last_id++;
+
+ producers[last_id] = std::move(binder);
+
+ return last_id;
+}
+
+android::IBinder* HosBinderDriverServer::TryGetProducer(u64 id) {
+ std::scoped_lock lk{lock};
+
+ if (auto search = producers.find(id); search != producers.end()) {
+ return search->second.get();
+ }
+
+ return {};
+}
+
+} // 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..8fddc1206
--- /dev/null
+++ b/src/core/hle/service/nvflinger/hos_binder_driver_server.h
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+
+#include "common/common_types.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/nvflinger/binder.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::NVFlinger {
+
+class HosBinderDriverServer final {
+public:
+ explicit HosBinderDriverServer(Core::System& system_);
+ ~HosBinderDriverServer();
+
+ u64 RegisterProducer(std::unique_ptr<android::IBinder>&& binder);
+
+ android::IBinder* TryGetProducer(u64 id);
+
+private:
+ KernelHelpers::ServiceContext service_context;
+
+ std::unordered_map<u64, std::unique_ptr<android::IBinder>> producers;
+ std::mutex lock;
+ u64 last_id{};
+};
+
+} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 01e69de30..aa14d2cbc 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include <optional>
@@ -16,11 +15,17 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
-#include "core/hle/service/nvflinger/buffer_queue.h"
+#include "core/hle/service/nvflinger/buffer_item_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvflinger/nvflinger.h"
+#include "core/hle/service/nvflinger/ui/graphic_buffer.h"
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
+#include "core/hle/service/vi/vi_results.h"
#include "video_core/gpu.h"
+#include "video_core/host1x/host1x.h"
+#include "video_core/host1x/syncpoint_manager.h"
namespace Service::NVFlinger {
@@ -28,7 +33,7 @@ constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60};
void NVFlinger::SplitVSync(std::stop_token stop_token) {
system.RegisterHostThread();
- std::string name = "yuzu:VSyncThread";
+ std::string name = "VSyncThread";
MicroProfileOnThreadCreate(name.c_str());
// Cleanup
@@ -36,69 +41,87 @@ void NVFlinger::SplitVSync(std::stop_token stop_token) {
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
- s64 delay = 0;
+
while (!stop_token.stop_requested()) {
+ vsync_signal.wait(false);
+ vsync_signal.store(false);
+
guard->lock();
- const s64 time_start = system.CoreTiming().GetGlobalTimeNs().count();
+
Compose();
- const auto ticks = GetNextTicks();
- const s64 time_end = system.CoreTiming().GetGlobalTimeNs().count();
- const s64 time_passed = time_end - time_start;
- const s64 next_time = std::max<s64>(0, ticks - time_passed - delay);
+
guard->unlock();
- if (next_time > 0) {
- std::this_thread::sleep_for(std::chrono::nanoseconds{next_time});
- }
- delay = (system.CoreTiming().GetGlobalTimeNs().count() - time_end) - next_time;
}
}
-NVFlinger::NVFlinger(Core::System& system_)
- : system(system_), service_context(system_, "nvflinger") {
- displays.emplace_back(0, "Default", service_context, system);
- displays.emplace_back(1, "External", service_context, system);
- displays.emplace_back(2, "Edid", service_context, system);
- displays.emplace_back(3, "Internal", service_context, system);
- displays.emplace_back(4, "Null", service_context, system);
+NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_)
+ : system(system_), service_context(system_, "nvflinger"),
+ hos_binder_driver_server(hos_binder_driver_server_) {
+ displays.emplace_back(0, "Default", hos_binder_driver_server, service_context, system);
+ displays.emplace_back(1, "External", hos_binder_driver_server, service_context, system);
+ displays.emplace_back(2, "Edid", hos_binder_driver_server, service_context, system);
+ displays.emplace_back(3, "Internal", hos_binder_driver_server, service_context, system);
+ displays.emplace_back(4, "Null", hos_binder_driver_server, service_context, system);
guard = std::make_shared<std::mutex>();
// Schedule the screen composition events
- composition_event = Core::Timing::CreateEvent(
- "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
+ multi_composition_event = Core::Timing::CreateEvent(
+ "ScreenComposition",
+ [this](std::uintptr_t, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
+ vsync_signal.store(true);
+ vsync_signal.notify_all();
+ return std::chrono::nanoseconds(GetNextTicks());
+ });
+
+ single_composition_event = Core::Timing::CreateEvent(
+ "ScreenComposition",
+ [this](std::uintptr_t, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
const auto lock_guard = Lock();
Compose();
- const auto ticks = std::chrono::nanoseconds{GetNextTicks()};
- const auto ticks_delta = ticks - ns_late;
- const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
-
- this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
+ return std::chrono::nanoseconds(GetNextTicks());
});
if (system.IsMulticore()) {
+ system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, multi_composition_event);
vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
} else {
- system.CoreTiming().ScheduleEvent(frame_ns, composition_event);
+ system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, single_composition_event);
}
}
NVFlinger::~NVFlinger() {
- for (auto& buffer_queue : buffer_queues) {
- buffer_queue->Disconnect();
+ if (system.IsMulticore()) {
+ system.CoreTiming().UnscheduleEvent(multi_composition_event, {});
+ vsync_thread.request_stop();
+ vsync_signal.store(true);
+ vsync_signal.notify_all();
+ } else {
+ system.CoreTiming().UnscheduleEvent(single_composition_event, {});
}
- if (!system.IsMulticore()) {
- system.CoreTiming().UnscheduleEvent(composition_event, 0);
+
+ for (auto& display : displays) {
+ for (size_t layer = 0; layer < display.GetNumLayers(); ++layer) {
+ display.GetLayer(layer).Core().NotifyShutdown();
+ }
+ }
+
+ if (nvdrv) {
+ nvdrv->Close(disp_fd);
}
}
void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
nvdrv = std::move(instance);
+ disp_fd = nvdrv->Open("/dev/nvdisp_disp0");
}
std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
const auto lock_guard = Lock();
- LOG_DEBUG(Service, "Opening \"{}\" display", name);
+ LOG_DEBUG(Service_NVFlinger, "Opening \"{}\" display", name);
const auto itr =
std::find_if(displays.begin(), displays.end(),
@@ -125,10 +148,8 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
}
void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
- const u32 buffer_queue_id = next_buffer_queue_id++;
- buffer_queues.emplace_back(
- std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
- display.CreateLayer(layer_id, *buffer_queues.back());
+ const auto buffer_id = next_buffer_queue_id++;
+ display.CreateLayer(layer_id, buffer_id, nvdrv->container);
}
void NVFlinger::CloseLayer(u64 layer_id) {
@@ -147,30 +168,18 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) {
return std::nullopt;
}
- return layer->GetBufferQueue().GetId();
+ return layer->GetBinderId();
}
-Kernel::KReadableEvent* NVFlinger::FindVsyncEvent(u64 display_id) {
+ResultVal<Kernel::KReadableEvent*> NVFlinger::FindVsyncEvent(u64 display_id) {
const auto lock_guard = Lock();
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
- return nullptr;
+ return VI::ResultNotFound;
}
- return &display->GetVSyncEvent();
-}
-
-BufferQueue* NVFlinger::FindBufferQueue(u32 id) {
- const auto lock_guard = Lock();
- const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
- [id](const auto& queue) { return queue->GetId() == id; });
-
- if (itr == buffer_queues.end()) {
- return nullptr;
- }
-
- return itr->get();
+ return display->GetVSyncEvent();
}
VI::Display* NVFlinger::FindDisplay(u64 display_id) {
@@ -227,7 +236,7 @@ VI::Layer* NVFlinger::FindOrCreateLayer(u64 display_id, u64 layer_id) {
auto* layer = display->FindLayer(layer_id);
if (layer == nullptr) {
- LOG_DEBUG(Service, "Layer at id {} not found. Trying to create it.", layer_id);
+ LOG_DEBUG(Service_NVFlinger, "Layer at id {} not found. Trying to create it.", layer_id);
CreateLayerAtId(*display, layer_id);
return display->FindLayer(layer_id);
}
@@ -246,44 +255,43 @@ void NVFlinger::Compose() {
// TODO(Subv): Support more than 1 layer.
VI::Layer& layer = display.GetLayer(0);
- auto& buffer_queue = layer.GetBufferQueue();
- // Search for a queued buffer and acquire it
- auto buffer = buffer_queue.AcquireBuffer();
+ android::BufferItem buffer{};
+ const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false);
- if (!buffer) {
+ if (status != android::Status::NoError) {
continue;
}
- const auto& igbp_buffer = buffer->get().igbp_buffer;
+ const auto& igbp_buffer = *buffer.graphic_buffer;
if (!system.IsPoweredOn()) {
return; // We are likely shutting down
}
- auto& gpu = system.GPU();
- const auto& multi_fence = buffer->get().multi_fence;
- guard->unlock();
- for (u32 fence_id = 0; fence_id < multi_fence.num_fences; fence_id++) {
- const auto& fence = multi_fence.fences[fence_id];
- gpu.WaitFence(fence.id, fence.value);
- }
- guard->lock();
-
- MicroProfileFlip();
-
// Now send the buffer to the GPU for drawing.
// TODO(Subv): Support more than just disp0. The display device selection is probably based
// on which display we're drawing (Default, Internal, External, etc)
- auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
+ auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd);
ASSERT(nvdisp);
- nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format,
- igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
- buffer->get().transform, buffer->get().crop_rect);
+ guard->unlock();
+ Common::Rectangle<int> crop_rect{
+ static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()),
+ static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())};
- swap_interval = buffer->get().swap_interval;
- buffer_queue.ReleaseBuffer(buffer->get().slot);
+ nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(),
+ igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(),
+ static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect,
+ buffer.fence.fences, buffer.fence.num_fences);
+
+ MicroProfileFlip();
+ guard->lock();
+
+ swap_interval = buffer.swap_interval;
+
+ auto fence = android::Fence::NoFence();
+ layer.GetConsumer().ReleaseBuffer(buffer, fence);
}
}
@@ -291,9 +299,21 @@ s64 NVFlinger::GetNextTicks() const {
static constexpr s64 max_hertz = 120LL;
const auto& settings = Settings::values;
- const bool unlocked_fps = settings.disable_fps_limit.GetValue();
- const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1;
- return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap);
+ auto speed_scale = 1.f;
+ if (settings.use_multi_core.GetValue()) {
+ if (settings.use_speed_limit.GetValue()) {
+ // Scales the speed based on speed_limit setting on MC. SC is handled by
+ // SpeedLimiter::DoSpeedLimiting.
+ speed_scale = 100.f / settings.speed_limit.GetValue();
+ } else {
+ // Run at unlocked framerate.
+ speed_scale = 0.01f;
+ }
+ }
+
+ const auto next_ticks = ((1000000000 * (1LL << swap_interval)) / max_hertz);
+
+ return static_cast<s64>(speed_scale * static_cast<float>(next_ticks));
}
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 7935cf773..b62615de2 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@@ -12,6 +11,7 @@
#include <vector>
#include "common/common_types.h"
+#include "core/hle/result.h"
#include "core/hle/service/kernel_helpers.h"
namespace Common {
@@ -37,13 +37,16 @@ class Display;
class Layer;
} // namespace Service::VI
-namespace Service::NVFlinger {
+namespace Service::android {
+class BufferQueueCore;
+class BufferQueueProducer;
+} // namespace Service::android
-class BufferQueue;
+namespace Service::NVFlinger {
class NVFlinger final {
public:
- explicit NVFlinger(Core::System& system_);
+ explicit NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
~NVFlinger();
/// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -69,11 +72,9 @@ public:
/// Gets the vsync event for the specified display.
///
- /// If an invalid display ID is provided, then nullptr is returned.
- [[nodiscard]] Kernel::KReadableEvent* FindVsyncEvent(u64 display_id);
-
- /// Obtains a buffer queue identified by the ID.
- [[nodiscard]] BufferQueue* FindBufferQueue(u32 id);
+ /// If an invalid display ID is provided, then VI::ResultNotFound is returned.
+ /// If the vsync event has already been retrieved, then VI::ResultPermissionDenied is returned.
+ [[nodiscard]] ResultVal<Kernel::KReadableEvent*> FindVsyncEvent(u64 display_id);
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
/// finished.
@@ -82,6 +83,12 @@ public:
[[nodiscard]] s64 GetNextTicks() const;
private:
+ struct Layer {
+ std::unique_ptr<android::BufferQueueCore> core;
+ std::unique_ptr<android::BufferQueueProducer> producer;
+ };
+
+private:
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
return std::unique_lock{*guard};
}
@@ -109,9 +116,9 @@ private:
void SplitVSync(std::stop_token stop_token);
std::shared_ptr<Nvidia::Module> nvdrv;
+ s32 disp_fd;
std::list<VI::Display> displays;
- std::vector<std::unique_ptr<BufferQueue>> buffer_queues;
/// Id to use for the next layer that is created, this counter is shared among all displays.
u64 next_layer_id = 1;
@@ -122,15 +129,20 @@ private:
u32 swap_interval = 1;
/// Event that handles screen composition.
- std::shared_ptr<Core::Timing::EventType> composition_event;
+ std::shared_ptr<Core::Timing::EventType> multi_composition_event;
+ std::shared_ptr<Core::Timing::EventType> single_composition_event;
std::shared_ptr<std::mutex> guard;
Core::System& system;
+ std::atomic<bool> vsync_signal;
+
std::jthread vsync_thread;
KernelHelpers::ServiceContext service_context;
+
+ HosBinderDriverServer& hos_binder_driver_server;
};
} // 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..f3fa2587d
--- /dev/null
+++ b/src/core/hle/service/nvflinger/parcel.h
@@ -0,0 +1,172 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/common_types.h"
+
+namespace Service::android {
+
+class Parcel final {
+public:
+ static constexpr std::size_t DefaultBufferSize = 0x40;
+
+ Parcel() : buffer(DefaultBufferSize) {}
+
+ template <typename T>
+ explicit Parcel(const T& out_data) : buffer(DefaultBufferSize) {
+ Write(out_data);
+ }
+
+ explicit Parcel(std::vector<u8> in_data) : buffer(std::move(in_data)) {
+ DeserializeHeader();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
+ }
+
+ template <typename T>
+ void Read(T& val) {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+ ASSERT(read_index + sizeof(T) <= buffer.size());
+
+ std::memcpy(&val, buffer.data() + read_index, sizeof(T));
+ read_index += sizeof(T);
+ read_index = Common::AlignUp(read_index, 4);
+ }
+
+ template <typename T>
+ T Read() {
+ T val;
+ Read(val);
+ return val;
+ }
+
+ template <typename T>
+ void ReadFlattened(T& val) {
+ const auto flattened_size = Read<s64>();
+ ASSERT(sizeof(T) == flattened_size);
+ Read(val);
+ }
+
+ template <typename T>
+ T ReadFlattened() {
+ T val;
+ ReadFlattened(val);
+ return val;
+ }
+
+ template <typename T>
+ T ReadUnaligned() {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+ ASSERT(read_index + sizeof(T) <= buffer.size());
+
+ T val;
+ std::memcpy(&val, buffer.data() + read_index, sizeof(T));
+ read_index += sizeof(T);
+ return val;
+ }
+
+ template <typename T>
+ const std::shared_ptr<T> ReadObject() {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+
+ const auto is_valid{Read<bool>()};
+
+ if (is_valid) {
+ auto result = std::make_shared<T>();
+ ReadFlattened(*result);
+ return result;
+ }
+
+ return {};
+ }
+
+ std::u16string ReadInterfaceToken() {
+ [[maybe_unused]] const u32 unknown = Read<u32>();
+ const u32 length = Read<u32>();
+
+ std::u16string token;
+ token.reserve(length + 1);
+
+ for (u32 ch = 0; ch < length + 1; ++ch) {
+ token.push_back(ReadUnaligned<u16>());
+ }
+
+ read_index = Common::AlignUp(read_index, 4);
+
+ return token;
+ }
+
+ template <typename T>
+ void Write(const T& val) {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+
+ if (buffer.size() < write_index + sizeof(T)) {
+ buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
+ }
+
+ std::memcpy(buffer.data() + write_index, &val, sizeof(T));
+ write_index += sizeof(T);
+ write_index = Common::AlignUp(write_index, 4);
+ }
+
+ template <typename T>
+ void WriteObject(const T* ptr) {
+ static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
+
+ if (!ptr) {
+ Write<u32>(0);
+ return;
+ }
+
+ Write<u32>(1);
+ Write<s64>(sizeof(T));
+ Write(*ptr);
+ }
+
+ template <typename T>
+ void WriteObject(const std::shared_ptr<T> ptr) {
+ WriteObject(ptr.get());
+ }
+
+ void DeserializeHeader() {
+ ASSERT(buffer.size() > sizeof(Header));
+
+ Header header{};
+ std::memcpy(&header, buffer.data(), sizeof(Header));
+
+ read_index = header.data_offset;
+ }
+
+ std::vector<u8> Serialize() const {
+ ASSERT(read_index == 0);
+
+ Header header{};
+ header.data_size = static_cast<u32>(write_index - sizeof(Header));
+ header.data_offset = sizeof(Header);
+ header.objects_size = 4;
+ header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
+ std::memcpy(buffer.data(), &header, sizeof(Header));
+
+ return buffer;
+ }
+
+private:
+ struct Header {
+ u32 data_size;
+ u32 data_offset;
+ u32 objects_size;
+ u32 objects_offset;
+ };
+ static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
+
+ mutable std::vector<u8> buffer;
+ std::size_t read_index = 0;
+ std::size_t write_index = sizeof(Header);
+};
+
+} // 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..f77d0acfb
--- /dev/null
+++ b/src/core/hle/service/nvflinger/pixel_format.h
@@ -0,0 +1,21 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace Service::android {
+
+enum class PixelFormat : u32 {
+ NoFormat = 0,
+ Rgba8888 = 1,
+ Rgbx8888 = 2,
+ Rgb888 = 3,
+ Rgb565 = 4,
+ Bgra8888 = 5,
+ Rgba5551 = 6,
+ Rgba4444 = 7,
+};
+
+} // 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..1c4d5db0e
--- /dev/null
+++ b/src/core/hle/service/nvflinger/producer_listener.h
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2014 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IProducerListener.h
+
+#pragma once
+
+namespace Service::android {
+
+class IProducerListener {
+public:
+ virtual void OnBufferReleased() = 0;
+};
+
+} // 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..7af166c40
--- /dev/null
+++ b/src/core/hle/service/nvflinger/status.h
@@ -0,0 +1,28 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Service::android {
+
+enum class Status : s32 {
+ None = 0,
+ NoError = 0,
+ StaleBufferSlot = 1,
+ NoBufferAvailable = 2,
+ PresentLater = 3,
+ WouldBlock = -11,
+ NoMemory = -12,
+ Busy = -16,
+ NoInit = -19,
+ BadValue = -22,
+ InvalidOperation = -37,
+ BufferNeedsReallocation = 1,
+ ReleaseAllBuffers = 2,
+};
+DECLARE_ENUM_FLAG_OPERATORS(Status);
+
+} // 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..536e8156d
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/fence.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2012 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/Fence.h
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+
+namespace Service::android {
+
+class Fence {
+public:
+ constexpr Fence() = default;
+
+ static constexpr Fence NoFence() {
+ Fence fence;
+ fence.fences[0].id = -1;
+ return fence;
+ }
+
+public:
+ u32 num_fences{};
+ std::array<Service::Nvidia::NvFence, 4> fences{};
+};
+static_assert(sizeof(Fence) == 36, "Fence has wrong size");
+
+} // 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..9a27f8f02
--- /dev/null
+++ b/src/core/hle/service/nvflinger/ui/graphic_buffer.h
@@ -0,0 +1,100 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-FileCopyrightText: Copyright 2007 The Android Open Source Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+// Parts of this implementation were based on:
+// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/ui/GraphicBuffer.h
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/service/nvflinger/pixel_format.h"
+
+namespace Service::android {
+
+class GraphicBuffer final {
+public:
+ constexpr GraphicBuffer() = default;
+
+ constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
+ : width{static_cast<s32>(width_)}, height{static_cast<s32>(height_)}, format{format_},
+ usage{static_cast<s32>(usage_)} {}
+
+ constexpr u32 Width() const {
+ return static_cast<u32>(width);
+ }
+
+ constexpr u32 Height() const {
+ return static_cast<u32>(height);
+ }
+
+ constexpr u32 Stride() const {
+ return static_cast<u32>(stride);
+ }
+
+ constexpr u32 Usage() const {
+ return static_cast<u32>(usage);
+ }
+
+ constexpr PixelFormat Format() const {
+ return format;
+ }
+
+ constexpr u32 BufferId() const {
+ return buffer_id;
+ }
+
+ constexpr PixelFormat ExternalFormat() const {
+ return external_format;
+ }
+
+ constexpr u32 Handle() const {
+ return handle;
+ }
+
+ constexpr u32 Offset() const {
+ return offset;
+ }
+
+ constexpr bool NeedsReallocation(u32 width_, u32 height_, PixelFormat format_,
+ u32 usage_) const {
+ if (static_cast<s32>(width_) != width) {
+ return true;
+ }
+
+ if (static_cast<s32>(height_) != height) {
+ return true;
+ }
+
+ if (format_ != format) {
+ return true;
+ }
+
+ if ((static_cast<u32>(usage) & usage_) != usage_) {
+ return true;
+ }
+
+ return false;
+ }
+
+private:
+ u32 magic{};
+ s32 width{};
+ s32 height{};
+ s32 stride{};
+ PixelFormat format{};
+ s32 usage{};
+ INSERT_PADDING_WORDS(1);
+ u32 index{};
+ INSERT_PADDING_WORDS(3);
+ u32 buffer_id{};
+ INSERT_PADDING_WORDS(6);
+ PixelFormat external_format{};
+ INSERT_PADDING_WORDS(10);
+ u32 handle{};
+ u32 offset{};
+ INSERT_PADDING_WORDS(60);
+};
+static_assert(sizeof(GraphicBuffer) == 0x16C, "GraphicBuffer has wrong size");
+
+} // 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..61cca5b01
--- /dev/null
+++ b/src/core/hle/service/nvflinger/window.h
@@ -0,0 +1,53 @@
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Service::android {
+
+/// Attributes queryable with Query
+enum class NativeWindow : s32 {
+ Width = 0,
+ Height = 1,
+ Format = 2,
+ MinUndequeedBuffers = 3,
+ QueuesToWindowComposer = 4,
+ ConcreteType = 5,
+ DefaultWidth = 6,
+ DefaultHeight = 7,
+ TransformHint = 8,
+ ConsumerRunningBehind = 9,
+ ConsumerUsageBits = 10,
+ StickyTransform = 11,
+ DefaultDataSpace = 12,
+ BufferAge = 13,
+};
+
+/// Parameter for Connect/Disconnect
+enum class NativeWindowApi : s32 {
+ NoConnectedApi = 0,
+ Egl = 1,
+ Cpu = 2,
+ Media = 3,
+ Camera = 4,
+};
+
+/// Scaling mode parameter for QueueBuffer
+enum class NativeWindowScalingMode : s32 {
+ Freeze = 0,
+ ScaleToWindow = 1,
+ ScaleCrop = 2,
+ NoScaleCrop = 3,
+};
+
+/// Transform parameter for QueueBuffer
+enum class NativeWindowTransform : u32 {
+ None = 0x0,
+ InverseDisplay = 0x08,
+};
+DECLARE_ENUM_FLAG_OPERATORS(NativeWindowTransform);
+
+} // namespace Service::android
diff --git a/src/core/hle/service/olsc/olsc.cpp b/src/core/hle/service/olsc/olsc.cpp
index 39a8031a5..530e1be3b 100644
--- a/src/core/hle/service/olsc/olsc.cpp
+++ b/src/core/hle/service/olsc/olsc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/olsc/olsc.h"
diff --git a/src/core/hle/service/olsc/olsc.h b/src/core/hle/service/olsc/olsc.h
index 24f24ca6b..1522d8d32 100644
--- a/src/core/hle/service/olsc/olsc.h
+++ b/src/core/hle/service/olsc/olsc.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/pcie/pcie.cpp b/src/core/hle/service/pcie/pcie.cpp
index 9bc851591..79501b9f9 100644
--- a/src/core/hle/service/pcie/pcie.cpp
+++ b/src/core/hle/service/pcie/pcie.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/pcie/pcie.h b/src/core/hle/service/pcie/pcie.h
index e5709a72f..cebfd9042 100644
--- a/src/core/hle/service/pcie/pcie.h
+++ b/src/core/hle/service/pcie/pcie.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/pctl/pctl.cpp b/src/core/hle/service/pctl/pctl.cpp
index 908e0a1e3..3f47bf094 100644
--- a/src/core/hle/service/pctl/pctl.cpp
+++ b/src/core/hle/service/pctl/pctl.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/pctl/pctl.h"
diff --git a/src/core/hle/service/pctl/pctl.h b/src/core/hle/service/pctl/pctl.h
index 1d28900b2..87f93161e 100644
--- a/src/core/hle/service/pctl/pctl.h
+++ b/src/core/hle/service/pctl/pctl.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp
index 240776101..2a123b42d 100644
--- a/src/core/hle/service/pctl/pctl_module.cpp
+++ b/src/core/hle/service/pctl/pctl_module.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
@@ -14,10 +13,10 @@ namespace Service::PCTL {
namespace Error {
-constexpr ResultCode ResultNoFreeCommunication{ErrorModule::PCTL, 101};
-constexpr ResultCode ResultStereoVisionRestricted{ErrorModule::PCTL, 104};
-constexpr ResultCode ResultNoCapability{ErrorModule::PCTL, 131};
-constexpr ResultCode ResultNoRestrictionEnabled{ErrorModule::PCTL, 181};
+constexpr Result ResultNoFreeCommunication{ErrorModule::PCTL, 101};
+constexpr Result ResultStereoVisionRestricted{ErrorModule::PCTL, 104};
+constexpr Result ResultNoCapability{ErrorModule::PCTL, 131};
+constexpr Result ResultNoRestrictionEnabled{ErrorModule::PCTL, 181};
} // namespace Error
diff --git a/src/core/hle/service/pctl/pctl_module.h b/src/core/hle/service/pctl/pctl_module.h
index f25c5c557..6f584530d 100644
--- a/src/core/hle/service/pctl/pctl_module.h
+++ b/src/core/hle/service/pctl/pctl_module.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
index 68b2c4178..f7a497a14 100644
--- a/src/core/hle/service/pcv/pcv.cpp
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -1,9 +1,9 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/pcv/pcv.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -78,10 +78,102 @@ public:
}
};
+class IClkrstSession final : public ServiceFramework<IClkrstSession> {
+public:
+ explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_)
+ : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "SetClockEnabled"},
+ {1, nullptr, "SetClockDisabled"},
+ {2, nullptr, "SetResetAsserted"},
+ {3, nullptr, "SetResetDeasserted"},
+ {4, nullptr, "SetPowerEnabled"},
+ {5, nullptr, "SetPowerDisabled"},
+ {6, nullptr, "GetState"},
+ {7, &IClkrstSession::SetClockRate, "SetClockRate"},
+ {8, &IClkrstSession::GetClockRate, "GetClockRate"},
+ {9, nullptr, "SetMinVClockRate"},
+ {10, nullptr, "GetPossibleClockRates"},
+ {11, nullptr, "GetDvfsTable"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+ }
+
+private:
+ void SetClockRate(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ clock_rate = rp.Pop<u32>();
+ LOG_DEBUG(Service_PCV, "(STUBBED) called, clock_rate={}", clock_rate);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetClockRate(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PCV, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(clock_rate);
+ }
+
+ DeviceCode deivce_code;
+ u32 clock_rate{};
+};
+
+class CLKRST final : public ServiceFramework<CLKRST> {
+public:
+ explicit CLKRST(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &CLKRST::OpenSession, "OpenSession"},
+ {1, nullptr, "GetTemperatureThresholds"},
+ {2, nullptr, "SetTemperature"},
+ {3, nullptr, "GetModuleStateTable"},
+ {4, nullptr, "GetModuleStateTableEvent"},
+ {5, nullptr, "GetModuleStateTableMaxCount"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void OpenSession(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>());
+ const auto unkonwn_input = rp.Pop<u32>();
+
+ LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IClkrstSession>(system, device_code);
+ }
+};
+
+class CLKRST_A final : public ServiceFramework<CLKRST_A> {
+public:
+ explicit CLKRST_A(Core::System& system_) : ServiceFramework{system_, "clkrst:a"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ReleaseControl"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<PCV>(system)->InstallAsService(sm);
std::make_shared<PCV_ARB>(system)->InstallAsService(sm);
std::make_shared<PCV_IMM>(system)->InstallAsService(sm);
+ std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm);
+ std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm);
+ std::make_shared<CLKRST_A>(system)->InstallAsService(sm);
}
} // namespace Service::PCV
diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h
index c61a0b591..6b26b6fa7 100644
--- a/src/core/hle/service/pcv/pcv.h
+++ b/src/core/hle/service/pcv/pcv.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -14,6 +13,97 @@ class ServiceManager;
namespace Service::PCV {
+enum class DeviceCode : u32 {
+ Cpu = 0x40000001,
+ Gpu = 0x40000002,
+ I2s1 = 0x40000003,
+ I2s2 = 0x40000004,
+ I2s3 = 0x40000005,
+ Pwm = 0x40000006,
+ I2c1 = 0x02000001,
+ I2c2 = 0x02000002,
+ I2c3 = 0x02000003,
+ I2c4 = 0x02000004,
+ I2c5 = 0x02000005,
+ I2c6 = 0x02000006,
+ Spi1 = 0x07000000,
+ Spi2 = 0x07000001,
+ Spi3 = 0x07000002,
+ Spi4 = 0x07000003,
+ Disp1 = 0x40000011,
+ Disp2 = 0x40000012,
+ Isp = 0x40000013,
+ Vi = 0x40000014,
+ Sdmmc1 = 0x40000015,
+ Sdmmc2 = 0x40000016,
+ Sdmmc3 = 0x40000017,
+ Sdmmc4 = 0x40000018,
+ Owr = 0x40000019,
+ Csite = 0x4000001A,
+ Tsec = 0x4000001B,
+ Mselect = 0x4000001C,
+ Hda2codec2x = 0x4000001D,
+ Actmon = 0x4000001E,
+ I2cSlow = 0x4000001F,
+ Sor1 = 0x40000020,
+ Sata = 0x40000021,
+ Hda = 0x40000022,
+ XusbCoreHostSrc = 0x40000023,
+ XusbFalconSrc = 0x40000024,
+ XusbFsSrc = 0x40000025,
+ XusbCoreDevSrc = 0x40000026,
+ XusbSsSrc = 0x40000027,
+ UartA = 0x03000001,
+ UartB = 0x35000405,
+ UartC = 0x3500040F,
+ UartD = 0x37000001,
+ Host1x = 0x4000002C,
+ Entropy = 0x4000002D,
+ SocTherm = 0x4000002E,
+ Vic = 0x4000002F,
+ Nvenc = 0x40000030,
+ Nvjpg = 0x40000031,
+ Nvdec = 0x40000032,
+ Qspi = 0x40000033,
+ ViI2c = 0x40000034,
+ Tsecb = 0x40000035,
+ Ape = 0x40000036,
+ AudioDsp = 0x40000037,
+ AudioUart = 0x40000038,
+ Emc = 0x40000039,
+ Plle = 0x4000003A,
+ PlleHwSeq = 0x4000003B,
+ Dsi = 0x4000003C,
+ Maud = 0x4000003D,
+ Dpaux1 = 0x4000003E,
+ MipiCal = 0x4000003F,
+ UartFstMipiCal = 0x40000040,
+ Osc = 0x40000041,
+ SysBus = 0x40000042,
+ SorSafe = 0x40000043,
+ XusbSs = 0x40000044,
+ XusbHost = 0x40000045,
+ XusbDevice = 0x40000046,
+ Extperiph1 = 0x40000047,
+ Ahub = 0x40000048,
+ Hda2hdmicodec = 0x40000049,
+ Gpuaux = 0x4000004A,
+ UsbD = 0x4000004B,
+ Usb2 = 0x4000004C,
+ Pcie = 0x4000004D,
+ Afi = 0x4000004E,
+ PciExClk = 0x4000004F,
+ PExUsbPhy = 0x40000050,
+ XUsbPadCtl = 0x40000051,
+ Apbdma = 0x40000052,
+ Usb2TrkClk = 0x40000053,
+ XUsbIoPll = 0x40000054,
+ XUsbIoPllHwSeq = 0x40000055,
+ Cec = 0x40000056,
+ Extperiph2 = 0x40000057,
+ OscClk = 0x40000080
+};
+
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
} // namespace Service::PCV
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 057666021..b10e86c8f 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
@@ -13,12 +12,12 @@ namespace Service::PM {
namespace {
-constexpr ResultCode ResultProcessNotFound{ErrorModule::PM, 1};
-[[maybe_unused]] constexpr ResultCode ResultAlreadyStarted{ErrorModule::PM, 2};
-[[maybe_unused]] constexpr ResultCode ResultNotTerminated{ErrorModule::PM, 3};
-[[maybe_unused]] constexpr ResultCode ResultDebugHookInUse{ErrorModule::PM, 4};
-[[maybe_unused]] constexpr ResultCode ResultApplicationRunning{ErrorModule::PM, 5};
-[[maybe_unused]] constexpr ResultCode ResultInvalidSize{ErrorModule::PM, 6};
+constexpr Result ResultProcessNotFound{ErrorModule::PM, 1};
+[[maybe_unused]] constexpr Result ResultAlreadyStarted{ErrorModule::PM, 2};
+[[maybe_unused]] constexpr Result ResultNotTerminated{ErrorModule::PM, 3};
+[[maybe_unused]] constexpr Result ResultDebugHookInUse{ErrorModule::PM, 4};
+[[maybe_unused]] constexpr Result ResultApplicationRunning{ErrorModule::PM, 5};
+[[maybe_unused]] constexpr Result ResultInvalidSize{ErrorModule::PM, 6};
constexpr u64 NO_PROCESS_FOUND_PID{0};
diff --git a/src/core/hle/service/pm/pm.h b/src/core/hle/service/pm/pm.h
index 852e7050c..060103928 100644
--- a/src/core/hle/service/pm/pm.h
+++ b/src/core/hle/service/pm/pm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 5c8a44688..78f897d3e 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/hex_util.h"
#include "common/logging/log.h"
diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h
index 395b57ead..37ea5afad 100644
--- a/src/core/hle/service/prepo/prepo.h
+++ b/src/core/hle/service/prepo/prepo.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp
index f8ea02b58..3a9412cf5 100644
--- a/src/core/hle/service/psc/psc.cpp
+++ b/src/core/hle/service/psc/psc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/psc/psc.h b/src/core/hle/service/psc/psc.h
index 89344f32d..d248372c2 100644
--- a/src/core/hle/service/psc/psc.h
+++ b/src/core/hle/service/psc/psc.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/ptm/psm.cpp b/src/core/hle/service/ptm/psm.cpp
index 5d248f671..2c31e9485 100644
--- a/src/core/hle/service/ptm/psm.cpp
+++ b/src/core/hle/service/ptm/psm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
@@ -10,10 +9,8 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/ptm/psm.h"
-#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
-namespace Service::PSM {
+namespace Service::PTM {
class IPsmSession final : public ServiceFramework<IPsmSession> {
public:
@@ -58,7 +55,7 @@ public:
private:
void BindStateChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ LOG_DEBUG(Service_PTM, "called");
should_signal = true;
@@ -68,7 +65,7 @@ private:
}
void UnbindStateChangeEvent(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ LOG_DEBUG(Service_PTM, "called");
should_signal = false;
@@ -79,7 +76,7 @@ private:
void SetChargerTypeChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_charger_type = state;
@@ -90,7 +87,7 @@ private:
void SetPowerSupplyChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_power_supply = state;
@@ -101,7 +98,7 @@ private:
void SetBatteryVoltageStateChangeEventEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto state = rp.Pop<bool>();
- LOG_DEBUG(Service_PSM, "called, state={}", state);
+ LOG_DEBUG(Service_PTM, "called, state={}", state);
should_signal_battery_voltage = state;
@@ -118,76 +115,58 @@ private:
Kernel::KEvent* state_change_event;
};
-class PSM final : public ServiceFramework<PSM> {
-public:
- explicit PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
- {1, &PSM::GetChargerType, "GetChargerType"},
- {2, nullptr, "EnableBatteryCharging"},
- {3, nullptr, "DisableBatteryCharging"},
- {4, nullptr, "IsBatteryChargingEnabled"},
- {5, nullptr, "AcquireControllerPowerSupply"},
- {6, nullptr, "ReleaseControllerPowerSupply"},
- {7, &PSM::OpenSession, "OpenSession"},
- {8, nullptr, "EnableEnoughPowerChargeEmulation"},
- {9, nullptr, "DisableEnoughPowerChargeEmulation"},
- {10, nullptr, "EnableFastBatteryCharging"},
- {11, nullptr, "DisableFastBatteryCharging"},
- {12, nullptr, "GetBatteryVoltageState"},
- {13, nullptr, "GetRawBatteryChargePercentage"},
- {14, nullptr, "IsEnoughPowerSupplied"},
- {15, nullptr, "GetBatteryAgePercentage"},
- {16, nullptr, "GetBatteryChargeInfoEvent"},
- {17, nullptr, "GetBatteryChargeInfoFields"},
- {18, nullptr, "GetBatteryChargeCalibratedEvent"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-
- ~PSM() override = default;
-
-private:
- void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+PSM::PSM(Core::System& system_) : ServiceFramework{system_, "psm"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &PSM::GetBatteryChargePercentage, "GetBatteryChargePercentage"},
+ {1, &PSM::GetChargerType, "GetChargerType"},
+ {2, nullptr, "EnableBatteryCharging"},
+ {3, nullptr, "DisableBatteryCharging"},
+ {4, nullptr, "IsBatteryChargingEnabled"},
+ {5, nullptr, "AcquireControllerPowerSupply"},
+ {6, nullptr, "ReleaseControllerPowerSupply"},
+ {7, &PSM::OpenSession, "OpenSession"},
+ {8, nullptr, "EnableEnoughPowerChargeEmulation"},
+ {9, nullptr, "DisableEnoughPowerChargeEmulation"},
+ {10, nullptr, "EnableFastBatteryCharging"},
+ {11, nullptr, "DisableFastBatteryCharging"},
+ {12, nullptr, "GetBatteryVoltageState"},
+ {13, nullptr, "GetRawBatteryChargePercentage"},
+ {14, nullptr, "IsEnoughPowerSupplied"},
+ {15, nullptr, "GetBatteryAgePercentage"},
+ {16, nullptr, "GetBatteryChargeInfoEvent"},
+ {17, nullptr, "GetBatteryChargeInfoFields"},
+ {18, nullptr, "GetBatteryChargeCalibratedEvent"},
+ };
+ // clang-format on
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u32>(battery_charge_percentage);
- }
+ RegisterHandlers(functions);
+}
- void GetChargerType(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+PSM::~PSM() = default;
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushEnum(charger_type);
- }
+void PSM::GetBatteryChargePercentage(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
- void OpenSession(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_PSM, "called");
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(battery_charge_percentage);
+}
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IPsmSession>(system);
- }
+void PSM::GetChargerType(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
- enum class ChargerType : u32 {
- Unplugged = 0,
- RegularCharger = 1,
- LowPowerCharger = 2,
- Unknown = 3,
- };
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(charger_type);
+}
- u32 battery_charge_percentage{100}; // 100%
- ChargerType charger_type{ChargerType::RegularCharger};
-};
+void PSM::OpenSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PTM, "called");
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
- std::make_shared<PSM>(system)->InstallAsService(sm);
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IPsmSession>(system);
}
-} // namespace Service::PSM
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/psm.h b/src/core/hle/service/ptm/psm.h
index 2930ce26a..f674ba8bc 100644
--- a/src/core/hle/service/ptm/psm.h
+++ b/src/core/hle/service/ptm/psm.h
@@ -1,19 +1,31 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-namespace Core {
-class System;
-}
+#include "core/hle/service/service.h"
-namespace Service::SM {
-class ServiceManager;
-}
+namespace Service::PTM {
-namespace Service::PSM {
+class PSM final : public ServiceFramework<PSM> {
+public:
+ explicit PSM(Core::System& system_);
+ ~PSM() override;
-void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+private:
+ enum class ChargerType : u32 {
+ Unplugged = 0,
+ RegularCharger = 1,
+ LowPowerCharger = 2,
+ Unknown = 3,
+ };
-} // namespace Service::PSM
+ void GetBatteryChargePercentage(Kernel::HLERequestContext& ctx);
+ void GetChargerType(Kernel::HLERequestContext& ctx);
+ void OpenSession(Kernel::HLERequestContext& ctx);
+
+ u32 battery_charge_percentage{100};
+ ChargerType charger_type{ChargerType::RegularCharger};
+};
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp
new file mode 100644
index 000000000..4bea995c6
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.cpp
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/ptm/ptm.h"
+#include "core/hle/service/ptm/ts.h"
+
+namespace Service::PTM {
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
+ std::make_shared<PSM>(system)->InstallAsService(sm);
+ std::make_shared<TS>(system)->InstallAsService(sm);
+}
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h
new file mode 100644
index 000000000..06224a24e
--- /dev/null
+++ b/src/core/hle/service/ptm/ptm.h
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::PTM {
+
+void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.cpp b/src/core/hle/service/ptm/ts.cpp
new file mode 100644
index 000000000..65c3f135f
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.cpp
@@ -0,0 +1,41 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include <memory>
+
+#include "core/core.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/ptm/ts.h"
+
+namespace Service::PTM {
+
+TS::TS(Core::System& system_) : ServiceFramework{system_, "ts"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "GetTemperatureRange"},
+ {1, &TS::GetTemperature, "GetTemperature"},
+ {2, nullptr, "SetMeasurementMode"},
+ {3, nullptr, "GetTemperatureMilliC"},
+ {4, nullptr, "OpenSession"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+TS::~TS() = default;
+
+void TS::GetTemperature(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto location{rp.PopEnum<Location>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called. location={}", location);
+
+ const s32 temperature = location == Location::Internal ? 35 : 20;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(temperature);
+}
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/ptm/ts.h b/src/core/hle/service/ptm/ts.h
new file mode 100644
index 000000000..39a734ef7
--- /dev/null
+++ b/src/core/hle/service/ptm/ts.h
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/service.h"
+
+namespace Service::PTM {
+
+class TS final : public ServiceFramework<TS> {
+public:
+ explicit TS(Core::System& system_);
+ ~TS() override;
+
+private:
+ enum class Location : u8 {
+ Internal,
+ External,
+ };
+
+ void GetTemperature(Kernel::HLERequestContext& ctx);
+};
+
+} // namespace Service::PTM
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index f54e6fe56..dadaf897f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/assert.h"
@@ -32,6 +31,7 @@
#include "core/hle/service/glue/glue.h"
#include "core/hle/service/grc/grc.h"
#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/jit/jit.h"
#include "core/hle/service/lbl/lbl.h"
#include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/ldr/ldr.h"
@@ -39,6 +39,7 @@
#include "core/hle/service/mig/mig.h"
#include "core/hle/service/mii/mii.h"
#include "core/hle/service/mm/mm_u.h"
+#include "core/hle/service/mnpp/mnpp_app.h"
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfp/nfp.h"
@@ -48,6 +49,7 @@
#include "core/hle/service/npns/npns.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvdrv/nvdrv.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/olsc/olsc.h"
#include "core/hle/service/pcie/pcie.h"
@@ -56,7 +58,7 @@
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/prepo/prepo.h"
#include "core/hle/service/psc/psc.h"
-#include "core/hle/service/ptm/psm.h"
+#include "core/hle/service/ptm/ptm.h"
#include "core/hle/service/service.h"
#include "core/hle/service/set/settings.h"
#include "core/hle/service/sm/sm.h"
@@ -89,8 +91,9 @@ namespace Service {
}
ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* service_name_,
- u32 max_sessions_, InvokerFn* handler_invoker_)
- : SessionRequestHandler(system_.Kernel(), service_name_), system{system_},
+ ServiceThreadType thread_type, u32 max_sessions_,
+ InvokerFn* handler_invoker_)
+ : SessionRequestHandler(system_.Kernel(), service_name_, thread_type), system{system_},
service_name{service_name_}, max_sessions{max_sessions_}, handler_invoker{handler_invoker_} {}
ServiceFrameworkBase::~ServiceFrameworkBase() {
@@ -187,17 +190,20 @@ void ServiceFrameworkBase::InvokeRequestTipc(Kernel::HLERequestContext& ctx) {
handler_invoker(this, info->handler_callback, ctx);
}
-ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
- Kernel::HLERequestContext& ctx) {
+Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
+ Kernel::HLERequestContext& ctx) {
const auto guard = LockService();
+ Result result = ResultSuccess;
+
switch (ctx.GetCommandType()) {
case IPC::CommandType::Close:
case IPC::CommandType::TIPC_Close: {
session.Close();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
- return IPC::ERR_REMOTE_PROCESS_DEAD;
+ result = IPC::ERR_REMOTE_PROCESS_DEAD;
+ break;
}
case IPC::CommandType::ControlWithContext:
case IPC::CommandType::Control: {
@@ -224,12 +230,13 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& sessi
ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
}
- return ResultSuccess;
+ return result;
}
/// Initialize Services
Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system)
- : nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system)} {
+ : hos_binder_driver_server{std::make_unique<NVFlinger::HosBinderDriverServer>(system)},
+ nv_flinger{std::make_unique<NVFlinger::NVFlinger>(system, *hos_binder_driver_server)} {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
// here and pass it into the respective InstallInterfaces functions.
@@ -258,6 +265,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
Glue::InstallInterfaces(system);
GRC::InstallInterfaces(*sm, system);
HID::InstallInterfaces(*sm, system);
+ JIT::InstallInterfaces(*sm, system);
LBL::InstallInterfaces(*sm, system);
LDN::InstallInterfaces(*sm, system);
LDR::InstallInterfaces(*sm, system);
@@ -265,6 +273,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
Migration::InstallInterfaces(*sm, system);
Mii::InstallInterfaces(*sm, system);
MM::InstallInterfaces(*sm, system);
+ MNPP::InstallInterfaces(*sm, system);
NCM::InstallInterfaces(*sm, system);
NFC::InstallInterfaces(*sm, system);
NFP::InstallInterfaces(*sm, system);
@@ -281,14 +290,14 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
PlayReport::InstallInterfaces(*sm, system);
PM::InstallInterfaces(system);
PSC::InstallInterfaces(*sm, system);
- PSM::InstallInterfaces(*sm, system);
+ PTM::InstallInterfaces(*sm, system);
Set::InstallInterfaces(*sm, system);
Sockets::InstallInterfaces(*sm, system);
SPL::InstallInterfaces(*sm, system);
SSL::InstallInterfaces(*sm, system);
Time::InstallInterfaces(system);
USB::InstallInterfaces(*sm, system);
- VI::InstallInterfaces(*sm, system, *nv_flinger);
+ VI::InstallInterfaces(*sm, system, *nv_flinger, *hos_binder_driver_server);
WLAN::InstallInterfaces(*sm, system);
}
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c9d6b879d..5bf197c51 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -9,7 +8,6 @@
#include <string>
#include <boost/container/flat_map.hpp>
#include "common/common_types.h"
-#include "common/spin_lock.h"
#include "core/hle/kernel/hle_ipc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,8 +31,9 @@ class FileSystemController;
}
namespace NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace NVFlinger
namespace SM {
class ServiceManager;
@@ -80,8 +79,8 @@ public:
Kernel::KClientPort& CreatePort();
/// Handles a synchronization request for the service.
- ResultCode HandleSyncRequest(Kernel::KServerSession& session,
- Kernel::HLERequestContext& context) override;
+ Result HandleSyncRequest(Kernel::KServerSession& session,
+ Kernel::HLERequestContext& context) override;
protected:
/// Member-function pointer type of SyncRequest handlers.
@@ -89,7 +88,7 @@ protected:
using HandlerFnP = void (Self::*)(Kernel::HLERequestContext&);
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
- [[nodiscard]] std::scoped_lock<Common::SpinLock> LockService() {
+ [[nodiscard]] std::scoped_lock<std::mutex> LockService() {
return std::scoped_lock{lock_service};
}
@@ -113,7 +112,8 @@ private:
Kernel::HLERequestContext& ctx);
explicit ServiceFrameworkBase(Core::System& system_, const char* service_name_,
- u32 max_sessions_, InvokerFn* handler_invoker_);
+ ServiceThreadType thread_type, u32 max_sessions_,
+ InvokerFn* handler_invoker_);
~ServiceFrameworkBase() override;
void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n);
@@ -133,7 +133,7 @@ private:
boost::container::flat_map<u32, FunctionInfoBase> handlers_tipc;
/// Used to gain exclusive access to the service members, e.g. from CoreTiming thread.
- Common::SpinLock lock_service;
+ std::mutex lock_service;
};
/**
@@ -175,14 +175,17 @@ protected:
/**
* Initializes the handler with no functions installed.
*
- * @param system_ The system context to construct this service under.
+ * @param system_ The system context to construct this service under.
* @param service_name_ Name of the service.
- * @param max_sessions_ Maximum number of sessions that can be
- * connected to this service at the same time.
+ * @param thread_type Specifies the thread type for this service. If this is set to CreateNew,
+ * it creates a new thread for it, otherwise this uses the default thread.
+ * @param max_sessions_ Maximum number of sessions that can be connected to this service at the
+ * same time.
*/
explicit ServiceFramework(Core::System& system_, const char* service_name_,
+ ServiceThreadType thread_type = ServiceThreadType::Default,
u32 max_sessions_ = ServerSessionCountMax)
- : ServiceFrameworkBase(system_, service_name_, max_sessions_, Invoker) {}
+ : ServiceFrameworkBase(system_, service_name_, thread_type, max_sessions_, Invoker) {}
/// Registers handlers in the service.
template <std::size_t N>
@@ -236,6 +239,7 @@ public:
~Services();
private:
+ std::unique_ptr<NVFlinger::HosBinderDriverServer> hos_binder_driver_server;
std::unique_ptr<NVFlinger::NVFlinger> nv_flinger;
};
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 8795eb6b7..f761c2da4 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
@@ -75,7 +74,7 @@ constexpr std::array<std::pair<LanguageCode, KeyboardLayout>, 18> language_to_la
constexpr std::size_t PRE_4_0_0_MAX_ENTRIES = 0xF;
constexpr std::size_t POST_4_0_0_MAX_ENTRIES = 0x40;
-constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
+constexpr Result ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t num_language_codes) {
IPC::ResponseBuilder rb{ctx, 3};
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index acabebeaa..60cad3e6f 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/set/set_cal.cpp b/src/core/hle/service/set/set_cal.cpp
index b2aa7bc0c..d2c0d536f 100644
--- a/src/core/hle/service/set/set_cal.cpp
+++ b/src/core/hle/service/set/set_cal.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/set/set_cal.h"
diff --git a/src/core/hle/service/set/set_cal.h b/src/core/hle/service/set/set_cal.h
index a29fc3ddd..8f50278ed 100644
--- a/src/core/hle/service/set/set_cal.h
+++ b/src/core/hle/service/set/set_cal.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/set/set_fd.cpp b/src/core/hle/service/set/set_fd.cpp
index f04dc5047..278ef32e1 100644
--- a/src/core/hle/service/set/set_fd.cpp
+++ b/src/core/hle/service/set/set_fd.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/set/set_fd.h"
diff --git a/src/core/hle/service/set/set_fd.h b/src/core/hle/service/set/set_fd.h
index c28cb301e..150a7cbce 100644
--- a/src/core/hle/service/set/set_fd.h
+++ b/src/core/hle/service/set/set_fd.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 38e6eae04..2a0b812c1 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
@@ -33,7 +32,7 @@ void GetFirmwareVersionImpl(Kernel::HLERequestContext& ctx, GetFirmwareVersionTy
// consistence (currently reports as 5.1.0-0.0)
const auto archive = FileSys::SystemArchive::SystemVersion();
- const auto early_exit_failure = [&ctx](std::string_view desc, ResultCode code) {
+ const auto early_exit_failure = [&ctx](std::string_view desc, Result code) {
LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).",
desc);
IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h
index edb185a68..ac97772b7 100644
--- a/src/core/hle/service/set/set_sys.h
+++ b/src/core/hle/service/set/set_sys.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/set/settings.cpp b/src/core/hle/service/set/settings.cpp
index 212ebc427..4ebc2a0ec 100644
--- a/src/core/hle/service/set/settings.cpp
+++ b/src/core/hle/service/set/settings.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/set/set.h"
#include "core/hle/service/set/set_cal.h"
diff --git a/src/core/hle/service/set/settings.h b/src/core/hle/service/set/settings.h
index 7a6950dd0..6cd7d634c 100644
--- a/src/core/hle/service/set/settings.h
+++ b/src/core/hle/service/set/settings.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index eaa172595..246c94623 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <tuple>
#include "common/assert.h"
@@ -18,10 +17,10 @@
namespace Service::SM {
-constexpr ResultCode ERR_NOT_INITIALIZED(ErrorModule::SM, 2);
-constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
-constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
-constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
+constexpr Result ERR_NOT_INITIALIZED(ErrorModule::SM, 2);
+constexpr Result ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
+constexpr Result ERR_INVALID_NAME(ErrorModule::SM, 6);
+constexpr Result ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
ServiceManager::~ServiceManager() = default;
@@ -30,7 +29,7 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
controller_interface->InvokeRequest(context);
}
-static ResultCode ValidateServiceName(const std::string& name) {
+static Result ValidateServiceName(const std::string& name) {
if (name.empty() || name.size() > 8) {
LOG_ERROR(Service_SM, "Invalid service name! service={}", name);
return ERR_INVALID_NAME;
@@ -44,8 +43,8 @@ Kernel::KClientPort& ServiceManager::InterfaceFactory(ServiceManager& self, Core
return self.sm_interface->CreatePort();
}
-ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions,
- Kernel::SessionRequestHandlerPtr handler) {
+Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
+ Kernel::SessionRequestHandlerPtr handler) {
CASCADE_CODE(ValidateServiceName(name));
@@ -59,7 +58,7 @@ ResultCode ServiceManager::RegisterService(std::string name, u32 max_sessions,
return ResultSuccess;
}
-ResultCode ServiceManager::UnregisterService(const std::string& name) {
+Result ServiceManager::UnregisterService(const std::string& name) {
CASCADE_CODE(ValidateServiceName(name));
const auto iter = registered_services.find(name);
@@ -81,6 +80,8 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
}
auto* port = Kernel::KPort::Create(kernel);
+ SCOPE_EXIT({ port->Close(); });
+
port->Initialize(ServerSessionCountMax, false, name);
auto handler = it->second;
port->GetServerPort().SetSessionHandler(std::move(handler));
@@ -93,7 +94,7 @@ ResultVal<Kernel::KPort*> ServiceManager::GetServicePort(const std::string& name
* Inputs:
* 0: 0x00000000
* Outputs:
- * 0: ResultCode
+ * 0: Result
*/
void SM::Initialize(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SM, "called");
@@ -151,7 +152,7 @@ ResultVal<Kernel::KClientSession*> SM::GetServiceImpl(Kernel::HLERequestContext&
auto& port = port_result.Unwrap();
SCOPE_EXIT({ port->GetClientPort().Close(); });
- server_ports.emplace_back(&port->GetServerPort());
+ kernel.RegisterServerObject(&port->GetServerPort());
// Create a new session.
Kernel::KClientSession* session{};
@@ -204,7 +205,7 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
}
SM::SM(ServiceManager& service_manager_, Core::System& system_)
- : ServiceFramework{system_, "sm:", 4},
+ : ServiceFramework{system_, "sm:", ServiceThreadType::Default, 4},
service_manager{service_manager_}, kernel{system_.Kernel()} {
RegisterHandlers({
{0, &SM::Initialize, "Initialize"},
@@ -222,10 +223,6 @@ SM::SM(ServiceManager& service_manager_, Core::System& system_)
});
}
-SM::~SM() {
- for (auto& server_port : server_ports) {
- server_port->Close();
- }
-}
+SM::~SM() = default;
} // namespace Service::SM
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 021eb51b4..878decc6f 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -22,7 +21,6 @@ class KClientPort;
class KClientSession;
class KernelCore;
class KPort;
-class KServerPort;
class SessionRequestHandler;
} // namespace Kernel
@@ -48,7 +46,6 @@ private:
ServiceManager& service_manager;
bool is_initialized{};
Kernel::KernelCore& kernel;
- std::vector<Kernel::KServerPort*> server_ports;
};
class ServiceManager {
@@ -58,9 +55,9 @@ public:
explicit ServiceManager(Kernel::KernelCore& kernel_);
~ServiceManager();
- ResultCode RegisterService(std::string name, u32 max_sessions,
- Kernel::SessionRequestHandlerPtr handler);
- ResultCode UnregisterService(const std::string& name);
+ Result RegisterService(std::string name, u32 max_sessions,
+ Kernel::SessionRequestHandlerPtr handler);
+ Result UnregisterService(const std::string& name);
ResultVal<Kernel::KPort*> GetServicePort(const std::string& name);
template <Common::DerivedFrom<Kernel::SessionRequestHandler> T>
diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp
index 09f9ecee1..2a4bd64ab 100644
--- a/src/core/hle/service/sm/sm_controller.cpp
+++ b/src/core/hle/service/sm/sm_controller.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
@@ -34,7 +33,7 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) {
// Create a session.
Kernel::KClientSession* session{};
- const ResultCode result = parent_port.CreateSession(std::addressof(session), session_manager);
+ const Result result = parent_port.CreateSession(std::addressof(session), session_manager);
if (result.IsError()) {
LOG_CRITICAL(Service, "CreateSession failed with error 0x{:08X}", result.raw);
IPC::ResponseBuilder rb{ctx, 2};
diff --git a/src/core/hle/service/sm/sm_controller.h b/src/core/hle/service/sm/sm_controller.h
index 7494f898d..ed386f660 100644
--- a/src/core/hle/service/sm/sm_controller.h
+++ b/src/core/hle/service/sm/sm_controller.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index f83272633..9e94a462f 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <memory>
@@ -10,13 +9,16 @@
#include <fmt/format.h>
#include "common/microprofile.h"
-#include "common/thread.h"
+#include "common/socket_types.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h"
-#include "core/network/network.h"
-#include "core/network/sockets.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/socket_proxy.h"
+#include "core/internal_network/sockets.h"
+#include "network/network.h"
namespace Service::Sockets {
@@ -474,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
LOG_INFO(Service, "New socket fd={}", fd);
- descriptor.socket = std::make_unique<Network::Socket>();
+ auto room_member = room_network.GetRoomMember().lock();
+ if (room_member && room_member->IsConnected()) {
+ descriptor.socket = std::make_unique<Network::ProxySocket>(room_network);
+ } else {
+ descriptor.socket = std::make_unique<Network::Socket>();
+ }
+
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
descriptor.is_connection_based = IsConnectionBased(type);
@@ -569,9 +577,9 @@ std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
new_descriptor.socket = std::move(result.socket);
new_descriptor.is_connection_based = descriptor.is_connection_based;
- ASSERT(write_buffer.size() == sizeof(SockAddrIn));
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
- std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in));
+ const size_t length = std::min(sizeof(guest_addr_in), write_buffer.size());
+ std::memcpy(write_buffer.data(), &guest_addr_in, length);
return {new_fd, Errno::SUCCESS};
}
@@ -650,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
ASSERT(arg == 0);
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
- const bool enable = (arg & FLAG_O_NONBLOCK) != 0;
+ const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
if (bsd_errno != Errno::SUCCESS) {
return {-1, bsd_errno};
@@ -671,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
return Errno::BADF;
}
- Network::Socket* const socket = file_descriptors[fd]->socket.get();
+ Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
if (optname == OptName::LINGER) {
ASSERT(optlen == sizeof(Linger));
@@ -690,6 +698,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
case OptName::REUSEADDR:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetReuseAddr(value != 0));
+ case OptName::KEEPALIVE:
+ ASSERT(value == 0 || value == 1);
+ return Translate(socket->SetKeepAlive(value != 0));
case OptName::BROADCAST:
ASSERT(value == 0 || value == 1);
return Translate(socket->SetBroadcast(value != 0));
@@ -719,7 +730,27 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
if (!IsFileDescriptorValid(fd)) {
return {-1, Errno::BADF};
}
- return Translate(file_descriptors[fd]->socket->Recv(flags, message));
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+
+ // Apply flags
+ using Network::FLAG_MSG_DONTWAIT;
+ using Network::FLAG_O_NONBLOCK;
+ if ((flags & FLAG_MSG_DONTWAIT) != 0) {
+ flags &= ~FLAG_MSG_DONTWAIT;
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(true);
+ }
+ }
+
+ const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
+
+ // Restore original state
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(false);
+ }
+
+ return {ret, bsd_errno};
}
std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
@@ -740,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
}
// Apply flags
+ using Network::FLAG_MSG_DONTWAIT;
+ using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
@@ -838,7 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
rb.PushEnum(bsd_errno);
}
-BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
+void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
+ for (auto& optional_descriptor : file_descriptors) {
+ if (!optional_descriptor.has_value()) {
+ continue;
+ }
+ FileDescriptor& descriptor = *optional_descriptor;
+ descriptor.socket.get()->HandleProxyPacket(packet);
+ }
+}
+
+BSD::BSD(Core::System& system_, const char* name)
+ : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{
+ system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
@@ -879,9 +924,20 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na
// clang-format on
RegisterHandlers(functions);
+
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ proxy_packet_received = room_member->BindOnProxyPacketReceived(
+ [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
+ } else {
+ LOG_ERROR(Service, "Network isn't initialized");
+ }
}
-BSD::~BSD() = default;
+BSD::~BSD() {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ room_member->Unbind(proxy_packet_received);
+ }
+}
BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} {
// clang-format off
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index a387e50df..81e855e0f 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,16 +7,19 @@
#include <vector>
#include "common/common_types.h"
+#include "common/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
+#include "network/network.h"
namespace Core {
class System;
}
namespace Network {
+class SocketBase;
class Socket;
-}
+} // namespace Network
namespace Service::Sockets {
@@ -31,7 +33,7 @@ private:
static constexpr size_t MAX_FD = 128;
struct FileDescriptor {
- std::unique_ptr<Network::Socket> socket;
+ std::unique_ptr<Network::SocketBase> socket;
s32 flags = 0;
bool is_connection_based = false;
};
@@ -166,6 +168,14 @@ private:
void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
+
+ Network::RoomNetwork& room_network;
+
+ /// Callback to parse and handle a received wifi packet.
+ void OnProxyPacketReceived(const Network::ProxyPacket& packet);
+
+ // Callback identifier for the OnProxyPacketReceived event.
+ Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
};
class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/ethc.cpp b/src/core/hle/service/sockets/ethc.cpp
index 899a64c2f..c12ea999b 100644
--- a/src/core/hle/service/sockets/ethc.cpp
+++ b/src/core/hle/service/sockets/ethc.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/sockets/ethc.h"
diff --git a/src/core/hle/service/sockets/ethc.h b/src/core/hle/service/sockets/ethc.h
index 71884182e..7c5759a96 100644
--- a/src/core/hle/service/sockets/ethc.h
+++ b/src/core/hle/service/sockets/ethc.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp
index 1159debc5..6491a73be 100644
--- a/src/core/hle/service/sockets/nsd.cpp
+++ b/src/core/hle/service/sockets/nsd.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/sockets/nsd.h"
diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h
index becf93125..5cc12b855 100644
--- a/src/core/hle/service/sockets/nsd.h
+++ b/src/core/hle/service/sockets/nsd.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp
index fb6142c49..097c37d7a 100644
--- a/src/core/hle/service/sockets/sfdnsres.cpp
+++ b/src/core/hle/service/sockets/sfdnsres.cpp
@@ -1,9 +1,28 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "common/string_util.h"
+#include "common/swap.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/sockets/sfdnsres.h"
+#include "core/memory.h"
+
+#ifdef _WIN32
+#include <ws2tcpip.h>
+#elif YUZU_UNIX
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#ifndef EAI_NODATA
+#define EAI_NODATA EAI_NONAME
+#endif
+#endif
namespace Service::Sockets {
@@ -21,7 +40,7 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
{9, nullptr, "CancelRequest"},
{10, nullptr, "GetHostByNameRequestWithOptions"},
{11, nullptr, "GetHostByAddrRequestWithOptions"},
- {12, nullptr, "GetAddrInfoRequestWithOptions"},
+ {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"},
{13, nullptr, "GetNameInfoRequestWithOptions"},
{14, nullptr, "ResolverSetOptionRequest"},
{15, nullptr, "ResolverGetOptionRequest"},
@@ -31,7 +50,142 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"
SFDNSRES::~SFDNSRES() = default;
-void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
+enum class NetDbError : s32 {
+ Internal = -1,
+ Success = 0,
+ HostNotFound = 1,
+ TryAgain = 2,
+ NoRecovery = 3,
+ NoData = 4,
+};
+
+static NetDbError AddrInfoErrorToNetDbError(s32 result) {
+ // Best effort guess to map errors
+ switch (result) {
+ case 0:
+ return NetDbError::Success;
+ case EAI_AGAIN:
+ return NetDbError::TryAgain;
+ case EAI_NODATA:
+ return NetDbError::NoData;
+ default:
+ return NetDbError::HostNotFound;
+ }
+}
+
+static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code,
+ std::string_view host) {
+ // Adapted from
+ // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
+ std::vector<u8> data;
+
+ auto* current = addrinfo;
+ while (current != nullptr) {
+ struct SerializedResponseHeader {
+ u32 magic;
+ s32 flags;
+ s32 family;
+ s32 socket_type;
+ s32 protocol;
+ u32 address_length;
+ };
+ static_assert(sizeof(SerializedResponseHeader) == 0x18,
+ "Response header size must be 0x18 bytes");
+
+ constexpr auto header_size = sizeof(SerializedResponseHeader);
+ const auto addr_size =
+ current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4;
+ const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1;
+
+ const auto last_size = data.size();
+ data.resize(last_size + header_size + addr_size + canonname_size);
+
+ // Header in network byte order
+ SerializedResponseHeader header{};
+
+ constexpr auto HEADER_MAGIC = 0xBEEFCAFE;
+ header.magic = htonl(HEADER_MAGIC);
+ header.family = htonl(current->ai_family);
+ header.flags = htonl(current->ai_flags);
+ header.socket_type = htonl(current->ai_socktype);
+ header.protocol = htonl(current->ai_protocol);
+ header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0;
+
+ auto* header_ptr = data.data() + last_size;
+ std::memcpy(header_ptr, &header, header_size);
+
+ if (header.address_length == 0) {
+ std::memset(header_ptr + header_size, 0, 4);
+ } else {
+ switch (current->ai_family) {
+ case AF_INET: {
+ struct SockAddrIn {
+ s16 sin_family;
+ u16 sin_port;
+ u32 sin_addr;
+ u8 sin_zero[8];
+ };
+
+ SockAddrIn serialized_addr{};
+ const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr);
+ serialized_addr.sin_port = htons(addr.sin_port);
+ serialized_addr.sin_family = htons(addr.sin_family);
+ serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr);
+ std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn));
+
+ char addr_string_buf[64]{};
+ inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf));
+ LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf);
+ break;
+ }
+ case AF_INET6: {
+ struct SockAddrIn6 {
+ s16 sin6_family;
+ u16 sin6_port;
+ u32 sin6_flowinfo;
+ u8 sin6_addr[16];
+ u32 sin6_scope_id;
+ };
+
+ SockAddrIn6 serialized_addr{};
+ const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr);
+ serialized_addr.sin6_family = htons(addr.sin6_family);
+ serialized_addr.sin6_port = htons(addr.sin6_port);
+ serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo);
+ serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id);
+ std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr,
+ sizeof(SockAddrIn6::sin6_addr));
+ std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6));
+
+ char addr_string_buf[64]{};
+ inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf));
+ LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf);
+ break;
+ }
+ default:
+ std::memcpy(header_ptr + header_size, current->ai_addr, addr_size);
+ break;
+ }
+ }
+ if (current->ai_canonname) {
+ std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size);
+ } else {
+ *(header_ptr + header_size + addr_size) = 0;
+ }
+
+ current = current->ai_next;
+ }
+
+ // 4-byte sentinel value
+ data.push_back(0);
+ data.push_back(0);
+ data.push_back(0);
+ data.push_back(0);
+
+ return data;
+}
+
+static std::pair<u32, s32> GetAddrInfoRequestImpl(Kernel::HLERequestContext& ctx) {
struct Parameters {
u8 use_nsd_resolve;
u32 unknown;
@@ -42,11 +196,51 @@ void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
const auto parameters = rp.PopRaw<Parameters>();
LOG_WARNING(Service,
- "(STUBBED) called. use_nsd_resolve={}, unknown=0x{:08X}, process_id=0x{:016X}",
+ "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}",
parameters.use_nsd_resolve, parameters.unknown, parameters.process_id);
- IPC::ResponseBuilder rb{ctx, 2};
+ const auto host_buffer = ctx.ReadBuffer(0);
+ const std::string host = Common::StringFromBuffer(host_buffer);
+
+ const auto service_buffer = ctx.ReadBuffer(1);
+ const std::string service = Common::StringFromBuffer(service_buffer);
+
+ addrinfo* addrinfo;
+ // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now
+ s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo);
+
+ u32 data_size = 0;
+ if (result_code == 0 && addrinfo != nullptr) {
+ const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host);
+ data_size = static_cast<u32>(data.size());
+ freeaddrinfo(addrinfo);
+
+ ctx.WriteBuffer(data, 0);
+ }
+
+ return std::make_pair(data_size, result_code);
+}
+
+void SFDNSRES::GetAddrInfoRequest(Kernel::HLERequestContext& ctx) {
+ auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
+ rb.Push(result_code); // errno
+ rb.Push(data_size); // serialized size
+}
+
+void SFDNSRES::GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx) {
+ // Additional options are ignored
+ auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx);
+
+ IPC::ResponseBuilder rb{ctx, 5};
rb.Push(ResultSuccess);
+ rb.Push(data_size); // serialized size
+ rb.Push(result_code); // errno
+ rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode
+ rb.Push(0);
}
-} // namespace Service::Sockets
+} // 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..96018ea77 100644
--- a/src/core/hle/service/sockets/sfdnsres.h
+++ b/src/core/hle/service/sockets/sfdnsres.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -19,6 +18,7 @@ public:
private:
void GetAddrInfoRequest(Kernel::HLERequestContext& ctx);
+ void GetAddrInfoRequestWithOptions(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp
index 96f73bce3..8d3ba6f96 100644
--- a/src/core/hle/service/sockets/sockets.cpp
+++ b/src/core/hle/service/sockets/sockets.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/ethc.h"
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index 02dbbae40..31b7dad33 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -23,7 +22,9 @@ enum class Errno : u32 {
AGAIN = 11,
INVAL = 22,
MFILE = 24,
+ MSGSIZE = 90,
NOTCONN = 107,
+ TIMEDOUT = 110,
};
enum class Domain : u32 {
@@ -46,6 +47,7 @@ enum class Protocol : u32 {
enum class OptName : u32 {
REUSEADDR = 0x4,
+ KEEPALIVE = 0x8,
BROADCAST = 0x20,
LINGER = 0x80,
SNDBUF = 0x1001,
@@ -96,10 +98,6 @@ struct Linger {
u32 linger;
};
-constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
-
-constexpr u32 FLAG_O_NONBLOCK = 0x800;
-
/// Registers all Sockets services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index ca61d72ca..023aa0486 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
@@ -8,7 +7,7 @@
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
#include "core/hle/service/sockets/sockets_translate.h"
-#include "core/network/network.h"
+#include "core/internal_network/network.h"
namespace Service::Sockets {
@@ -26,6 +25,8 @@ Errno Translate(Network::Errno value) {
return Errno::MFILE;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
+ case Network::Errno::TIMEDOUT:
+ return Errno::TIMEDOUT;
default:
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
return Errno::SUCCESS;
diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h
index 057d1ff22..c93291d3e 100644
--- a/src/core/hle/service/sockets/sockets_translate.h
+++ b/src/core/hle/service/sockets/sockets_translate.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,7 +7,7 @@
#include "common/common_types.h"
#include "core/hle/service/sockets/sockets.h"
-#include "core/network/network.h"
+#include "core/internal_network/network.h"
namespace Service::Sockets {
diff --git a/src/core/hle/service/spl/csrng.cpp b/src/core/hle/service/spl/csrng.cpp
index 9c7f89475..ca121fb05 100644
--- a/src/core/hle/service/spl/csrng.cpp
+++ b/src/core/hle/service/spl/csrng.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/spl/csrng.h"
diff --git a/src/core/hle/service/spl/csrng.h b/src/core/hle/service/spl/csrng.h
index 0d03cc6cb..b337a8281 100644
--- a/src/core/hle/service/spl/csrng.h
+++ b/src/core/hle/service/spl/csrng.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/spl/spl.cpp b/src/core/hle/service/spl/spl.cpp
index 20384042f..fde212186 100644
--- a/src/core/hle/service/spl/spl.cpp
+++ b/src/core/hle/service/spl/spl.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/spl/spl.h"
diff --git a/src/core/hle/service/spl/spl.h b/src/core/hle/service/spl/spl.h
index 5599c0c01..7b63d8b1a 100644
--- a/src/core/hle/service/spl/spl.h
+++ b/src/core/hle/service/spl/spl.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/spl/spl_module.cpp b/src/core/hle/service/spl/spl_module.cpp
index 10f7d1461..64eae1ebf 100644
--- a/src/core/hle/service/spl/spl_module.cpp
+++ b/src/core/hle/service/spl/spl_module.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstdlib>
diff --git a/src/core/hle/service/spl/spl_module.h b/src/core/hle/service/spl/spl_module.h
index 61630df80..4c9a3c618 100644
--- a/src/core/hle/service/spl/spl_module.h
+++ b/src/core/hle/service/spl/spl_module.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/spl/spl_results.h b/src/core/hle/service/spl/spl_results.h
index a07c61409..dd7ba11f3 100644
--- a/src/core/hle/service/spl/spl_results.h
+++ b/src/core/hle/service/spl/spl_results.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -9,23 +8,23 @@
namespace Service::SPL {
// Description 0 - 99
-constexpr ResultCode ResultSecureMonitorError{ErrorModule::SPL, 0};
-constexpr ResultCode ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1};
-constexpr ResultCode ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2};
-constexpr ResultCode ResultSecureMonitorBusy{ErrorModule::SPL, 3};
-constexpr ResultCode ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4};
-constexpr ResultCode ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5};
-constexpr ResultCode ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6};
-constexpr ResultCode ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7};
+constexpr Result ResultSecureMonitorError{ErrorModule::SPL, 0};
+constexpr Result ResultSecureMonitorNotImplemented{ErrorModule::SPL, 1};
+constexpr Result ResultSecureMonitorInvalidArgument{ErrorModule::SPL, 2};
+constexpr Result ResultSecureMonitorBusy{ErrorModule::SPL, 3};
+constexpr Result ResultSecureMonitorNoAsyncOperation{ErrorModule::SPL, 4};
+constexpr Result ResultSecureMonitorInvalidAsyncOperation{ErrorModule::SPL, 5};
+constexpr Result ResultSecureMonitorNotPermitted{ErrorModule::SPL, 6};
+constexpr Result ResultSecureMonitorNotInitialized{ErrorModule::SPL, 7};
-constexpr ResultCode ResultInvalidSize{ErrorModule::SPL, 100};
-constexpr ResultCode ResultUnknownSecureMonitorError{ErrorModule::SPL, 101};
-constexpr ResultCode ResultDecryptionFailed{ErrorModule::SPL, 102};
+constexpr Result ResultInvalidSize{ErrorModule::SPL, 100};
+constexpr Result ResultUnknownSecureMonitorError{ErrorModule::SPL, 101};
+constexpr Result ResultDecryptionFailed{ErrorModule::SPL, 102};
-constexpr ResultCode ResultOutOfKeySlots{ErrorModule::SPL, 104};
-constexpr ResultCode ResultInvalidKeySlot{ErrorModule::SPL, 105};
-constexpr ResultCode ResultBootReasonAlreadySet{ErrorModule::SPL, 106};
-constexpr ResultCode ResultBootReasonNotSet{ErrorModule::SPL, 107};
-constexpr ResultCode ResultInvalidArgument{ErrorModule::SPL, 108};
+constexpr Result ResultOutOfKeySlots{ErrorModule::SPL, 104};
+constexpr Result ResultInvalidKeySlot{ErrorModule::SPL, 105};
+constexpr Result ResultBootReasonAlreadySet{ErrorModule::SPL, 106};
+constexpr Result ResultBootReasonNotSet{ErrorModule::SPL, 107};
+constexpr Result ResultInvalidArgument{ErrorModule::SPL, 108};
} // namespace Service::SPL
diff --git a/src/core/hle/service/spl/spl_types.h b/src/core/hle/service/spl/spl_types.h
index a654e7556..91f9cc032 100644
--- a/src/core/hle/service/spl/spl_types.h
+++ b/src/core/hle/service/spl/spl_types.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index a81a595ea..3735e0452 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/service.h"
diff --git a/src/core/hle/service/ssl/ssl.h b/src/core/hle/service/ssl/ssl.h
index a3aa4b4b5..27b38a003 100644
--- a/src/core/hle/service/ssl/ssl.h
+++ b/src/core/hle/service/ssl/ssl.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
index d0cacb80c..ef070f32f 100644
--- a/src/core/hle/service/time/clock_types.h
+++ b/src/core/hle/service/time/clock_types.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -23,7 +22,7 @@ struct SteadyClockTimePoint {
s64 time_point;
Common::UUID clock_source_id;
- ResultCode GetSpanBetween(SteadyClockTimePoint other, s64& span) const {
+ Result GetSpanBetween(SteadyClockTimePoint other, s64& span) const {
span = 0;
if (clock_source_id != other.clock_source_id) {
@@ -93,9 +92,9 @@ struct ClockSnapshot {
TimeType type;
INSERT_PADDING_BYTES_NOINIT(0x2);
- static ResultCode GetCurrentTime(s64& current_time,
- const SteadyClockTimePoint& steady_clock_time_point,
- const SystemClockContext& context) {
+ static Result GetCurrentTime(s64& current_time,
+ const SteadyClockTimePoint& steady_clock_time_point,
+ const SystemClockContext& context) {
if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) {
current_time = 0;
return ERROR_TIME_MISMATCH;
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
index 42893e3f6..0f928a5a5 100644
--- a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
+++ b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
index d12cb5335..0a5f5aafb 100644
--- a/src/core/hle/service/time/ephemeral_network_system_clock_core.h
+++ b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h
index 8501a3e8c..6655d30e1 100644
--- a/src/core/hle/service/time/errors.h
+++ b/src/core/hle/service/time/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -8,15 +7,15 @@
namespace Service::Time {
-constexpr ResultCode ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
-constexpr ResultCode ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
-constexpr ResultCode ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
-constexpr ResultCode ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
-constexpr ResultCode ERROR_OVERFLOW{ErrorModule::Time, 201};
-constexpr ResultCode ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
-constexpr ResultCode ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
-constexpr ResultCode ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
-constexpr ResultCode ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
-constexpr ResultCode ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
+constexpr Result ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
+constexpr Result ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
+constexpr Result ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
+constexpr Result ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
+constexpr Result ERROR_OVERFLOW{ErrorModule::Time, 201};
+constexpr Result ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
+constexpr Result ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
+constexpr Result ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
+constexpr Result ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
+constexpr Result ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
} // namespace Service::Time
diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h
index ac6c7b4b1..1639ef2b9 100644
--- a/src/core/hle/service/time/local_system_clock_context_writer.h
+++ b/src/core/hle/service/time/local_system_clock_context_writer.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -15,7 +14,7 @@ public:
: SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {}
protected:
- ResultCode Update() override {
+ Result Update() override {
shared_memory.UpdateLocalSystemClockContext(context);
return ResultSuccess;
}
diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h
index a54fd7fe1..655e4c06d 100644
--- a/src/core/hle/service/time/network_system_clock_context_writer.h
+++ b/src/core/hle/service/time/network_system_clock_context_writer.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -16,7 +15,7 @@ public:
: SystemClockContextUpdateCallback{}, shared_memory{shared_memory_} {}
protected:
- ResultCode Update() override {
+ Result Update() override {
shared_memory.UpdateNetworkSystemClockContext(context);
return ResultSuccess;
}
diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h
index 6320c7af1..ae2ff1bfd 100644
--- a/src/core/hle/service/time/standard_local_system_clock_core.h
+++ b/src/core/hle/service/time/standard_local_system_clock_core.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h
index 95923a27b..c1ec5252b 100644
--- a/src/core/hle/service/time/standard_network_system_clock_core.h
+++ b/src/core/hle/service/time/standard_network_system_clock_core.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
index a1ffdd524..3dbbb9850 100644
--- a/src/core/hle/service/time/standard_steady_clock_core.cpp
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h
index f56f3fd95..036463b87 100644
--- a/src/core/hle/service/time/standard_steady_clock_core.h
+++ b/src/core/hle/service/time/standard_steady_clock_core.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
index e94220a44..b033757ed 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.cpp
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/core.h"
@@ -28,9 +27,9 @@ StandardUserSystemClockCore::~StandardUserSystemClockCore() {
service_context.CloseEvent(auto_correction_event);
}
-ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
- bool value) {
- if (const ResultCode result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) {
+Result StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
+ bool value) {
+ if (const Result result{ApplyAutomaticCorrection(system, value)}; result != ResultSuccess) {
return result;
}
@@ -39,27 +38,27 @@ ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::Syst
return ResultSuccess;
}
-ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
- SystemClockContext& ctx) const {
- if (const ResultCode result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) {
+Result StandardUserSystemClockCore::GetClockContext(Core::System& system,
+ SystemClockContext& ctx) const {
+ if (const Result result{ApplyAutomaticCorrection(system, false)}; result != ResultSuccess) {
return result;
}
return local_system_clock_core.GetClockContext(system, ctx);
}
-ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext&) {
- UNREACHABLE();
+Result StandardUserSystemClockCore::Flush(const SystemClockContext&) {
+ UNIMPLEMENTED();
return ERROR_NOT_IMPLEMENTED;
}
-ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
- UNREACHABLE();
+Result StandardUserSystemClockCore::SetClockContext(const SystemClockContext&) {
+ UNIMPLEMENTED();
return ERROR_NOT_IMPLEMENTED;
}
-ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
- bool value) const {
+Result StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
+ bool value) const {
if (auto_correction_enabled == value) {
return ResultSuccess;
}
@@ -69,7 +68,7 @@ ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& s
}
SystemClockContext ctx{};
- if (const ResultCode result{network_system_clock_core.GetClockContext(system, ctx)};
+ if (const Result result{network_system_clock_core.GetClockContext(system, ctx)};
result != ResultSuccess) {
return result;
}
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h
index b7cb2b045..ee6e29487 100644
--- a/src/core/hle/service/time/standard_user_system_clock_core.h
+++ b/src/core/hle/service/time/standard_user_system_clock_core.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -29,9 +28,9 @@ public:
~StandardUserSystemClockCore() override;
- ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value);
+ Result SetAutomaticCorrectionEnabled(Core::System& system, bool value);
- ResultCode GetClockContext(Core::System& system, SystemClockContext& ctx) const override;
+ Result GetClockContext(Core::System& system, SystemClockContext& ctx) const override;
bool IsAutomaticCorrectionEnabled() const {
return auto_correction_enabled;
@@ -42,11 +41,11 @@ public:
}
protected:
- ResultCode Flush(const SystemClockContext&) override;
+ Result Flush(const SystemClockContext&) override;
- ResultCode SetClockContext(const SystemClockContext&) override;
+ Result SetClockContext(const SystemClockContext&) override;
- ResultCode ApplyAutomaticCorrection(Core::System& system, bool value) const;
+ Result ApplyAutomaticCorrection(Core::System& system, bool value) const;
const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const {
return auto_correction_time;
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h
index 5ee2c0e0a..2867c351c 100644
--- a/src/core/hle/service/time/steady_clock_core.h
+++ b/src/core/hle/service/time/steady_clock_core.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp
index f656fab1c..a649bed3a 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.cpp
+++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/time/errors.h"
@@ -31,8 +30,8 @@ void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
}
}
-ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
- ResultCode result{ResultSuccess};
+Result SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
+ Result result{ResultSuccess};
if (NeedUpdate(value)) {
context = value;
@@ -48,7 +47,7 @@ ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& va
return result;
}
-ResultCode SystemClockContextUpdateCallback::Update() {
+Result SystemClockContextUpdateCallback::Update() {
return ResultSuccess;
}
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
index 6936397a5..9c6caf196 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.h
+++ b/src/core/hle/service/time/system_clock_context_update_callback.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -29,10 +28,10 @@ public:
void BroadcastOperationEvent();
- ResultCode Update(const SystemClockContext& value);
+ Result Update(const SystemClockContext& value);
protected:
- virtual ResultCode Update();
+ virtual Result Update();
SystemClockContext context{};
diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp
index 5c2354cdd..da078241f 100644
--- a/src/core/hle/service/time/system_clock_core.cpp
+++ b/src/core/hle/service/time/system_clock_core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/time/steady_clock_core.h"
#include "core/hle/service/time/system_clock_context_update_callback.h"
@@ -15,13 +14,13 @@ SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core_)
SystemClockCore::~SystemClockCore() = default;
-ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
+Result SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
posix_time = 0;
const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
SystemClockContext clock_context{};
- if (const ResultCode result{GetClockContext(system, clock_context)}; result != ResultSuccess) {
+ if (const Result result{GetClockContext(system, clock_context)}; result != ResultSuccess) {
return result;
}
@@ -34,26 +33,26 @@ ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time
return ResultSuccess;
}
-ResultCode SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
+Result SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
const SystemClockContext clock_context{posix_time - current_time_point.time_point,
current_time_point};
- if (const ResultCode result{SetClockContext(clock_context)}; result != ResultSuccess) {
+ if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) {
return result;
}
return Flush(clock_context);
}
-ResultCode SystemClockCore::Flush(const SystemClockContext& clock_context) {
+Result SystemClockCore::Flush(const SystemClockContext& clock_context) {
if (!system_clock_context_update_callback) {
return ResultSuccess;
}
return system_clock_context_update_callback->Update(clock_context);
}
-ResultCode SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) {
- if (const ResultCode result{SetClockContext(clock_context)}; result != ResultSuccess) {
+Result SystemClockCore::SetSystemClockContext(const SystemClockContext& clock_context) {
+ if (const Result result{SetClockContext(clock_context)}; result != ResultSuccess) {
return result;
}
return Flush(clock_context);
diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h
index b9237ad28..8cb34126f 100644
--- a/src/core/hle/service/time/system_clock_core.h
+++ b/src/core/hle/service/time/system_clock_core.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -30,28 +29,28 @@ public:
return steady_clock_core;
}
- ResultCode GetCurrentTime(Core::System& system, s64& posix_time) const;
+ Result GetCurrentTime(Core::System& system, s64& posix_time) const;
- ResultCode SetCurrentTime(Core::System& system, s64 posix_time);
+ Result SetCurrentTime(Core::System& system, s64 posix_time);
- virtual ResultCode GetClockContext([[maybe_unused]] Core::System& system,
- SystemClockContext& value) const {
+ virtual Result GetClockContext([[maybe_unused]] Core::System& system,
+ SystemClockContext& value) const {
value = context;
return ResultSuccess;
}
- virtual ResultCode SetClockContext(const SystemClockContext& value) {
+ virtual Result SetClockContext(const SystemClockContext& value) {
context = value;
return ResultSuccess;
}
- virtual ResultCode Flush(const SystemClockContext& clock_context);
+ virtual Result Flush(const SystemClockContext& clock_context);
void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) {
system_clock_context_update_callback = std::move(callback);
}
- ResultCode SetSystemClockContext(const SystemClockContext& context);
+ Result SetSystemClockContext(const SystemClockContext& context);
bool IsInitialized() const {
return is_initialized;
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
index 47d4ab980..27600413e 100644
--- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h
index 1a5a53fd7..491185dc3 100644
--- a/src/core/hle/service/time/tick_based_steady_clock_core.h
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 4d8823b5a..f77cdbb43 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/core.h"
@@ -44,8 +43,7 @@ private:
}
s64 posix_time{};
- if (const ResultCode result{clock_core.GetCurrentTime(system, posix_time)};
- result.IsError()) {
+ if (const Result result{clock_core.GetCurrentTime(system, posix_time)}; result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
@@ -66,7 +64,7 @@ private:
}
Clock::SystemClockContext system_clock_context{};
- if (const ResultCode result{clock_core.GetClockContext(system, system_clock_context)};
+ if (const Result result{clock_core.GetClockContext(system, system_clock_context)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@@ -117,7 +115,7 @@ private:
Clock::SteadyClockCore& clock_core;
};
-ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
+Result Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
Kernel::KThread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, Clock::TimeType type,
Clock::ClockSnapshot& clock_snapshot) {
@@ -130,7 +128,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
clock_snapshot.type = type;
- if (const ResultCode result{
+ if (const Result result{
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
clock_snapshot.location_name)};
result != ResultSuccess) {
@@ -139,7 +137,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
clock_snapshot.user_context = user_context;
- if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
+ if (const Result result{Clock::ClockSnapshot::GetCurrentTime(
clock_snapshot.user_time, clock_snapshot.steady_clock_time_point,
clock_snapshot.user_context)};
result != ResultSuccess) {
@@ -147,7 +145,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
}
TimeZone::CalendarInfo userCalendarInfo{};
- if (const ResultCode result{
+ if (const Result result{
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
clock_snapshot.user_time, userCalendarInfo)};
result != ResultSuccess) {
@@ -166,7 +164,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
}
TimeZone::CalendarInfo networkCalendarInfo{};
- if (const ResultCode result{
+ if (const Result result{
time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
clock_snapshot.network_time, networkCalendarInfo)};
result != ResultSuccess) {
@@ -263,7 +261,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called, type={}", type);
Clock::SystemClockContext user_context{};
- if (const ResultCode result{
+ if (const Result result{
system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
user_context)};
result.IsError()) {
@@ -273,7 +271,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
Clock::SystemClockContext network_context{};
- if (const ResultCode result{
+ if (const Result result{
system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
system, network_context)};
result.IsError()) {
@@ -283,7 +281,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
Clock::ClockSnapshot clock_snapshot{};
- if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
+ if (const Result result{GetClockSnapshotFromSystemClockContextInternal(
&ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -309,7 +307,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques
LOG_DEBUG(Service_Time, "called, type={}", type);
Clock::ClockSnapshot clock_snapshot{};
- if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
+ if (const Result result{GetClockSnapshotFromSystemClockContextInternal(
&ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -366,7 +364,7 @@ void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) {
Clock::TimeSpanType time_span_type{};
s64 span{};
- if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween(
+ if (const Result result{snapshot_a.steady_clock_time_point.GetSpanBetween(
snapshot_b.steady_clock_time_point, span)};
result != ResultSuccess) {
if (snapshot_a.network_time && snapshot_b.network_time) {
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 30e2cd369..76a46cfc7 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -37,7 +36,7 @@ public:
void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
private:
- ResultCode GetClockSnapshotFromSystemClockContextInternal(
+ Result GetClockSnapshotFromSystemClockContextInternal(
Kernel::KThread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, Clock::TimeType type,
Clock::ClockSnapshot& cloc_snapshot);
diff --git a/src/core/hle/service/time/time_interface.cpp b/src/core/hle/service/time/time_interface.cpp
index bb7b6b5c1..0c53e98ee 100644
--- a/src/core/hle/service/time/time_interface.cpp
+++ b/src/core/hle/service/time/time_interface.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/time/time_interface.h"
diff --git a/src/core/hle/service/time/time_interface.h b/src/core/hle/service/time/time_interface.h
index c41766f1a..ceeb0e5ef 100644
--- a/src/core/hle/service/time/time_interface.h
+++ b/src/core/hle/service/time/time_interface.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index 00f1ae8cf..28667710e 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <ctime>
@@ -112,7 +111,7 @@ struct TimeManager::Impl final {
FileSys::VirtualFile& vfs_file) {
if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
location_name, vfs_file) != ResultSuccess) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
@@ -156,7 +155,7 @@ struct TimeManager::Impl final {
} else {
if (standard_local_system_clock_core.SetCurrentTime(system_, posix_time) !=
ResultSuccess) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
}
@@ -171,7 +170,7 @@ struct TimeManager::Impl final {
if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
ResultSuccess) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
@@ -184,7 +183,7 @@ struct TimeManager::Impl final {
Clock::SteadyClockTimePoint steady_clock_time_point) {
if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
system_, is_automatic_correction_enabled) != ResultSuccess) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
@@ -204,7 +203,7 @@ struct TimeManager::Impl final {
if (GetStandardLocalSystemClockCore()
.SetCurrentTime(system_, timespan.ToSeconds())
.IsError()) {
- UNREACHABLE();
+ ASSERT(false);
return;
}
}
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
index 2404067c0..4f046f266 100644
--- a/src/core/hle/service/time/time_manager.h
+++ b/src/core/hle/service/time/time_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index ed9f75ed6..a3aa0e77f 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/core_timing.h"
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
index 9307ea795..561685acd 100644
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index c634b6abd..afbfe9715 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <sstream>
@@ -91,10 +90,10 @@ void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
}
}
-ResultCode TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
- const std::string& location_name) const {
+Result TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
+ const std::string& location_name) const {
FileSys::VirtualFile vfs_file;
- if (const ResultCode result{GetTimeZoneInfoFile(location_name, vfs_file)};
+ if (const Result result{GetTimeZoneInfoFile(location_name, vfs_file)};
result != ResultSuccess) {
return result;
}
@@ -107,8 +106,8 @@ bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_nam
location_name_cache.end();
}
-ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
- FileSys::VirtualFile& vfs_file) const {
+Result TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) const {
if (!IsLocationNameValid(location_name)) {
return ERROR_TIME_NOT_FOUND;
}
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
index cfa601084..3d94b6428 100644
--- a/src/core/hle/service/time/time_zone_content_manager.h
+++ b/src/core/hle/service/time/time_zone_content_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -33,12 +32,12 @@ public:
return time_zone_manager;
}
- ResultCode LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
+ Result LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
private:
bool IsLocationNameValid(const std::string& location_name) const;
- ResultCode GetTimeZoneInfoFile(const std::string& location_name,
- FileSys::VirtualFile& vfs_file) const;
+ Result GetTimeZoneInfoFile(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) const;
Core::System& system;
TimeZoneManager time_zone_manager;
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 2989cee5e..2aa675df9 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <climits>
@@ -111,10 +110,9 @@ static constexpr s64 GetLeapDaysFromYear(s64 year) {
}
}
-static constexpr int GetMonthLength(bool is_leap_year, int month) {
- constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
- constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30,
- 31, 31, 30, 31, 30, 31};
+static constexpr s8 GetMonthLength(bool is_leap_year, int month) {
+ constexpr std::array<s8, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ constexpr std::array<s8, 12> month_lengths_leap{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
}
@@ -281,7 +279,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) {
break;
}
default:
- UNREACHABLE();
+ ASSERT(false);
}
return value + rule.transition_time + offset;
}
@@ -668,8 +666,8 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
return true;
}
-static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
- CalendarAdditionalInfo& calendar_additional_info) {
+static Result CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
+ CalendarAdditionalInfo& calendar_additional_info) {
s64 year{epoch_year};
s64 time_days{time / seconds_per_day};
s64 remaining_seconds{time % seconds_per_day};
@@ -743,9 +741,9 @@ static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInter
return ResultSuccess;
}
-static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
- CalendarTimeInternal& calendar_time,
- CalendarAdditionalInfo& calendar_additional_info) {
+static Result ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
+ CalendarTimeInternal& calendar_time,
+ CalendarAdditionalInfo& calendar_additional_info) {
if ((rules.go_ahead && time < rules.ats[0]) ||
(rules.go_back && time > rules.ats[rules.time_count - 1])) {
s64 seconds{};
@@ -768,7 +766,7 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) {
return ERROR_TIME_NOT_FOUND;
}
- if (const ResultCode result{
+ if (const Result result{
ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)};
result != ResultSuccess) {
return result;
@@ -799,8 +797,8 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
tti_index = rules.types[low - 1];
}
- if (const ResultCode result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
- calendar_time, calendar_additional_info)};
+ if (const Result result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
+ calendar_time, calendar_additional_info)};
result != ResultSuccess) {
return result;
}
@@ -813,9 +811,9 @@ static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
return ResultSuccess;
}
-static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
+static Result ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
CalendarTimeInternal calendar_time{};
- const ResultCode result{
+ const Result result{
ToCalendarTimeInternal(rules, time, calendar_time, calendar.additional_info)};
calendar.time.year = static_cast<s16>(calendar_time.year);
@@ -832,13 +830,13 @@ static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, Calend
TimeZoneManager::TimeZoneManager() = default;
TimeZoneManager::~TimeZoneManager() = default;
-ResultCode TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
- CalendarInfo& calendar) const {
+Result TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
+ CalendarInfo& calendar) const {
return ToCalendarTimeImpl(rules, time, calendar);
}
-ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
- FileSys::VirtualFile& vfs_file) {
+Result TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file) {
TimeZoneRule rule{};
if (ParseTimeZoneBinary(rule, vfs_file)) {
device_location_name = location_name;
@@ -848,12 +846,12 @@ ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::str
return ERROR_TIME_ZONE_CONVERSION_FAILED;
}
-ResultCode TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
+Result TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
time_zone_update_time_point = value;
return ResultSuccess;
}
-ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
+Result TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
if (is_initialized) {
return ToCalendarTime(time_zone_rule, time, calendar);
} else {
@@ -861,16 +859,16 @@ ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& ca
}
}
-ResultCode TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
- FileSys::VirtualFile& vfs_file) const {
+Result TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
+ FileSys::VirtualFile& vfs_file) const {
if (!ParseTimeZoneBinary(rules, vfs_file)) {
return ERROR_TIME_ZONE_CONVERSION_FAILED;
}
return ResultSuccess;
}
-ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
- const CalendarTime& calendar_time, s64& posix_time) const {
+Result TimeZoneManager::ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
+ s64& posix_time) const {
posix_time = 0;
CalendarTimeInternal internal_time{
@@ -1022,8 +1020,8 @@ ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
return ResultSuccess;
}
-ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time,
- s64& posix_time) const {
+Result TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_time,
+ s64& posix_time) const {
if (is_initialized) {
return ToPosixTime(time_zone_rule, calendar_time, posix_time);
}
@@ -1031,7 +1029,7 @@ ResultCode TimeZoneManager::ToPosixTimeWithMyRule(const CalendarTime& calendar_t
return ERROR_UNINITIALIZED_CLOCK;
}
-ResultCode TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
+Result TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
if (!is_initialized) {
return ERROR_UNINITIALIZED_CLOCK;
}
diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h
index aaab0a1e0..5ebd4035e 100644
--- a/src/core/hle/service/time/time_zone_manager.h
+++ b/src/core/hle/service/time/time_zone_manager.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -30,16 +29,16 @@ public:
is_initialized = true;
}
- ResultCode SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
- FileSys::VirtualFile& vfs_file);
- ResultCode SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
- ResultCode GetDeviceLocationName(TimeZone::LocationName& value) const;
- ResultCode ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
- ResultCode ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
- ResultCode ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
- ResultCode ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
- s64& posix_time) const;
- ResultCode ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const;
+ Result SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
+ FileSys::VirtualFile& vfs_file);
+ Result SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
+ Result GetDeviceLocationName(TimeZone::LocationName& value) const;
+ Result ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
+ Result ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
+ Result ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
+ Result ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
+ s64& posix_time) const;
+ Result ToPosixTimeWithMyRule(const CalendarTime& calendar_time, s64& posix_time) const;
private:
bool is_initialized{};
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
index 3871e7316..961040bfc 100644
--- a/src/core/hle/service/time/time_zone_service.cpp
+++ b/src/core/hle/service/time/time_zone_service.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
@@ -33,7 +32,7 @@ void ITimeZoneService::GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
TimeZone::LocationName location_name{};
- if (const ResultCode result{
+ if (const Result result{
time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -62,7 +61,7 @@ void ITimeZoneService::LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
TimeZone::TimeZoneRule time_zone_rule{};
- if (const ResultCode result{
+ if (const Result result{
time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -89,7 +88,7 @@ void ITimeZoneService::ToCalendarTime(Kernel::HLERequestContext& ctx) {
std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
TimeZone::CalendarInfo calendar_info{};
- if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
+ if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
time_zone_rule, posix_time, calendar_info)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -109,7 +108,7 @@ void ITimeZoneService::ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx)
LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
TimeZone::CalendarInfo calendar_info{};
- if (const ResultCode result{
+ if (const Result result{
time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules(
posix_time, calendar_info)};
result != ResultSuccess) {
@@ -132,7 +131,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule));
s64 posix_time{};
- if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
+ if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
time_zone_rule, calendar_time, posix_time)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -155,9 +154,8 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
s64 posix_time{};
- if (const ResultCode result{
- time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(calendar_time,
- posix_time)};
+ if (const Result result{time_zone_content_manager.GetTimeZoneManager().ToPosixTimeWithMyRule(
+ calendar_time, posix_time)};
result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h
index 2c9b97603..f151f4b56 100644
--- a/src/core/hle/service/time/time_zone_service.h
+++ b/src/core/hle/service/time/time_zone_service.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h
index d39103253..eb4fb52d1 100644
--- a/src/core/hle/service/time/time_zone_types.h
+++ b/src/core/hle/service/time/time_zone_types.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
index 0747c33cd..ac46a406c 100644
--- a/src/core/hle/service/usb/usb.cpp
+++ b/src/core/hle/service/usb/usb.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h
index fc366df34..b41b9684c 100644
--- a/src/core/hle/service/usb/usb.h
+++ b/src/core/hle/service/usb/usb.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index b7705c02a..288aafaaf 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <utility>
@@ -13,14 +12,38 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/nvdrv/core/container.h"
+#include "core/hle/service/nvflinger/buffer_item_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_consumer.h"
+#include "core/hle/service/nvflinger/buffer_queue_core.h"
+#include "core/hle/service/nvflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
+#include "core/hle/service/vi/vi_results.h"
namespace Service::VI {
-Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
- Core::System& system_)
- : display_id{id}, name{std::move(name_)}, service_context{service_context_} {
+struct BufferQueue {
+ std::shared_ptr<android::BufferQueueCore> core;
+ std::unique_ptr<android::BufferQueueProducer> producer;
+ std::unique_ptr<android::BufferQueueConsumer> consumer;
+};
+
+static BufferQueue CreateBufferQueue(KernelHelpers::ServiceContext& service_context,
+ Service::Nvidia::NvCore::NvMap& nvmap) {
+ auto buffer_queue_core = std::make_shared<android::BufferQueueCore>();
+ return {
+ buffer_queue_core,
+ std::make_unique<android::BufferQueueProducer>(service_context, buffer_queue_core, nvmap),
+ std::make_unique<android::BufferQueueConsumer>(buffer_queue_core, nvmap)};
+}
+
+Display::Display(u64 id, std::string name_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
+ KernelHelpers::ServiceContext& service_context_, Core::System& system_)
+ : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_},
+ service_context{service_context_} {
vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
}
@@ -36,29 +59,48 @@ const Layer& Display::GetLayer(std::size_t index) const {
return *layers.at(index);
}
-Kernel::KReadableEvent& Display::GetVSyncEvent() {
- return vsync_event->GetReadableEvent();
+ResultVal<Kernel::KReadableEvent*> Display::GetVSyncEvent() {
+ if (got_vsync_event) {
+ return ResultPermissionDenied;
+ }
+
+ got_vsync_event = true;
+
+ return GetVSyncEventUnchecked();
+}
+
+Kernel::KReadableEvent* Display::GetVSyncEventUnchecked() {
+ return &vsync_event->GetReadableEvent();
}
void Display::SignalVSyncEvent() {
vsync_event->GetWritableEvent().Signal();
}
-void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) {
- // TODO(Subv): Support more than 1 layer.
+void Display::CreateLayer(u64 layer_id, u32 binder_id,
+ Service::Nvidia::NvCore::Container& nv_core) {
ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment");
- layers.emplace_back(std::make_shared<Layer>(layer_id, buffer_queue));
+ auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile());
+
+ auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer));
+ buffer_item_consumer->Connect(false);
+
+ layers.emplace_back(std::make_unique<Layer>(layer_id, binder_id, *core, *producer,
+ std::move(buffer_item_consumer)));
+
+ hos_binder_driver_server.RegisterProducer(std::move(producer));
}
void Display::CloseLayer(u64 layer_id) {
- std::erase_if(layers, [layer_id](const auto& layer) { return layer->GetID() == layer_id; });
+ std::erase_if(layers,
+ [layer_id](const auto& layer) { return layer->GetLayerId() == layer_id; });
}
Layer* Display::FindLayer(u64 layer_id) {
const auto itr =
- std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) {
- return layer->GetID() == layer_id;
+ std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
+ return layer->GetLayerId() == layer_id;
});
if (itr == layers.end()) {
@@ -70,8 +112,8 @@ Layer* Display::FindLayer(u64 layer_id) {
const Layer* Display::FindLayer(u64 layer_id) const {
const auto itr =
- std::find_if(layers.begin(), layers.end(), [layer_id](const std::shared_ptr<Layer>& layer) {
- return layer->GetID() == layer_id;
+ std::find_if(layers.begin(), layers.end(), [layer_id](const std::unique_ptr<Layer>& layer) {
+ return layer->GetLayerId() == layer_id;
});
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..33d5f398c 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,17 +9,28 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
+#include "core/hle/result.h"
namespace Kernel {
class KEvent;
}
-namespace Service::NVFlinger {
-class BufferQueue;
+namespace Service::android {
+class BufferQueueProducer;
}
+
namespace Service::KernelHelpers {
class ServiceContext;
-} // namespace Service::KernelHelpers
+}
+
+namespace Service::NVFlinger {
+class HosBinderDriverServer;
+}
+
+namespace Service::Nvidia::NvCore {
+class Container;
+class NvMap;
+} // namespace Service::Nvidia::NvCore
namespace Service::VI {
@@ -35,12 +45,13 @@ public:
/// Constructs a display with a given unique ID and name.
///
/// @param id The unique ID for this display.
+ /// @param hos_binder_driver_server_ NVFlinger HOSBinderDriver server instance.
/// @param service_context_ The ServiceContext for the owning service.
/// @param name_ The name for this display.
/// @param system_ The global system instance.
///
- Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
- Core::System& system_);
+ Display(u64 id, std::string name_, NVFlinger::HosBinderDriverServer& hos_binder_driver_server_,
+ KernelHelpers::ServiceContext& service_context_, Core::System& system_);
~Display();
/// Gets the unique ID assigned to this display.
@@ -64,18 +75,30 @@ public:
/// Gets a layer for this display based off an index.
const Layer& GetLayer(std::size_t index) const;
- /// Gets the readable vsync event.
- Kernel::KReadableEvent& GetVSyncEvent();
+ std::size_t GetNumLayers() const {
+ return layers.size();
+ }
+
+ /**
+ * Gets the internal vsync event.
+ *
+ * @returns The internal Vsync event if it has not yet been retrieved,
+ * VI::ResultPermissionDenied otherwise.
+ */
+ [[nodiscard]] ResultVal<Kernel::KReadableEvent*> GetVSyncEvent();
+
+ /// Gets the internal vsync event.
+ Kernel::KReadableEvent* GetVSyncEventUnchecked();
/// Signals the internal vsync event.
void SignalVSyncEvent();
/// Creates and adds a layer to this display with the given ID.
///
- /// @param layer_id The ID to assign to the created layer.
- /// @param buffer_queue The buffer queue for the layer instance to use.
+ /// @param layer_id The ID to assign to the created layer.
+ /// @param binder_id The ID assigned to the buffer queue.
///
- void CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue);
+ void CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& core);
/// Closes and removes a layer from this display with the given ID.
///
@@ -104,10 +127,12 @@ public:
private:
u64 display_id;
std::string name;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
KernelHelpers::ServiceContext& service_context;
- std::vector<std::shared_ptr<Layer>> layers;
+ std::vector<std::unique_ptr<Layer>> layers;
Kernel::KEvent* vsync_event{};
+ bool got_vsync_event{false};
};
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/layer/vi_layer.cpp b/src/core/hle/service/vi/layer/vi_layer.cpp
index 9bc382587..9ae2e0e44 100644
--- a/src/core/hle/service/vi/layer/vi_layer.cpp
+++ b/src/core/hle/service/vi/layer/vi_layer.cpp
@@ -1,12 +1,15 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/hle/service/vi/layer/vi_layer.h"
namespace Service::VI {
-Layer::Layer(u64 id, NVFlinger::BufferQueue& queue) : layer_id{id}, buffer_queue{queue} {}
+Layer::Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
+ android::BufferQueueProducer& binder_,
+ std::shared_ptr<android::BufferItemConsumer>&& consumer_)
+ : layer_id{layer_id_}, binder_id{binder_id_}, core{core_}, binder{binder_}, consumer{std::move(
+ consumer_)} {}
Layer::~Layer() = default;
diff --git a/src/core/hle/service/vi/layer/vi_layer.h b/src/core/hle/service/vi/layer/vi_layer.h
index ebdd85505..8cf1b5275 100644
--- a/src/core/hle/service/vi/layer/vi_layer.h
+++ b/src/core/hle/service/vi/layer/vi_layer.h
@@ -1,14 +1,17 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
+#include <memory>
+
#include "common/common_types.h"
-namespace Service::NVFlinger {
-class BufferQueue;
-}
+namespace Service::android {
+class BufferItemConsumer;
+class BufferQueueCore;
+class BufferQueueProducer;
+} // namespace Service::android
namespace Service::VI {
@@ -17,10 +20,13 @@ class Layer {
public:
/// Constructs a layer with a given ID and buffer queue.
///
- /// @param id The ID to assign to this layer.
- /// @param queue The buffer queue for this layer to use.
+ /// @param layer_id_ The ID to assign to this layer.
+ /// @param binder_id_ The binder ID to assign to this layer.
+ /// @param binder_ The buffer producer queue for this layer to use.
///
- Layer(u64 id, NVFlinger::BufferQueue& queue);
+ Layer(u64 layer_id_, u32 binder_id_, android::BufferQueueCore& core_,
+ android::BufferQueueProducer& binder_,
+ std::shared_ptr<android::BufferItemConsumer>&& consumer_);
~Layer();
Layer(const Layer&) = delete;
@@ -30,23 +36,47 @@ public:
Layer& operator=(Layer&&) = delete;
/// Gets the ID for this layer.
- u64 GetID() const {
+ u64 GetLayerId() const {
return layer_id;
}
+ /// Gets the binder ID for this layer.
+ u32 GetBinderId() const {
+ return binder_id;
+ }
+
/// Gets a reference to the buffer queue this layer is using.
- NVFlinger::BufferQueue& GetBufferQueue() {
- return buffer_queue;
+ android::BufferQueueProducer& GetBufferQueue() {
+ return binder;
}
/// Gets a const reference to the buffer queue this layer is using.
- const NVFlinger::BufferQueue& GetBufferQueue() const {
- return buffer_queue;
+ const android::BufferQueueProducer& GetBufferQueue() const {
+ return binder;
+ }
+
+ android::BufferItemConsumer& GetConsumer() {
+ return *consumer;
+ }
+
+ const android::BufferItemConsumer& GetConsumer() const {
+ return *consumer;
+ }
+
+ android::BufferQueueCore& Core() {
+ return core;
+ }
+
+ const android::BufferQueueCore& Core() const {
+ return core;
}
private:
- u64 layer_id;
- NVFlinger::BufferQueue& buffer_queue;
+ const u64 layer_id;
+ const u32 binder_id;
+ android::BufferQueueCore& core;
+ android::BufferQueueProducer& binder;
+ std::shared_ptr<android::BufferItemConsumer> consumer;
};
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 75ee3e5e4..9c917cacf 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
@@ -22,21 +21,20 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/nvdrv/nvdata.h"
-#include "core/hle/service/nvflinger/buffer_queue.h"
+#include "core/hle/service/nvflinger/binder.h"
+#include "core/hle/service/nvflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvflinger/nvflinger.h"
+#include "core/hle/service/nvflinger/parcel.h"
#include "core/hle/service/service.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
+#include "core/hle/service/vi/vi_results.h"
#include "core/hle/service/vi/vi_s.h"
#include "core/hle/service/vi/vi_u.h"
namespace Service::VI {
-constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
-constexpr ResultCode ERR_PERMISSION_DENIED{ErrorModule::VI, 5};
-constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
-constexpr ResultCode ERR_NOT_FOUND{ErrorModule::VI, 7};
-
struct DisplayInfo {
/// The name of this particular display.
char display_name[0x40]{"Default"};
@@ -57,447 +55,26 @@ struct DisplayInfo {
};
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
-class Parcel {
-public:
- // This default size was chosen arbitrarily.
- static constexpr std::size_t DefaultBufferSize = 0x40;
- Parcel() : buffer(DefaultBufferSize) {}
- explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {}
- virtual ~Parcel() = default;
-
- template <typename T>
- T Read() {
- static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
- ASSERT(read_index + sizeof(T) <= buffer.size());
-
- T val;
- std::memcpy(&val, buffer.data() + read_index, sizeof(T));
- read_index += sizeof(T);
- read_index = Common::AlignUp(read_index, 4);
- return val;
- }
-
- template <typename T>
- T ReadUnaligned() {
- static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
- ASSERT(read_index + sizeof(T) <= buffer.size());
-
- T val;
- std::memcpy(&val, buffer.data() + read_index, sizeof(T));
- read_index += sizeof(T);
- return val;
- }
-
- std::vector<u8> ReadBlock(std::size_t length) {
- ASSERT(read_index + length <= buffer.size());
- const u8* const begin = buffer.data() + read_index;
- const u8* const end = begin + length;
- std::vector<u8> data(begin, end);
- read_index += length;
- read_index = Common::AlignUp(read_index, 4);
- return data;
- }
-
- std::u16string ReadInterfaceToken() {
- [[maybe_unused]] const u32 unknown = Read<u32_le>();
- const u32 length = Read<u32_le>();
-
- std::u16string token{};
-
- for (u32 ch = 0; ch < length + 1; ++ch) {
- token.push_back(ReadUnaligned<u16_le>());
- }
-
- read_index = Common::AlignUp(read_index, 4);
-
- return token;
- }
-
- template <typename T>
- void Write(const T& val) {
- static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
-
- if (buffer.size() < write_index + sizeof(T)) {
- buffer.resize(buffer.size() + sizeof(T) + DefaultBufferSize);
- }
-
- std::memcpy(buffer.data() + write_index, &val, sizeof(T));
- write_index += sizeof(T);
- write_index = Common::AlignUp(write_index, 4);
- }
-
- template <typename T>
- void WriteObject(const T& val) {
- static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable.");
-
- const u32_le size = static_cast<u32>(sizeof(val));
- Write(size);
- // TODO(Subv): Support file descriptors.
- Write<u32_le>(0); // Fd count.
- Write(val);
- }
-
- void Deserialize() {
- ASSERT(buffer.size() > sizeof(Header));
-
- Header header{};
- std::memcpy(&header, buffer.data(), sizeof(Header));
-
- read_index = header.data_offset;
- DeserializeData();
- }
-
- std::vector<u8> Serialize() {
- ASSERT(read_index == 0);
- write_index = sizeof(Header);
-
- SerializeData();
-
- Header header{};
- header.data_size = static_cast<u32_le>(write_index - sizeof(Header));
- header.data_offset = sizeof(Header);
- header.objects_size = 4;
- header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
- std::memcpy(buffer.data(), &header, sizeof(Header));
-
- return buffer;
- }
-
-protected:
- virtual void SerializeData() {}
-
- virtual void DeserializeData() {}
-
-private:
- struct Header {
- u32_le data_size;
- u32_le data_offset;
- u32_le objects_size;
- u32_le objects_offset;
- };
- static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size");
-
- std::vector<u8> buffer;
- std::size_t read_index = 0;
- std::size_t write_index = 0;
-};
-
-class NativeWindow : public Parcel {
-public:
- explicit NativeWindow(u32 id) {
- data.id = id;
- }
- ~NativeWindow() override = default;
-
-protected:
- void SerializeData() override {
- Write(data);
- }
-
-private:
- struct Data {
- u32_le magic = 2;
- u32_le process_id = 1;
- u32_le id;
- INSERT_PADDING_WORDS(3);
- std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
- INSERT_PADDING_WORDS(2);
- };
- static_assert(sizeof(Data) == 0x28, "ParcelData has wrong size");
-
- Data data{};
-};
-
-class IGBPConnectRequestParcel : public Parcel {
-public:
- explicit IGBPConnectRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- }
-
- struct Data {
- u32_le unk;
- u32_le api;
- u32_le producer_controlled_by_app;
- };
-
- Data data;
-};
-
-class IGBPConnectResponseParcel : public Parcel {
+class NativeWindow final {
public:
- explicit IGBPConnectResponseParcel(u32 width, u32 height) {
- data.width = width;
- data.height = height;
- }
- ~IGBPConnectResponseParcel() override = default;
-
-protected:
- void SerializeData() override {
- Write(data);
- }
-
-private:
- struct Data {
- u32_le width;
- u32_le height;
- u32_le transform_hint;
- u32_le num_pending_buffers;
- u32_le status;
- };
- static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
-
- Data data{};
-};
-
-/// Represents a parcel containing one int '0' as its data
-/// Used by DetachBuffer and Disconnect
-class IGBPEmptyResponseParcel : public Parcel {
-protected:
- void SerializeData() override {
- Write(data);
- }
+ constexpr explicit NativeWindow(u32 id_) : id{id_} {}
+ constexpr explicit NativeWindow(const NativeWindow& other) = default;
private:
- struct Data {
- u32_le unk_0{};
- };
-
- Data data{};
-};
-
-class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
-public:
- explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer_)
- : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- if (data.contains_object != 0) {
- buffer_container = Read<BufferContainer>();
- }
- }
-
- struct Data {
- u32_le slot;
- u32_le contains_object;
- };
-
- struct BufferContainer {
- u32_le graphic_buffer_length;
- INSERT_PADDING_WORDS(1);
- NVFlinger::IGBPBuffer buffer{};
- };
-
- Data data{};
- BufferContainer buffer_container{};
-};
-
-class IGBPSetPreallocatedBufferResponseParcel : public Parcel {
-protected:
- void SerializeData() override {
- // TODO(Subv): Find out what this means
- Write<u32>(0);
- }
-};
-
-class IGBPCancelBufferRequestParcel : public Parcel {
-public:
- explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- }
-
- struct Data {
- u32_le slot;
- Service::Nvidia::MultiFence multi_fence;
- };
-
- Data data;
-};
-
-class IGBPCancelBufferResponseParcel : public Parcel {
-protected:
- void SerializeData() override {
- Write<u32>(0); // Success
- }
-};
-
-class IGBPDequeueBufferRequestParcel : public Parcel {
-public:
- explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- }
-
- struct Data {
- u32_le pixel_format;
- u32_le width;
- u32_le height;
- u32_le get_frame_timestamps;
- u32_le usage;
- };
-
- Data data;
-};
-
-class IGBPDequeueBufferResponseParcel : public Parcel {
-public:
- explicit IGBPDequeueBufferResponseParcel(u32 slot_, Nvidia::MultiFence& multi_fence_)
- : slot(slot_), multi_fence(multi_fence_) {}
-
-protected:
- void SerializeData() override {
- Write(slot);
- Write<u32_le>(1);
- WriteObject(multi_fence);
- Write<u32_le>(0);
- }
-
- u32_le slot;
- Service::Nvidia::MultiFence multi_fence;
-};
-
-class IGBPRequestBufferRequestParcel : public Parcel {
-public:
- explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- slot = Read<u32_le>();
- }
-
- u32_le slot;
-};
-
-class IGBPRequestBufferResponseParcel : public Parcel {
-public:
- explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer_) : buffer(buffer_) {}
- ~IGBPRequestBufferResponseParcel() override = default;
-
-protected:
- void SerializeData() override {
- // TODO(Subv): Figure out what this value means, writing non-zero here will make libnx
- // try to read an IGBPBuffer object from the parcel.
- Write<u32_le>(1);
- WriteObject(buffer);
- Write<u32_le>(0);
- }
-
- NVFlinger::IGBPBuffer buffer;
-};
-
-class IGBPQueueBufferRequestParcel : public Parcel {
-public:
- explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- data = Read<Data>();
- }
-
- struct Data {
- u32_le slot;
- INSERT_PADDING_WORDS(3);
- u32_le timestamp;
- s32_le is_auto_timestamp;
- s32_le crop_top;
- s32_le crop_left;
- s32_le crop_right;
- s32_le crop_bottom;
- s32_le scaling_mode;
- NVFlinger::BufferQueue::BufferTransformFlags transform;
- u32_le sticky_transform;
- INSERT_PADDING_WORDS(1);
- u32_le swap_interval;
- Service::Nvidia::MultiFence multi_fence;
-
- Common::Rectangle<int> GetCropRect() const {
- return {crop_left, crop_top, crop_right, crop_bottom};
- }
- };
- static_assert(sizeof(Data) == 96, "ParcelData has wrong size");
-
- Data data;
-};
-
-class IGBPQueueBufferResponseParcel : public Parcel {
-public:
- explicit IGBPQueueBufferResponseParcel(u32 width, u32 height) {
- data.width = width;
- data.height = height;
- }
- ~IGBPQueueBufferResponseParcel() override = default;
-
-protected:
- void SerializeData() override {
- Write(data);
- }
-
-private:
- struct Data {
- u32_le width;
- u32_le height;
- u32_le transform_hint;
- u32_le num_pending_buffers;
- u32_le status;
- };
- static_assert(sizeof(Data) == 20, "ParcelData has wrong size");
-
- Data data{};
-};
-
-class IGBPQueryRequestParcel : public Parcel {
-public:
- explicit IGBPQueryRequestParcel(std::vector<u8> buffer_) : Parcel(std::move(buffer_)) {
- Deserialize();
- }
-
- void DeserializeData() override {
- [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
- type = Read<u32_le>();
- }
-
- u32 type;
-};
-
-class IGBPQueryResponseParcel : public Parcel {
-public:
- explicit IGBPQueryResponseParcel(u32 value_) : value{value_} {}
- ~IGBPQueryResponseParcel() override = default;
-
-protected:
- void SerializeData() override {
- Write(value);
- }
-
-private:
- u32_le value;
+ const u32 magic = 2;
+ const u32 process_id = 1;
+ const u32 id;
+ INSERT_PADDING_WORDS(3);
+ std::array<u8, 8> dispdrv = {'d', 'i', 's', 'p', 'd', 'r', 'v', '\0'};
+ INSERT_PADDING_WORDS(2);
};
+static_assert(sizeof(NativeWindow) == 0x28, "NativeWindow has wrong size");
class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
public:
- explicit IHOSBinderDriver(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "IHOSBinderDriver"}, nv_flinger(nv_flinger_) {
+ explicit IHOSBinderDriver(Core::System& system_, NVFlinger::HosBinderDriverServer& server_)
+ : ServiceFramework{system_, "IHOSBinderDriver", ServiceThreadType::CreateNew},
+ server(server_) {
static const FunctionInfo functions[] = {
{0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
{1, &IHOSBinderDriver::AdjustRefcount, "AdjustRefcount"},
@@ -508,147 +85,16 @@ public:
}
private:
- enum class TransactionId {
- RequestBuffer = 1,
- SetBufferCount = 2,
- DequeueBuffer = 3,
- DetachBuffer = 4,
- DetachNextBuffer = 5,
- AttachBuffer = 6,
- QueueBuffer = 7,
- CancelBuffer = 8,
- Query = 9,
- Connect = 10,
- Disconnect = 11,
-
- AllocateBuffers = 13,
- SetPreallocatedBuffer = 14,
-
- GetBufferHistory = 17
- };
-
void TransactParcel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 id = rp.Pop<u32>();
- const auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
+ const auto transaction = static_cast<android::TransactionId>(rp.Pop<u32>());
const u32 flags = rp.Pop<u32>();
LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
transaction, flags);
- auto& buffer_queue = *nv_flinger.FindBufferQueue(id);
-
- switch (transaction) {
- case TransactionId::Connect: {
- IGBPConnectRequestParcel request{ctx.ReadBuffer()};
- IGBPConnectResponseParcel response{static_cast<u32>(DisplayResolution::UndockedWidth),
- static_cast<u32>(DisplayResolution::UndockedHeight)};
-
- buffer_queue.Connect();
-
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::SetPreallocatedBuffer: {
- IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
-
- buffer_queue.SetPreallocatedBuffer(request.data.slot, request.buffer_container.buffer);
-
- IGBPSetPreallocatedBufferResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::DequeueBuffer: {
- IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
- const u32 width{request.data.width};
- const u32 height{request.data.height};
-
- do {
- if (auto result = buffer_queue.DequeueBuffer(width, height); result) {
- // Buffer is available
- IGBPDequeueBufferResponseParcel response{result->first, *result->second};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- } while (buffer_queue.IsConnected());
-
- break;
- }
- case TransactionId::RequestBuffer: {
- IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
-
- auto& buffer = buffer_queue.RequestBuffer(request.slot);
- IGBPRequestBufferResponseParcel response{buffer};
- ctx.WriteBuffer(response.Serialize());
-
- break;
- }
- case TransactionId::QueueBuffer: {
- IGBPQueueBufferRequestParcel request{ctx.ReadBuffer()};
-
- buffer_queue.QueueBuffer(request.data.slot, request.data.transform,
- request.data.GetCropRect(), request.data.swap_interval,
- request.data.multi_fence);
-
- IGBPQueueBufferResponseParcel response{1280, 720};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::Query: {
- IGBPQueryRequestParcel request{ctx.ReadBuffer()};
-
- const u32 value =
- buffer_queue.Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
-
- IGBPQueryResponseParcel response{value};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::CancelBuffer: {
- IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
-
- buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
-
- IGBPCancelBufferResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::Disconnect: {
- LOG_WARNING(Service_VI, "(STUBBED) called, transaction=Disconnect");
- const auto buffer = ctx.ReadBuffer();
-
- buffer_queue.Disconnect();
-
- IGBPEmptyResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::DetachBuffer: {
- const auto buffer = ctx.ReadBuffer();
-
- IGBPEmptyResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::SetBufferCount: {
- LOG_WARNING(Service_VI, "(STUBBED) called, transaction=SetBufferCount");
- [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
-
- IGBPEmptyResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- case TransactionId::GetBufferHistory: {
- LOG_WARNING(Service_VI, "(STUBBED) called, transaction=GetBufferHistory");
- [[maybe_unused]] const auto buffer = ctx.ReadBuffer();
-
- IGBPEmptyResponseParcel response{};
- ctx.WriteBuffer(response.Serialize());
- break;
- }
- default:
- ASSERT_MSG(false, "Unimplemented");
- }
+ server.TryGetProducer(id)->Transact(ctx, transaction, flags);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -674,13 +120,13 @@ private:
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
- // TODO(Subv): Find out what this actually is.
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(nv_flinger.FindBufferQueue(id)->GetBufferWaitEvent());
+ rb.PushCopyObjects(server.TryGetProducer(id)->GetNativeHandle());
}
- NVFlinger::NVFlinger& nv_flinger;
+private:
+ NVFlinger::HosBinderDriverServer& server;
};
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
@@ -899,7 +345,7 @@ private:
if (!layer_id) {
LOG_ERROR(Service_VI, "Layer not found! display=0x{:016X}", display);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_FOUND);
+ rb.Push(ResultNotFound);
return;
}
@@ -937,7 +383,40 @@ private:
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
public:
- explicit IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_);
+ IApplicationDisplayService(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
+ : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_},
+ hos_binder_driver_server{hos_binder_driver_server_} {
+
+ static const FunctionInfo functions[] = {
+ {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
+ {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
+ {102, &IApplicationDisplayService::GetManagerDisplayService,
+ "GetManagerDisplayService"},
+ {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
+ "GetIndirectDisplayTransactionService"},
+ {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
+ {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
+ {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
+ {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
+ {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
+ {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
+ {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
+ {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
+ {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
+ {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
+ {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
+ {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
+ {2450, &IApplicationDisplayService::GetIndirectLayerImageMap,
+ "GetIndirectLayerImageMap"},
+ {2451, nullptr, "GetIndirectLayerImageCropMap"},
+ {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
+ "GetIndirectLayerImageRequiredMemoryInfo"},
+ {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
+ {5203, nullptr, "GetDisplayVsyncEventForDebug"},
+ };
+ RegisterHandlers(functions);
+ }
private:
enum class ConvertedScaleMode : u64 {
@@ -961,7 +440,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger);
+ rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
}
void GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
@@ -985,7 +464,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IHOSBinderDriver>(system, nv_flinger);
+ rb.PushIpcInterface<IHOSBinderDriver>(system, hos_binder_driver_server);
}
void OpenDisplay(Kernel::HLERequestContext& ctx) {
@@ -1016,7 +495,7 @@ private:
if (!display_id) {
LOG_ERROR(Service_VI, "Display not found! display_name={}", name);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_FOUND);
+ rb.Push(ResultNotFound);
return;
}
@@ -1072,14 +551,14 @@ private:
if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) {
LOG_ERROR(Service_VI, "Invalid scaling mode provided.");
- rb.Push(ERR_OPERATION_FAILED);
+ rb.Push(ResultOperationFailed);
return;
}
if (scaling_mode != NintendoScaleMode::ScaleToWindow &&
scaling_mode != NintendoScaleMode::PreserveAspectRatio) {
LOG_ERROR(Service_VI, "Unsupported scaling mode supplied.");
- rb.Push(ERR_UNSUPPORTED);
+ rb.Push(ResultNotSupported);
return;
}
@@ -1089,7 +568,7 @@ private:
void ListDisplays(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
- DisplayInfo display_info;
+ const DisplayInfo display_info;
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -1112,7 +591,7 @@ private:
if (!display_id) {
LOG_ERROR(Service_VI, "Layer not found! layer_id={}", layer_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_FOUND);
+ rb.Push(ResultNotFound);
return;
}
@@ -1120,12 +599,12 @@ private:
if (!buffer_queue_id) {
LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", *display_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_FOUND);
+ rb.Push(ResultNotFound);
return;
}
- NativeWindow native_window{*buffer_queue_id};
- const auto buffer_size = ctx.WriteBuffer(native_window.Serialize());
+ const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
+ const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
@@ -1158,7 +637,7 @@ private:
if (!layer_id) {
LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_FOUND);
+ rb.Push(ResultNotFound);
return;
}
@@ -1166,12 +645,12 @@ private:
if (!buffer_queue_id) {
LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_FOUND);
+ rb.Push(ResultNotFound);
return;
}
- NativeWindow native_window{*buffer_queue_id};
- const auto buffer_size = ctx.WriteBuffer(native_window.Serialize());
+ const auto parcel = android::Parcel{NativeWindow{*buffer_queue_id}};
+ const auto buffer_size = ctx.WriteBuffer(parcel.Serialize());
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(ResultSuccess);
@@ -1193,19 +672,23 @@ private:
IPC::RequestParser rp{ctx};
const u64 display_id = rp.Pop<u64>();
- LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
+ LOG_DEBUG(Service_VI, "called. display_id={}", display_id);
const auto vsync_event = nv_flinger.FindVsyncEvent(display_id);
- if (!vsync_event) {
- LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
+ if (vsync_event.Failed()) {
+ const auto result = vsync_event.Code();
+ if (result == ResultNotFound) {
+ LOG_ERROR(Service_VI, "Vsync event was not found for display_id={}", display_id);
+ }
+
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_NOT_FOUND);
+ rb.Push(result);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess);
- rb.PushCopyObjects(vsync_event);
+ rb.PushCopyObjects(*vsync_event);
}
void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
@@ -1282,44 +765,14 @@ private:
return ConvertedScaleMode::PreserveAspectRatio;
default:
LOG_ERROR(Service_VI, "Invalid scaling mode specified, mode={}", mode);
- return ERR_OPERATION_FAILED;
+ return ResultOperationFailed;
}
}
NVFlinger::NVFlinger& nv_flinger;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
};
-IApplicationDisplayService::IApplicationDisplayService(Core::System& system_,
- NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "IApplicationDisplayService"}, nv_flinger{nv_flinger_} {
- static const FunctionInfo functions[] = {
- {100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
- {101, &IApplicationDisplayService::GetSystemDisplayService, "GetSystemDisplayService"},
- {102, &IApplicationDisplayService::GetManagerDisplayService, "GetManagerDisplayService"},
- {103, &IApplicationDisplayService::GetIndirectDisplayTransactionService,
- "GetIndirectDisplayTransactionService"},
- {1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
- {1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
- {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
- {1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
- {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
- {1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
- {2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
- {2021, &IApplicationDisplayService::CloseLayer, "CloseLayer"},
- {2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},
- {2031, &IApplicationDisplayService::DestroyStrayLayer, "DestroyStrayLayer"},
- {2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
- {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"},
- {2450, &IApplicationDisplayService::GetIndirectLayerImageMap, "GetIndirectLayerImageMap"},
- {2451, nullptr, "GetIndirectLayerImageCropMap"},
- {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo,
- "GetIndirectLayerImageRequiredMemoryInfo"},
- {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"},
- {5203, nullptr, "GetDisplayVsyncEventForDebug"},
- };
- RegisterHandlers(functions);
-}
-
static bool IsValidServiceAccess(Permission permission, Policy policy) {
if (permission == Permission::User) {
return policy == Policy::User;
@@ -1333,27 +786,33 @@ static bool IsValidServiceAccess(Permission permission, Policy policy) {
}
void detail::GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
- NVFlinger::NVFlinger& nv_flinger, Permission permission) {
+ NVFlinger::NVFlinger& nv_flinger,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
+ Permission permission) {
IPC::RequestParser rp{ctx};
const auto policy = rp.PopEnum<Policy>();
if (!IsValidServiceAccess(permission, policy)) {
LOG_ERROR(Service_VI, "Permission denied for policy {}", policy);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERR_PERMISSION_DENIED);
+ rb.Push(ResultPermissionDenied);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger);
+ rb.PushIpcInterface<IApplicationDisplayService>(system, nv_flinger, hos_binder_driver_server);
}
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
- NVFlinger::NVFlinger& nv_flinger) {
- std::make_shared<VI_M>(system, nv_flinger)->InstallAsService(service_manager);
- std::make_shared<VI_S>(system, nv_flinger)->InstallAsService(service_manager);
- std::make_shared<VI_U>(system, nv_flinger)->InstallAsService(service_manager);
+ NVFlinger::NVFlinger& nv_flinger,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server) {
+ std::make_shared<VI_M>(system, nv_flinger, hos_binder_driver_server)
+ ->InstallAsService(service_manager);
+ std::make_shared<VI_S>(system, nv_flinger, hos_binder_driver_server)
+ ->InstallAsService(service_manager);
+ std::make_shared<VI_U>(system, nv_flinger, hos_binder_driver_server)
+ ->InstallAsService(service_manager);
}
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h
index 2fd7f8e61..fc2d717e7 100644
--- a/src/core/hle/service/vi/vi.h
+++ b/src/core/hle/service/vi/vi.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -15,8 +14,9 @@ class HLERequestContext;
}
namespace Service::NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace Service::NVFlinger
namespace Service::SM {
class ServiceManager;
@@ -47,11 +47,14 @@ enum class Policy {
namespace detail {
void GetDisplayServiceImpl(Kernel::HLERequestContext& ctx, Core::System& system,
- NVFlinger::NVFlinger& nv_flinger, Permission permission);
+ NVFlinger::NVFlinger& nv_flinger,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server,
+ Permission permission);
} // namespace detail
/// Registers all VI services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system,
- NVFlinger::NVFlinger& nv_flinger);
+ NVFlinger::NVFlinger& nv_flinger,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server);
} // 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..1ab7fe4ab 100644
--- a/src/core/hle/service/vi/vi_m.cpp
+++ b/src/core/hle/service/vi/vi_m.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/service/vi/vi.h"
@@ -8,8 +7,10 @@
namespace Service::VI {
-VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_} {
+VI_M::VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
+ : ServiceFramework{system_, "vi:m"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
+ hos_binder_driver_server_} {
static const FunctionInfo functions[] = {
{2, &VI_M::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +23,8 @@ VI_M::~VI_M() = default;
void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::Manager);
+ detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
+ Permission::Manager);
}
} // 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..3bf76d439 100644
--- a/src/core/hle/service/vi/vi_m.h
+++ b/src/core/hle/service/vi/vi_m.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -15,20 +14,23 @@ class HLERequestContext;
}
namespace Service::NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace Service::NVFlinger
namespace Service::VI {
class VI_M final : public ServiceFramework<VI_M> {
public:
- explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_);
+ explicit VI_M(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
~VI_M() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
NVFlinger::NVFlinger& nv_flinger;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
};
} // namespace Service::VI
diff --git a/src/core/hle/service/vi/vi_results.h b/src/core/hle/service/vi/vi_results.h
new file mode 100644
index 000000000..a46c247d2
--- /dev/null
+++ b/src/core/hle/service/vi/vi_results.h
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "core/hle/result.h"
+
+namespace Service::VI {
+
+constexpr Result ResultOperationFailed{ErrorModule::VI, 1};
+constexpr Result ResultPermissionDenied{ErrorModule::VI, 5};
+constexpr Result ResultNotSupported{ErrorModule::VI, 6};
+constexpr Result ResultNotFound{ErrorModule::VI, 7};
+
+} // 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..fd799dac1 100644
--- a/src/core/hle/service/vi/vi_s.cpp
+++ b/src/core/hle/service/vi/vi_s.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/service/vi/vi.h"
@@ -8,8 +7,10 @@
namespace Service::VI {
-VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_} {
+VI_S::VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
+ : ServiceFramework{system_, "vi:s"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
+ hos_binder_driver_server_} {
static const FunctionInfo functions[] = {
{1, &VI_S::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +23,8 @@ VI_S::~VI_S() = default;
void VI_S::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::System);
+ detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
+ Permission::System);
}
} // 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..97503ac7f 100644
--- a/src/core/hle/service/vi/vi_s.h
+++ b/src/core/hle/service/vi/vi_s.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -15,20 +14,23 @@ class HLERequestContext;
}
namespace Service::NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace Service::NVFlinger
namespace Service::VI {
class VI_S final : public ServiceFramework<VI_S> {
public:
- explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_);
+ explicit VI_S(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
~VI_S() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
NVFlinger::NVFlinger& nv_flinger;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
};
} // 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..6cc54bd13 100644
--- a/src/core/hle/service/vi/vi_u.cpp
+++ b/src/core/hle/service/vi/vi_u.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/hle/service/vi/vi.h"
@@ -8,8 +7,10 @@
namespace Service::VI {
-VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_)
- : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_} {
+VI_U::VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_)
+ : ServiceFramework{system_, "vi:u"}, nv_flinger{nv_flinger_}, hos_binder_driver_server{
+ hos_binder_driver_server_} {
static const FunctionInfo functions[] = {
{0, &VI_U::GetDisplayService, "GetDisplayService"},
{1, nullptr, "GetDisplayServiceWithProxyNameExchange"},
@@ -22,7 +23,8 @@ VI_U::~VI_U() = default;
void VI_U::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_VI, "called");
- detail::GetDisplayServiceImpl(ctx, system, nv_flinger, Permission::User);
+ detail::GetDisplayServiceImpl(ctx, system, nv_flinger, hos_binder_driver_server,
+ Permission::User);
}
} // 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..797941bd7 100644
--- a/src/core/hle/service/vi/vi_u.h
+++ b/src/core/hle/service/vi/vi_u.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -15,20 +14,23 @@ class HLERequestContext;
}
namespace Service::NVFlinger {
+class HosBinderDriverServer;
class NVFlinger;
-}
+} // namespace Service::NVFlinger
namespace Service::VI {
class VI_U final : public ServiceFramework<VI_U> {
public:
- explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_);
+ explicit VI_U(Core::System& system_, NVFlinger::NVFlinger& nv_flinger_,
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server_);
~VI_U() override;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
NVFlinger::NVFlinger& nv_flinger;
+ NVFlinger::HosBinderDriverServer& hos_binder_driver_server;
};
} // namespace Service::VI
diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp
index f10b8c853..226e3034c 100644
--- a/src/core/hle/service/wlan/wlan.cpp
+++ b/src/core/hle/service/wlan/wlan.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
diff --git a/src/core/hle/service/wlan/wlan.h b/src/core/hle/service/wlan/wlan.h
index 3899eedbb..535c3bf0d 100644
--- a/src/core/hle/service/wlan/wlan.h
+++ b/src/core/hle/service/wlan/wlan.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/network/network.cpp b/src/core/internal_network/network.cpp
index a3e0664b9..447fbffaa 100644
--- a/src/core/network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -30,9 +29,10 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
-#include "core/network/network.h"
-#include "core/network/network_interface.h"
-#include "core/network/sockets.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+#include "core/internal_network/sockets.h"
+#include "network/network.h"
namespace Network {
@@ -115,7 +115,10 @@ Errno TranslateNativeError(int e) {
return Errno::NETDOWN;
case WSAENETUNREACH:
return Errno::NETUNREACH;
+ case WSAEMSGSIZE:
+ return Errno::MSGSIZE;
default:
+ UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
return Errno::OTHER;
}
}
@@ -126,7 +129,6 @@ using SOCKET = int;
using WSAPOLLFD = pollfd;
using ULONG = u64;
-constexpr SOCKET INVALID_SOCKET = -1;
constexpr SOCKET SOCKET_ERROR = -1;
constexpr int SD_RECEIVE = SHUT_RD;
@@ -207,7 +209,10 @@ Errno TranslateNativeError(int e) {
return Errno::NETDOWN;
case ENETUNREACH:
return Errno::NETUNREACH;
+ case EMSGSIZE:
+ return Errno::MSGSIZE;
default:
+ UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
return Errno::OTHER;
}
}
@@ -330,16 +335,6 @@ PollEvents TranslatePollRevents(short revents) {
return result;
}
-template <typename T>
-Errno SetSockOpt(SOCKET fd, int option, T value) {
- const int result =
- setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
- if (result != SOCKET_ERROR) {
- return Errno::SUCCESS;
- }
- return GetAndLogLastError();
-}
-
} // Anonymous namespace
NetworkInstance::NetworkInstance() {
@@ -351,26 +346,16 @@ NetworkInstance::~NetworkInstance() {
}
std::optional<IPv4Address> GetHostIPv4Address() {
- const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
- const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
- if (network_interfaces.size() == 0) {
- LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+ if (!network_interface.has_value()) {
+ LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
return {};
}
- const auto res =
- std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
- return iface.name == selected_network_interface;
- });
-
- if (res != network_interfaces.end()) {
- char ip_addr[16] = {};
- ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
- return TranslateIPv4(res->ip_address);
- } else {
- LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
- return {};
- }
+ std::array<char, 16> ip_addr = {};
+ ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) !=
+ nullptr);
+ return TranslateIPv4(network_interface->ip_address);
}
std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
@@ -379,7 +364,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
std::vector<WSAPOLLFD> host_pollfds(pollfds.size());
std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) {
WSAPOLLFD result;
- result.fd = fd.socket->fd;
+ result.fd = fd.socket->GetFD();
result.events = TranslatePollEvents(fd.events);
result.revents = 0;
return result;
@@ -413,7 +398,19 @@ Socket::~Socket() {
fd = INVALID_SOCKET;
}
-Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {}
+Socket::Socket(Socket&& rhs) noexcept {
+ fd = std::exchange(rhs.fd, INVALID_SOCKET);
+}
+
+template <typename T>
+Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) {
+ const int result =
+ setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
+ if (result != SOCKET_ERROR) {
+ return Errno::SUCCESS;
+ }
+ return GetAndLogLastError();
+}
Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol));
@@ -424,7 +421,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
return GetAndLogLastError();
}
-std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
+std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
sockaddr addr;
socklen_t addrlen = sizeof(addr);
const SOCKET new_socket = accept(fd, &addr, &addrlen);
@@ -433,12 +430,12 @@ std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
return {AcceptResult{}, GetAndLogLastError()};
}
- AcceptResult result;
- result.socket = std::make_unique<Socket>();
- result.socket->fd = new_socket;
-
ASSERT(addrlen == sizeof(sockaddr_in));
- result.sockaddr_in = TranslateToSockAddrIn(addr);
+
+ AcceptResult result{
+ .socket = std::make_unique<Socket>(new_socket),
+ .sockaddr_in = TranslateToSockAddrIn(addr),
+ };
return {std::move(result), Errno::SUCCESS};
}
@@ -600,6 +597,10 @@ Errno Socket::SetReuseAddr(bool enable) {
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
}
+Errno Socket::SetKeepAlive(bool enable) {
+ return SetSockOpt<u32>(fd, SO_KEEPALIVE, enable ? 1 : 0);
+}
+
Errno Socket::SetBroadcast(bool enable) {
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
}
@@ -631,4 +632,8 @@ bool Socket::IsOpened() const {
return fd != INVALID_SOCKET;
}
+void Socket::HandleProxyPacket(const ProxyPacket& packet) {
+ LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!");
+}
+
} // namespace Network
diff --git a/src/core/network/network.h b/src/core/internal_network/network.h
index e85df3ab7..36994c22e 100644
--- a/src/core/network/network.h
+++ b/src/core/internal_network/network.h
@@ -1,6 +1,5 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -9,6 +8,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
+#include "common/socket_types.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -18,6 +18,7 @@
namespace Network {
+class SocketBase;
class Socket;
/// Error code for network functions
@@ -32,46 +33,11 @@ enum class Errno {
HOSTUNREACH,
NETDOWN,
NETUNREACH,
+ TIMEDOUT,
+ MSGSIZE,
OTHER,
};
-/// Address families
-enum class Domain {
- INET, ///< Address family for IPv4
-};
-
-/// Socket types
-enum class Type {
- STREAM,
- DGRAM,
- RAW,
- SEQPACKET,
-};
-
-/// Protocol values for sockets
-enum class Protocol {
- ICMP,
- TCP,
- UDP,
-};
-
-/// Shutdown mode
-enum class ShutdownHow {
- RD,
- WR,
- RDWR,
-};
-
-/// Array of IPv4 address
-using IPv4Address = std::array<u8, 4>;
-
-/// Cross-platform sockaddr structure
-struct SockAddrIn {
- Domain family;
- IPv4Address ip;
- u16 portno;
-};
-
/// Cross-platform poll fd structure
enum class PollEvents : u16 {
@@ -87,7 +53,7 @@ enum class PollEvents : u16 {
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
- Socket* socket;
+ SocketBase* socket;
PollEvents events;
PollEvents revents;
};
diff --git a/src/core/network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 6811f21b1..057fd3661 100644
--- a/src/core/network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <fstream>
@@ -12,7 +11,7 @@
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
-#include "core/network/network_interface.h"
+#include "core/internal_network/network_interface.h"
#ifdef _WIN32
#include <iphlpapi.h>
@@ -189,7 +188,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
const auto& selected_network_interface = Settings::values.network_interface.GetValue();
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
- if (network_interfaces.size() == 0) {
+ if (network_interfaces.empty()) {
LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
return std::nullopt;
}
@@ -207,4 +206,14 @@ std::optional<NetworkInterface> GetSelectedNetworkInterface() {
return *res;
}
+void SelectFirstNetworkInterface() {
+ const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
+
+ if (network_interfaces.empty()) {
+ return;
+ }
+
+ Settings::values.network_interface.SetValue(network_interfaces[0].name);
+}
+
} // namespace Network
diff --git a/src/core/network/network_interface.h b/src/core/internal_network/network_interface.h
index 980edb2f5..175e61b1f 100644
--- a/src/core/network/network_interface.h
+++ b/src/core/internal_network/network_interface.h
@@ -1,6 +1,5 @@
-// Copyright 2021 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -25,5 +24,6 @@ struct NetworkInterface {
std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
std::optional<NetworkInterface> GetSelectedNetworkInterface();
+void SelectFirstNetworkInterface();
} // namespace Network
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
new file mode 100644
index 000000000..7d5d37bbc
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -0,0 +1,296 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <chrono>
+#include <thread>
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/zstd_compression.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+#include "core/internal_network/socket_proxy.h"
+
+namespace Network {
+
+ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {}
+
+ProxySocket::~ProxySocket() {
+ if (fd == INVALID_SOCKET) {
+ return;
+ }
+ fd = INVALID_SOCKET;
+}
+
+void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
+ if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno ||
+ closed) {
+ return;
+ }
+
+ if (!broadcast && packet.broadcast) {
+ LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode");
+ return;
+ }
+
+ auto decompressed = packet;
+ decompressed.data = Common::Compression::DecompressDataZSTD(packet.data);
+
+ std::lock_guard guard(packets_mutex);
+ received_packets.push(decompressed);
+}
+
+template <typename T>
+Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
+ LOG_DEBUG(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
+ protocol = socket_protocol;
+ SetSockOpt(fd, SO_TYPE, type);
+
+ return Errno::SUCCESS;
+}
+
+std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {AcceptResult{}, Errno::SUCCESS};
+}
+
+Errno ProxySocket::Connect(SockAddrIn addr_in) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {SockAddrIn{}, Errno::SUCCESS};
+}
+
+std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {SockAddrIn{}, Errno::SUCCESS};
+}
+
+Errno ProxySocket::Bind(SockAddrIn addr) {
+ if (is_bound) {
+ LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
+ return Errno::SUCCESS;
+ }
+ local_endpoint = addr;
+ is_bound = true;
+
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Listen(s32 backlog) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Shutdown(ShutdownHow how) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ ASSERT(flags == 0);
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+
+ return {static_cast<s32>(0), Errno::SUCCESS};
+}
+
+std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
+ ASSERT(flags == 0);
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+
+ // TODO (flTobi): Verify the timeout behavior and break when connection is lost
+ const auto timestamp = std::chrono::steady_clock::now();
+ // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a
+ // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set
+ // the timeout to 5s instead
+ const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout;
+ while (true) {
+ {
+ std::lock_guard guard(packets_mutex);
+ if (received_packets.size() > 0) {
+ return ReceivePacket(flags, message, addr, message.size());
+ }
+ }
+
+ if (!blocking) {
+ return {-1, Errno::AGAIN};
+ }
+
+ std::this_thread::yield();
+
+ const auto time_diff = std::chrono::steady_clock::now() - timestamp;
+ const auto time_diff_ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
+
+ if (time_diff_ms > timeout) {
+ return {-1, Errno::TIMEDOUT};
+ }
+ }
+}
+
+std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message,
+ SockAddrIn* addr, std::size_t max_length) {
+ ProxyPacket& packet = received_packets.front();
+ if (addr) {
+ addr->family = Domain::INET;
+ addr->ip = packet.local_endpoint.ip; // The senders ip address
+ addr->portno = packet.local_endpoint.portno; // The senders port number
+ }
+
+ bool peek = (flags & FLAG_MSG_PEEK) != 0;
+ std::size_t read_bytes;
+ if (packet.data.size() > max_length) {
+ read_bytes = max_length;
+ message.clear();
+ std::copy(packet.data.begin(), packet.data.begin() + read_bytes,
+ std::back_inserter(message));
+ message.resize(max_length);
+
+ if (protocol == Protocol::UDP) {
+ if (!peek) {
+ received_packets.pop();
+ }
+ return {-1, Errno::MSGSIZE};
+ } else if (protocol == Protocol::TCP) {
+ std::vector<u8> numArray(packet.data.size() - max_length);
+ std::copy(packet.data.begin() + max_length, packet.data.end(),
+ std::back_inserter(numArray));
+ packet.data = numArray;
+ }
+ } else {
+ read_bytes = packet.data.size();
+ message.clear();
+ std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message));
+ message.resize(max_length);
+ if (!peek) {
+ received_packets.pop();
+ }
+ }
+
+ return {static_cast<u32>(read_bytes), Errno::SUCCESS};
+}
+
+std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+ ASSERT(flags == 0);
+
+ return {static_cast<s32>(0), Errno::SUCCESS};
+}
+
+void ProxySocket::SendPacket(ProxyPacket& packet) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
+ packet.data.size());
+ room_member->SendProxyPacket(packet);
+ }
+ }
+}
+
+std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) {
+ ASSERT(flags == 0);
+
+ if (!is_bound) {
+ LOG_ERROR(Network, "ProxySocket is not bound!");
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+ }
+
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (!room_member->IsConnected()) {
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+ }
+ }
+
+ ProxyPacket packet;
+ packet.local_endpoint = local_endpoint;
+ packet.remote_endpoint = *addr;
+ packet.protocol = protocol;
+ packet.broadcast = broadcast && packet.remote_endpoint.ip[3] == 255;
+
+ auto& ip = local_endpoint.ip;
+ auto ipv4 = Network::GetHostIPv4Address();
+ // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address,
+ // replace it with a "fake" routing address
+ if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ packet.local_endpoint.ip = room_member->GetFakeIpAddress();
+ }
+ }
+
+ packet.data.clear();
+ std::copy(message.begin(), message.end(), std::back_inserter(packet.data));
+
+ SendPacket(packet);
+
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+}
+
+Errno ProxySocket::Close() {
+ fd = INVALID_SOCKET;
+ closed = true;
+
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::SetLinger(bool enable, u32 linger) {
+ struct Linger {
+ u16 linger_enable;
+ u16 linger_time;
+ } values;
+ values.linger_enable = enable ? 1 : 0;
+ values.linger_time = static_cast<u16>(linger);
+
+ return SetSockOpt(fd, SO_LINGER, values);
+}
+
+Errno ProxySocket::SetReuseAddr(bool enable) {
+ return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
+}
+
+Errno ProxySocket::SetBroadcast(bool enable) {
+ broadcast = enable;
+ return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
+}
+
+Errno ProxySocket::SetSndBuf(u32 value) {
+ return SetSockOpt(fd, SO_SNDBUF, value);
+}
+
+Errno ProxySocket::SetKeepAlive(bool enable) {
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::SetRcvBuf(u32 value) {
+ return SetSockOpt(fd, SO_RCVBUF, value);
+}
+
+Errno ProxySocket::SetSndTimeo(u32 value) {
+ send_timeout = value;
+ return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
+}
+
+Errno ProxySocket::SetRcvTimeo(u32 value) {
+ receive_timeout = value;
+ return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
+}
+
+Errno ProxySocket::SetNonBlock(bool enable) {
+ blocking = !enable;
+ return Errno::SUCCESS;
+}
+
+bool ProxySocket::IsOpened() const {
+ return fd != INVALID_SOCKET;
+}
+
+} // namespace Network
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
new file mode 100644
index 000000000..f12b5f567
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.h
@@ -0,0 +1,97 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <mutex>
+#include <vector>
+#include <queue>
+
+#include "common/common_funcs.h"
+#include "core/internal_network/sockets.h"
+#include "network/network.h"
+
+namespace Network {
+
+class ProxySocket : public SocketBase {
+public:
+ YUZU_NON_COPYABLE(ProxySocket);
+ YUZU_NON_MOVEABLE(ProxySocket);
+
+ explicit ProxySocket(RoomNetwork& room_network_) noexcept;
+ ~ProxySocket() override;
+
+ void HandleProxyPacket(const ProxyPacket& packet) override;
+
+ Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
+
+ Errno Close() override;
+
+ std::pair<AcceptResult, Errno> Accept() override;
+
+ Errno Connect(SockAddrIn addr_in) override;
+
+ std::pair<SockAddrIn, Errno> GetPeerName() override;
+
+ std::pair<SockAddrIn, Errno> GetSockName() override;
+
+ Errno Bind(SockAddrIn addr) override;
+
+ Errno Listen(s32 backlog) override;
+
+ Errno Shutdown(ShutdownHow how) override;
+
+ std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
+
+ std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
+
+ std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr,
+ std::size_t max_length);
+
+ std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
+
+ void SendPacket(ProxyPacket& packet);
+
+ std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) override;
+
+ Errno SetLinger(bool enable, u32 linger) override;
+
+ Errno SetReuseAddr(bool enable) override;
+
+ Errno SetBroadcast(bool enable) override;
+
+ Errno SetKeepAlive(bool enable) override;
+
+ Errno SetSndBuf(u32 value) override;
+
+ Errno SetRcvBuf(u32 value) override;
+
+ Errno SetSndTimeo(u32 value) override;
+
+ Errno SetRcvTimeo(u32 value) override;
+
+ Errno SetNonBlock(bool enable) override;
+
+ template <typename T>
+ Errno SetSockOpt(SOCKET fd, int option, T value);
+
+ bool IsOpened() const override;
+
+private:
+ bool broadcast = false;
+ bool closed = false;
+ u32 send_timeout = 0;
+ u32 receive_timeout = 0;
+ bool is_bound = false;
+ SockAddrIn local_endpoint{};
+ bool blocking = true;
+ std::queue<ProxyPacket> received_packets;
+ Protocol protocol;
+
+ std::mutex packets_mutex;
+
+ RoomNetwork& room_network;
+};
+
+} // namespace Network
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
new file mode 100644
index 000000000..2e328c645
--- /dev/null
+++ b/src/core/internal_network/sockets.h
@@ -0,0 +1,174 @@
+// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <utility>
+
+#if defined(_WIN32)
+#elif !YUZU_UNIX
+#error "Platform not implemented"
+#endif
+
+#include "common/common_types.h"
+#include "core/internal_network/network.h"
+#include "network/network.h"
+
+// TODO: C++20 Replace std::vector usages with std::span
+
+namespace Network {
+
+class SocketBase {
+public:
+#ifdef YUZU_UNIX
+ using SOCKET = int;
+ static constexpr SOCKET INVALID_SOCKET = -1;
+ static constexpr SOCKET SOCKET_ERROR = -1;
+#endif
+
+ struct AcceptResult {
+ std::unique_ptr<SocketBase> socket;
+ SockAddrIn sockaddr_in;
+ };
+
+ SocketBase() = default;
+ explicit SocketBase(SOCKET fd_) : fd{fd_} {}
+
+ virtual ~SocketBase() = default;
+
+ virtual SocketBase& operator=(const SocketBase&) = delete;
+
+ // Avoid closing sockets implicitly
+ virtual SocketBase& operator=(SocketBase&&) noexcept = delete;
+
+ virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
+
+ virtual Errno Close() = 0;
+
+ virtual std::pair<AcceptResult, Errno> Accept() = 0;
+
+ virtual Errno Connect(SockAddrIn addr_in) = 0;
+
+ virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
+
+ virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
+
+ virtual Errno Bind(SockAddrIn addr) = 0;
+
+ virtual Errno Listen(s32 backlog) = 0;
+
+ virtual Errno Shutdown(ShutdownHow how) = 0;
+
+ virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0;
+
+ virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message,
+ SockAddrIn* addr) = 0;
+
+ virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0;
+
+ virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) = 0;
+
+ virtual Errno SetLinger(bool enable, u32 linger) = 0;
+
+ virtual Errno SetReuseAddr(bool enable) = 0;
+
+ virtual Errno SetKeepAlive(bool enable) = 0;
+
+ virtual Errno SetBroadcast(bool enable) = 0;
+
+ virtual Errno SetSndBuf(u32 value) = 0;
+
+ virtual Errno SetRcvBuf(u32 value) = 0;
+
+ virtual Errno SetSndTimeo(u32 value) = 0;
+
+ virtual Errno SetRcvTimeo(u32 value) = 0;
+
+ virtual Errno SetNonBlock(bool enable) = 0;
+
+ virtual bool IsOpened() const = 0;
+
+ virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
+
+ [[nodiscard]] SOCKET GetFD() const {
+ return fd;
+ }
+
+protected:
+ SOCKET fd = INVALID_SOCKET;
+};
+
+class Socket : public SocketBase {
+public:
+ Socket() = default;
+ explicit Socket(SOCKET fd_) : SocketBase{fd_} {}
+
+ ~Socket() override;
+
+ Socket(const Socket&) = delete;
+ Socket& operator=(const Socket&) = delete;
+
+ Socket(Socket&& rhs) noexcept;
+
+ // Avoid closing sockets implicitly
+ Socket& operator=(Socket&&) noexcept = delete;
+
+ Errno Initialize(Domain domain, Type type, Protocol protocol) override;
+
+ Errno Close() override;
+
+ std::pair<AcceptResult, Errno> Accept() override;
+
+ Errno Connect(SockAddrIn addr_in) override;
+
+ std::pair<SockAddrIn, Errno> GetPeerName() override;
+
+ std::pair<SockAddrIn, Errno> GetSockName() override;
+
+ Errno Bind(SockAddrIn addr) override;
+
+ Errno Listen(s32 backlog) override;
+
+ Errno Shutdown(ShutdownHow how) override;
+
+ std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
+
+ std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
+
+ std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
+
+ std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) override;
+
+ Errno SetLinger(bool enable, u32 linger) override;
+
+ Errno SetReuseAddr(bool enable) override;
+
+ Errno SetKeepAlive(bool enable) override;
+
+ Errno SetBroadcast(bool enable) override;
+
+ Errno SetSndBuf(u32 value) override;
+
+ Errno SetRcvBuf(u32 value) override;
+
+ Errno SetSndTimeo(u32 value) override;
+
+ Errno SetRcvTimeo(u32 value) override;
+
+ Errno SetNonBlock(bool enable) override;
+
+ template <typename T>
+ Errno SetSockOpt(SOCKET fd, int option, T value);
+
+ bool IsOpened() const override;
+
+ void HandleProxyPacket(const ProxyPacket& packet) override;
+};
+
+std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
+
+} // namespace Network
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index b47e3bf69..192571d35 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "common/logging/log.h"
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 79a4d4db5..f7702225e 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
deleted file mode 100644
index d0250bdb4..000000000
--- a/src/core/loader/elf.cpp
+++ /dev/null
@@ -1,414 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <cstring>
-#include <memory>
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "common/logging/log.h"
-#include "core/hle/kernel/code_set.h"
-#include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/k_process.h"
-#include "core/loader/elf.h"
-#include "core/memory.h"
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// ELF Header Constants
-
-// File type
-enum ElfType {
- ET_NONE = 0,
- ET_REL = 1,
- ET_EXEC = 2,
- ET_DYN = 3,
- ET_CORE = 4,
- ET_LOPROC = 0xFF00,
- ET_HIPROC = 0xFFFF,
-};
-
-// Machine/Architecture
-enum ElfMachine {
- EM_NONE = 0,
- EM_M32 = 1,
- EM_SPARC = 2,
- EM_386 = 3,
- EM_68K = 4,
- EM_88K = 5,
- EM_860 = 7,
- EM_MIPS = 8
-};
-
-// File version
-#define EV_NONE 0
-#define EV_CURRENT 1
-
-// Identification index
-#define EI_MAG0 0
-#define EI_MAG1 1
-#define EI_MAG2 2
-#define EI_MAG3 3
-#define EI_CLASS 4
-#define EI_DATA 5
-#define EI_VERSION 6
-#define EI_PAD 7
-#define EI_NIDENT 16
-
-// Sections constants
-
-// Section types
-#define SHT_NULL 0
-#define SHT_PROGBITS 1
-#define SHT_SYMTAB 2
-#define SHT_STRTAB 3
-#define SHT_RELA 4
-#define SHT_HASH 5
-#define SHT_DYNAMIC 6
-#define SHT_NOTE 7
-#define SHT_NOBITS 8
-#define SHT_REL 9
-#define SHT_SHLIB 10
-#define SHT_DYNSYM 11
-#define SHT_LOPROC 0x70000000
-#define SHT_HIPROC 0x7FFFFFFF
-#define SHT_LOUSER 0x80000000
-#define SHT_HIUSER 0xFFFFFFFF
-
-// Section flags
-enum ElfSectionFlags {
- SHF_WRITE = 0x1,
- SHF_ALLOC = 0x2,
- SHF_EXECINSTR = 0x4,
- SHF_MASKPROC = 0xF0000000,
-};
-
-// Segment types
-#define PT_NULL 0
-#define PT_LOAD 1
-#define PT_DYNAMIC 2
-#define PT_INTERP 3
-#define PT_NOTE 4
-#define PT_SHLIB 5
-#define PT_PHDR 6
-#define PT_LOPROC 0x70000000
-#define PT_HIPROC 0x7FFFFFFF
-
-// Segment flags
-#define PF_X 0x1
-#define PF_W 0x2
-#define PF_R 0x4
-#define PF_MASKPROC 0xF0000000
-
-typedef unsigned int Elf32_Addr;
-typedef unsigned short Elf32_Half;
-typedef unsigned int Elf32_Off;
-typedef signed int Elf32_Sword;
-typedef unsigned int Elf32_Word;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// ELF file header
-
-struct Elf32_Ehdr {
- unsigned char e_ident[EI_NIDENT];
- Elf32_Half e_type;
- Elf32_Half e_machine;
- Elf32_Word e_version;
- Elf32_Addr e_entry;
- Elf32_Off e_phoff;
- Elf32_Off e_shoff;
- Elf32_Word e_flags;
- Elf32_Half e_ehsize;
- Elf32_Half e_phentsize;
- Elf32_Half e_phnum;
- Elf32_Half e_shentsize;
- Elf32_Half e_shnum;
- Elf32_Half e_shstrndx;
-};
-
-// Section header
-struct Elf32_Shdr {
- Elf32_Word sh_name;
- Elf32_Word sh_type;
- Elf32_Word sh_flags;
- Elf32_Addr sh_addr;
- Elf32_Off sh_offset;
- Elf32_Word sh_size;
- Elf32_Word sh_link;
- Elf32_Word sh_info;
- Elf32_Word sh_addralign;
- Elf32_Word sh_entsize;
-};
-
-// Segment header
-struct Elf32_Phdr {
- Elf32_Word p_type;
- Elf32_Off p_offset;
- Elf32_Addr p_vaddr;
- Elf32_Addr p_paddr;
- Elf32_Word p_filesz;
- Elf32_Word p_memsz;
- Elf32_Word p_flags;
- Elf32_Word p_align;
-};
-
-// Symbol table entry
-struct Elf32_Sym {
- Elf32_Word st_name;
- Elf32_Addr st_value;
- Elf32_Word st_size;
- unsigned char st_info;
- unsigned char st_other;
- Elf32_Half st_shndx;
-};
-
-// Relocation entries
-struct Elf32_Rel {
- Elf32_Addr r_offset;
- Elf32_Word r_info;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// ElfReader class
-
-typedef int SectionID;
-
-class ElfReader {
-private:
- char* base;
- u32* base32;
-
- Elf32_Ehdr* header;
- Elf32_Phdr* segments;
- Elf32_Shdr* sections;
-
- u32* sectionAddrs;
- bool relocate;
- VAddr entryPoint;
-
-public:
- explicit ElfReader(void* ptr);
-
- u32 Read32(int off) const {
- return base32[off >> 2];
- }
-
- // Quick accessors
- ElfType GetType() const {
- return (ElfType)(header->e_type);
- }
- ElfMachine GetMachine() const {
- return (ElfMachine)(header->e_machine);
- }
- VAddr GetEntryPoint() const {
- return entryPoint;
- }
- u32 GetFlags() const {
- return (u32)(header->e_flags);
- }
- Kernel::CodeSet LoadInto(VAddr vaddr);
-
- int GetNumSegments() const {
- return (int)(header->e_phnum);
- }
- int GetNumSections() const {
- return (int)(header->e_shnum);
- }
- const u8* GetPtr(int offset) const {
- return (u8*)base + offset;
- }
- const char* GetSectionName(int section) const;
- const u8* GetSectionDataPtr(int section) const {
- if (section < 0 || section >= header->e_shnum)
- return nullptr;
- if (sections[section].sh_type != SHT_NOBITS)
- return GetPtr(sections[section].sh_offset);
- else
- return nullptr;
- }
- bool IsCodeSection(int section) const {
- return sections[section].sh_type == SHT_PROGBITS;
- }
- const u8* GetSegmentPtr(int segment) {
- return GetPtr(segments[segment].p_offset);
- }
- u32 GetSectionAddr(SectionID section) const {
- return sectionAddrs[section];
- }
- unsigned int GetSectionSize(SectionID section) const {
- return sections[section].sh_size;
- }
- SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found
-
- bool DidRelocate() const {
- return relocate;
- }
-};
-
-ElfReader::ElfReader(void* ptr) {
- base = (char*)ptr;
- base32 = (u32*)ptr;
- header = (Elf32_Ehdr*)ptr;
-
- segments = (Elf32_Phdr*)(base + header->e_phoff);
- sections = (Elf32_Shdr*)(base + header->e_shoff);
-
- entryPoint = header->e_entry;
-}
-
-const char* ElfReader::GetSectionName(int section) const {
- if (sections[section].sh_type == SHT_NULL)
- return nullptr;
-
- int name_offset = sections[section].sh_name;
- const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx));
-
- if (ptr)
- return ptr + name_offset;
-
- return nullptr;
-}
-
-Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
- LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
-
- // Should we relocate?
- relocate = (header->e_type != ET_EXEC);
-
- if (relocate) {
- LOG_DEBUG(Loader, "Relocatable module");
- entryPoint += vaddr;
- } else {
- LOG_DEBUG(Loader, "Prerelocated executable");
- }
- LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
-
- // First pass : Get the bits into RAM
- const VAddr base_addr = relocate ? vaddr : 0;
-
- u64 total_image_size = 0;
- for (unsigned int i = 0; i < header->e_phnum; ++i) {
- const Elf32_Phdr* p = &segments[i];
- if (p->p_type == PT_LOAD) {
- total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
- }
- }
-
- Kernel::PhysicalMemory program_image(total_image_size);
- std::size_t current_image_position = 0;
-
- Kernel::CodeSet codeset;
-
- for (unsigned int i = 0; i < header->e_phnum; ++i) {
- const Elf32_Phdr* p = &segments[i];
- LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
- p->p_vaddr, p->p_filesz, p->p_memsz);
-
- if (p->p_type == PT_LOAD) {
- Kernel::CodeSet::Segment* codeset_segment;
- u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X);
- if (permission_flags == (PF_R | PF_X)) {
- codeset_segment = &codeset.CodeSegment();
- } else if (permission_flags == (PF_R)) {
- codeset_segment = &codeset.RODataSegment();
- } else if (permission_flags == (PF_R | PF_W)) {
- codeset_segment = &codeset.DataSegment();
- } else {
- LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
- p->p_flags);
- continue;
- }
-
- if (codeset_segment->size != 0) {
- LOG_ERROR(Loader,
- "ELF has more than one segment of the same type. Skipping extra "
- "segment (id {})",
- i);
- continue;
- }
-
- const VAddr segment_addr = base_addr + p->p_vaddr;
- const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
-
- codeset_segment->offset = current_image_position;
- codeset_segment->addr = segment_addr;
- codeset_segment->size = aligned_size;
-
- std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i),
- p->p_filesz);
- current_image_position += aligned_size;
- }
- }
-
- codeset.entrypoint = base_addr + header->e_entry;
- codeset.memory = std::move(program_image);
-
- LOG_DEBUG(Loader, "Done loading.");
-
- return codeset;
-}
-
-SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const {
- for (int i = firstSection; i < header->e_shnum; i++) {
- const char* secname = GetSectionName(i);
-
- if (secname != nullptr && strcmp(name, secname) == 0)
- return i;
- }
- return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Loader namespace
-
-namespace Loader {
-
-AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {}
-
-FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& elf_file) {
- static constexpr u16 ELF_MACHINE_ARM{0x28};
-
- u32 magic = 0;
- if (4 != elf_file->ReadObject(&magic)) {
- return FileType::Error;
- }
-
- u16 machine = 0;
- if (2 != elf_file->ReadObject(&machine, 18)) {
- return FileType::Error;
- }
-
- if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) {
- return FileType::ELF;
- }
-
- return FileType::Error;
-}
-
-AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::KProcess& process,
- [[maybe_unused]] Core::System& system) {
- if (is_loaded) {
- return {ResultStatus::ErrorAlreadyLoaded, {}};
- }
-
- std::vector<u8> buffer = file->ReadAllBytes();
- if (buffer.size() != file->GetSize()) {
- return {ResultStatus::ErrorIncorrectELFFileSize, {}};
- }
-
- const VAddr base_address = process.PageTable().GetCodeRegionStart();
- ElfReader elf_reader(&buffer[0]);
- Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
- const VAddr entry_point = codeset.entrypoint;
-
- // Setup the process code layout
- if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) {
- return {ResultStatus::ErrorNotInitialized, {}};
- }
-
- process.LoadModule(std::move(codeset), entry_point);
-
- is_loaded = true;
- return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}};
-}
-
-} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
deleted file mode 100644
index bff51ec17..000000000
--- a/src/core/loader/elf.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include "core/loader/loader.h"
-
-namespace Core {
-class System;
-}
-
-namespace Loader {
-
-/// Loads an ELF/AXF file
-class AppLoader_ELF final : public AppLoader {
-public:
- explicit AppLoader_ELF(FileSys::VirtualFile file);
-
- /**
- * Identifies whether or not the given file is an ELF file.
- *
- * @param elf_file The file to identify.
- *
- * @return FileType::ELF, or FileType::Error if the file is not an ELF file.
- */
- static FileType IdentifyType(const FileSys::VirtualFile& elf_file);
-
- FileType GetFileType() const override {
- return IdentifyType(file);
- }
-
- LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
-};
-
-} // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 99ed34b00..d8a1bf82a 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include "core/file_sys/kernel_executable.h"
@@ -15,7 +14,7 @@ namespace Loader {
namespace {
constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
} // Anonymous namespace
diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h
index 5f914b4a8..63f66e85c 100644
--- a/src/core/loader/kip.h
+++ b/src/core/loader/kip.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 199e69e89..f24474ed8 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <memory>
#include <optional>
@@ -13,7 +12,6 @@
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/loader/deconstructed_rom_directory.h"
-#include "core/loader/elf.h"
#include "core/loader/kip.h"
#include "core/loader/nax.h"
#include "core/loader/nca.h"
@@ -40,8 +38,6 @@ std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
FileType IdentifyFile(FileSys::VirtualFile file) {
if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
return *romdir_type;
- } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
- return *elf_type;
} else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
return *nso_type;
} else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
@@ -70,8 +66,6 @@ FileType GuessFromFilename(const std::string& name) {
const std::string extension =
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
- if (extension == "elf")
- return FileType::ELF;
if (extension == "nro")
return FileType::NRO;
if (extension == "nso")
@@ -90,8 +84,6 @@ FileType GuessFromFilename(const std::string& name) {
std::string GetFileTypeString(FileType type) {
switch (type) {
- case FileType::ELF:
- return "ELF";
case FileType::NRO:
return "NRO";
case FileType::NSO:
@@ -209,10 +201,6 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
FileType type, u64 program_id,
std::size_t program_index) {
switch (type) {
- // Standard ELF file format.
- case FileType::ELF:
- return std::make_unique<AppLoader_ELF>(std::move(file));
-
// NX NSO file format.
case FileType::NSO:
return std::make_unique<AppLoader_NSO>(std::move(file));
@@ -256,12 +244,17 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
u64 program_id, std::size_t program_index) {
+ if (!file) {
+ return nullptr;
+ }
+
FileType type = IdentifyFile(file);
const FileType filename_type = GuessFromFilename(file->GetName());
// Special case: 00 is either a NCA or NAX.
if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) {
- LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName());
+ LOG_WARNING(Loader, "File {} has a different type ({}) than its extension.",
+ file->GetName(), GetFileTypeString(type));
if (FileType::Unknown == type) {
type = filename_type;
}
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 8b6b3b68f..7b43f70ed 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -35,7 +34,6 @@ namespace Loader {
enum class FileType {
Error,
Unknown,
- ELF,
NSO,
NRO,
NCA,
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 3375dab7c..cf35b1249 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/content_archive.h"
#include "core/file_sys/romfs.h"
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index b3a50894f..d7f70db43 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 219bbeaf5..513af194d 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index c0db8c740..d22d9146e 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 951ea966e..73d04d7ee 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <utility>
#include <vector>
@@ -126,7 +125,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
}
static constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) {
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index fd453b402..ccb77b581 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 4a2224c02..4c3b3c655 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <cinttypes>
#include <cstring>
@@ -46,7 +45,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
}
constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
} // Anonymous namespace
@@ -129,11 +128,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
// Apply patches if necessary
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
- std::vector<u8> pi_header;
- pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
- reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
- pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.data(),
- program_image.data() + program_image.size());
+ std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
+ std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader));
+ std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(),
+ program_image.size());
pi_header = pm->PatchNSO(pi_header, nso_file.GetName());
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index f7b61bc2d..0b53b4ecd 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index f7ccc678d..80663e0e0 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <vector>
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 378e4077a..003cc345c 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 8c6c1a3fd..c7b1b3815 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <vector>
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 6e3810e48..2affb6c6e 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 88d6ec908..2ac792566 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -37,11 +36,11 @@ struct Memory::Impl {
}
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
- ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
- ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- ASSERT_MSG(target >= DramMemoryMap::Base && target < DramMemoryMap::End,
- "Out of bounds target: {:016X}", target);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
+ ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
+ ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base);
+ ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
+ MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
+ Common::PageType::Memory);
if (Settings::IsFastmemEnabled()) {
system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size);
@@ -49,9 +48,10 @@ struct Memory::Impl {
}
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
- ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
- ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);
+ ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
+ ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base);
+ MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
+ Common::PageType::Unmapped);
if (Settings::IsFastmemEnabled()) {
system.DeviceMemory().buffer.Unmap(base, size);
@@ -59,7 +59,7 @@ struct Memory::Impl {
}
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
- const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
+ const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
if (!paddr) {
return {};
@@ -68,6 +68,16 @@ struct Memory::Impl {
return system.DeviceMemory().GetPointer(paddr) + vaddr;
}
+ [[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const {
+ const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
+
+ if (paddr == 0) {
+ return {};
+ }
+
+ return system.DeviceMemory().GetPointer(paddr) + vaddr;
+ }
+
u8 Read8(const VAddr addr) {
return Read<u8>(addr);
}
@@ -168,13 +178,14 @@ struct Memory::Impl {
auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) {
const auto& page_table = process.PageTable().PageTableImpl();
std::size_t remaining_size = size;
- std::size_t page_index = addr >> PAGE_BITS;
- std::size_t page_offset = addr & PAGE_MASK;
+ std::size_t page_index = addr >> YUZU_PAGEBITS;
+ std::size_t page_offset = addr & YUZU_PAGEMASK;
while (remaining_size) {
const std::size_t copy_amount =
- std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
- const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
+ std::min(static_cast<std::size_t>(YUZU_PAGESIZE) - page_offset, remaining_size);
+ const auto current_vaddr =
+ static_cast<VAddr>((page_index << YUZU_PAGEBITS) + page_offset);
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
switch (type) {
@@ -184,7 +195,13 @@ struct Memory::Impl {
}
case Common::PageType::Memory: {
DEBUG_ASSERT(pointer);
- u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS);
+ u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS);
+ on_memory(copy_amount, mem_ptr);
+ break;
+ }
+ case Common::PageType::DebugMemory: {
+ DEBUG_ASSERT(pointer);
+ u8* const mem_ptr{GetPointerFromDebugMemory(current_vaddr)};
on_memory(copy_amount, mem_ptr);
break;
}
@@ -317,6 +334,58 @@ struct Memory::Impl {
});
}
+ void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
+ if (vaddr == 0) {
+ return;
+ }
+
+ // Iterate over a contiguous CPU address space, marking/unmarking the region.
+ // The region is at a granularity of CPU pages.
+
+ const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
+ for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
+ const Common::PageType page_type{
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()};
+ if (debug) {
+ // Switch page type to debug if now debug
+ switch (page_type) {
+ case Common::PageType::Unmapped:
+ ASSERT_MSG(false, "Attempted to mark unmapped pages as debug");
+ break;
+ case Common::PageType::RasterizerCachedMemory:
+ case Common::PageType::DebugMemory:
+ // Page is already marked.
+ break;
+ case Common::PageType::Memory:
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
+ nullptr, Common::PageType::DebugMemory);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ } else {
+ // Switch page type to non-debug if now non-debug
+ switch (page_type) {
+ case Common::PageType::Unmapped:
+ ASSERT_MSG(false, "Attempted to mark unmapped pages as non-debug");
+ break;
+ case Common::PageType::RasterizerCachedMemory:
+ case Common::PageType::Memory:
+ // Don't mess with already non-debug or rasterizer memory.
+ break;
+ case Common::PageType::DebugMemory: {
+ u8* const pointer{GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK)};
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
+ pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+ }
+
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
if (vaddr == 0) {
return;
@@ -332,10 +401,10 @@ struct Memory::Impl {
// granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
// is different). This assumes the specified GPU address region is contiguous as well.
- const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
- for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
+ const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
+ for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
const Common::PageType page_type{
- current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()};
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
@@ -343,8 +412,9 @@ struct Memory::Impl {
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
+ case Common::PageType::DebugMemory:
case Common::PageType::Memory:
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
nullptr, Common::PageType::RasterizerCachedMemory);
break;
case Common::PageType::RasterizerCachedMemory:
@@ -361,21 +431,22 @@ struct Memory::Impl {
// It is not necessary for a process to have this region mapped into its address
// space, for example, a system module need not have a VRAM mapping.
break;
+ case Common::PageType::DebugMemory:
case Common::PageType::Memory:
// There can be more than one GPU region mapped per CPU region, so it's common
// that this area is already unmarked as cached.
break;
case Common::PageType::RasterizerCachedMemory: {
- u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
+ u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK)};
if (pointer == nullptr) {
// It's possible that this function has been called while updating the
// pagetable after unmapping a VMA. In that case the underlying VMA will no
// longer exist, and we should just leave the pagetable entry blank.
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
nullptr, Common::PageType::Unmapped);
} else {
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
- pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
+ pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
}
break;
}
@@ -397,8 +468,8 @@ struct Memory::Impl {
*/
void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
Common::PageType type) {
- LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE,
- (base + size) * PAGE_SIZE);
+ LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * YUZU_PAGESIZE,
+ (base + size) * YUZU_PAGESIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (system.IsPoweredOn()) {
@@ -406,7 +477,7 @@ struct Memory::Impl {
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
- gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
+ gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE);
}
}
}
@@ -417,7 +488,7 @@ struct Memory::Impl {
if (!target) {
ASSERT_MSG(type != Common::PageType::Memory,
- "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
+ "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
while (base != end) {
page_table.pointers[base].Store(nullptr, type);
@@ -428,21 +499,21 @@ struct Memory::Impl {
} else {
while (base != end) {
page_table.pointers[base].Store(
- system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type);
- page_table.backing_addr[base] = target - (base << PAGE_BITS);
+ system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type);
+ page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS);
ASSERT_MSG(page_table.pointers[base].Pointer(),
"memory mapping base yield a nullptr within the table");
base += 1;
- target += PAGE_SIZE;
+ target += YUZU_PAGESIZE;
}
}
}
[[nodiscard]] u8* GetPointerImpl(VAddr vaddr, auto on_unmapped, auto on_rasterizer) const {
// AARCH64 masks the upper 16 bit of all memory accesses
- vaddr &= 0xffffffffffffLL;
+ vaddr &= 0xffffffffffffULL;
if (vaddr >= 1uLL << current_page_table->GetAddressSpaceBits()) {
on_unmapped();
@@ -450,7 +521,7 @@ struct Memory::Impl {
}
// Avoid adding any extra logic to this fast-path block
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
+ const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw();
if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
return &pointer[vaddr];
}
@@ -461,6 +532,8 @@ struct Memory::Impl {
case Common::PageType::Memory:
ASSERT_MSG(false, "Mapped memory page without a pointer @ 0x{:016X}", vaddr);
return nullptr;
+ case Common::PageType::DebugMemory:
+ return GetPointerFromDebugMemory(vaddr);
case Common::PageType::RasterizerCachedMemory: {
u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};
on_rasterizer();
@@ -478,6 +551,11 @@ struct Memory::Impl {
[]() {});
}
+ [[nodiscard]] u8* GetPointerSilent(const VAddr vaddr) const {
+ return GetPointerImpl(
+ vaddr, []() {}, []() {});
+ }
+
/**
* Reads a particular data type out of memory at the given virtual address.
*
@@ -587,18 +665,36 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
const Kernel::KProcess& process = *system.CurrentProcess();
const auto& page_table = process.PageTable().PageTableImpl();
- const size_t page = vaddr >> PAGE_BITS;
+ const size_t page = vaddr >> YUZU_PAGEBITS;
if (page >= page_table.pointers.size()) {
return false;
}
const auto [pointer, type] = page_table.pointers[page].PointerType();
- return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
+ return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory ||
+ type == Common::PageType::DebugMemory;
+}
+
+bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
+ VAddr end = base + size;
+ VAddr page = Common::AlignDown(base, YUZU_PAGESIZE);
+
+ for (; page < end; page += YUZU_PAGESIZE) {
+ if (!IsValidVirtualAddress(page)) {
+ return false;
+ }
+ }
+
+ return true;
}
u8* Memory::GetPointer(VAddr vaddr) {
return impl->GetPointer(vaddr);
}
+u8* Memory::GetPointerSilent(VAddr vaddr) {
+ return impl->GetPointerSilent(vaddr);
+}
+
const u8* Memory::GetPointer(VAddr vaddr) const {
return impl->GetPointer(vaddr);
}
@@ -691,8 +787,16 @@ void Memory::CopyBlock(const Kernel::KProcess& process, VAddr dest_addr, VAddr s
impl->CopyBlock(process, dest_addr, src_addr, size);
}
+void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const std::size_t size) {
+ impl->ZeroBlock(process, dest_addr, size);
+}
+
void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
impl->RasterizerMarkRegionCached(vaddr, size, cached);
}
+void Memory::MarkRegionDebug(VAddr vaddr, u64 size, bool debug) {
+ impl->MarkRegionDebug(vaddr, size, debug);
+}
+
} // namespace Core::Memory
diff --git a/src/core/memory.h b/src/core/memory.h
index b5721b740..81eac448b 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -28,9 +27,9 @@ namespace Core::Memory {
* Page size used by the ARM architecture. This is the smallest granularity with which memory can
* be mapped.
*/
-constexpr std::size_t PAGE_BITS = 12;
-constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
-constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
+constexpr std::size_t YUZU_PAGEBITS = 12;
+constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS;
+constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1;
/// Virtual user-space memory regions
enum : VAddr {
@@ -96,6 +95,17 @@ public:
[[nodiscard]] bool IsValidVirtualAddress(VAddr vaddr) const;
/**
+ * Checks whether or not the supplied range of addresses are all valid
+ * virtual addresses for the current process.
+ *
+ * @param base The address to begin checking.
+ * @param size The amount of bytes to check.
+ *
+ * @returns True if all bytes in the given range are valid, false otherwise.
+ */
+ [[nodiscard]] bool IsValidVirtualAddressRange(VAddr base, u64 size) const;
+
+ /**
* Gets a pointer to the given address.
*
* @param vaddr Virtual address to retrieve a pointer to.
@@ -104,6 +114,7 @@ public:
* If the address is not valid, nullptr will be returned.
*/
u8* GetPointer(VAddr vaddr);
+ u8* GetPointerSilent(VAddr vaddr);
template <typename T>
T* GetPointer(VAddr vaddr) {
@@ -426,6 +437,19 @@ public:
std::size_t size);
/**
+ * Zeros a range of bytes within the current process' address space at the specified
+ * virtual address.
+ *
+ * @param process The process that will have data zeroed within its address space.
+ * @param dest_addr The destination virtual address to zero the data from.
+ * @param size The size of the range to zero out, in bytes.
+ *
+ * @post The range [dest_addr, size) within the process' address space contains the
+ * value 0.
+ */
+ void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size);
+
+ /**
* Marks each page within the specified address range as cached or uncached.
*
* @param vaddr The virtual address indicating the start of the address range.
@@ -435,6 +459,17 @@ public:
*/
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
+ /**
+ * Marks each page within the specified address range as debug or non-debug.
+ * Debug addresses are not accessible from fastmem pointers.
+ *
+ * @param vaddr The virtual address indicating the start of the address range.
+ * @param size The size of the address range in bytes.
+ * @param debug Whether or not any pages within the address range should be
+ * marked as debug or non-debug.
+ */
+ void MarkRegionDebug(VAddr vaddr, u64 size, bool debug);
+
private:
Core::System& system;
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index 12446c9ac..ffdbacc18 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <locale>
#include "common/hex_util.h"
@@ -185,10 +184,12 @@ CheatEngine::~CheatEngine() {
void CheatEngine::Initialize() {
event = Core::Timing::CreateEvent(
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
FrameCallback(user_data, ns_late);
+ return std::nullopt;
});
- core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event);
+ core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
metadata.process_id = system.CurrentProcess()->GetProcessID();
metadata.title_id = system.GetCurrentProcessProgramID();
@@ -238,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late
MICROPROFILE_SCOPE(Cheat_Engine);
vm.Execute(metadata);
-
- core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event);
}
} // namespace Core::Memory
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index a8e041d9d..284abdd28 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -1,6 +1,5 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h
index 5e60733dc..c6b40e505 100644
--- a/src/core/memory/dmnt_cheat_types.h
+++ b/src/core/memory/dmnt_cheat_types.h
@@ -1,26 +1,5 @@
-/*
- * Copyright (c) 2018-2019 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Adapted by DarkLordZach for use/interaction with yuzu
- *
- * Modifications Copyright 2019 yuzu emulator team
- * Licensed under GPLv2 or any later version
- * Refer to the license.txt file included.
- */
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index dc04e37d2..de96fcb8e 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -1,26 +1,5 @@
-/*
- * Copyright (c) 2018-2019 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Adapted by DarkLordZach for use/interaction with yuzu
- *
- * Modifications Copyright 2019 yuzu emulator team
- * Licensed under GPLv2 or any later version
- * Refer to the license.txt file included.
- */
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/scope_exit.h"
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
index 707bee82b..641cb09c4 100644
--- a/src/core/memory/dmnt_cheat_vm.h
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -1,26 +1,5 @@
-/*
- * Copyright (c) 2018-2019 Atmosphère-NX
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * Adapted by DarkLordZach for use/interaction with yuzu
- *
- * Modifications Copyright 2019 yuzu emulator team
- * Licensed under GPLv2 or any later version
- * Refer to the license.txt file included.
- */
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h
deleted file mode 100644
index a44393325..000000000
--- a/src/core/network/sockets.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2020 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-#include <utility>
-
-#if defined(_WIN32)
-#include <winsock.h>
-#elif !YUZU_UNIX
-#error "Platform not implemented"
-#endif
-
-#include "common/common_types.h"
-#include "core/network/network.h"
-
-// TODO: C++20 Replace std::vector usages with std::span
-
-namespace Network {
-
-class Socket {
-public:
- struct AcceptResult {
- std::unique_ptr<Socket> socket;
- SockAddrIn sockaddr_in;
- };
-
- explicit Socket() = default;
- ~Socket();
-
- Socket(const Socket&) = delete;
- Socket& operator=(const Socket&) = delete;
-
- Socket(Socket&& rhs) noexcept;
-
- // Avoid closing sockets implicitly
- Socket& operator=(Socket&&) noexcept = delete;
-
- Errno Initialize(Domain domain, Type type, Protocol protocol);
-
- Errno Close();
-
- std::pair<AcceptResult, Errno> Accept();
-
- Errno Connect(SockAddrIn addr_in);
-
- std::pair<SockAddrIn, Errno> GetPeerName();
-
- std::pair<SockAddrIn, Errno> GetSockName();
-
- Errno Bind(SockAddrIn addr);
-
- Errno Listen(s32 backlog);
-
- Errno Shutdown(ShutdownHow how);
-
- std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message);
-
- std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr);
-
- std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags);
-
- std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr);
-
- Errno SetLinger(bool enable, u32 linger);
-
- Errno SetReuseAddr(bool enable);
-
- Errno SetBroadcast(bool enable);
-
- Errno SetSndBuf(u32 value);
-
- Errno SetRcvBuf(u32 value);
-
- Errno SetSndTimeo(u32 value);
-
- Errno SetRcvTimeo(u32 value);
-
- Errno SetNonBlock(bool enable);
-
- bool IsOpened() const;
-
-#if defined(_WIN32)
- SOCKET fd = INVALID_SOCKET;
-#elif YUZU_UNIX
- int fd = -1;
-#endif
-};
-
-std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
-
-} // namespace Network
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 52c43c857..f09c176f8 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <chrono>
@@ -53,13 +52,13 @@ PerfStats::~PerfStats() {
}
void PerfStats::BeginSystemFrame() {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
frame_begin = Clock::now();
}
void PerfStats::EndSystemFrame() {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
auto frame_end = Clock::now();
const auto frame_time = frame_end - frame_begin;
@@ -79,7 +78,7 @@ void PerfStats::EndGameFrame() {
}
double PerfStats::GetMeanFrametime() const {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
if (current_index <= IgnoreFrames) {
return 0;
@@ -91,7 +90,7 @@ double PerfStats::GetMeanFrametime() const {
}
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
const auto now = Clock::now();
// Walltime elapsed since stats were reset
@@ -120,7 +119,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us
}
double PerfStats::GetLastFrameTimeScale() const {
- std::lock_guard lock{object_mutex};
+ std::scoped_lock lock{object_mutex};
constexpr double FRAME_LENGTH = 1.0 / 60;
return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH;
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 816202588..dd6becc02 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
index d4becdc0a..6e21296f6 100644
--- a/src/core/reporter.cpp
+++ b/src/core/reporter.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <ctime>
#include <fstream>
@@ -8,7 +7,6 @@
#include <fmt/chrono.h>
#include <fmt/format.h>
-#include <fmt/ostream.h>
#include <nlohmann/json.hpp>
#include "common/fs/file.h"
@@ -65,7 +63,7 @@ json GetYuzuVersionData() {
};
}
-json GetReportCommonData(u64 title_id, ResultCode result, const std::string& timestamp,
+json GetReportCommonData(u64 title_id, Result result, const std::string& timestamp,
std::optional<u128> user_id = {}) {
auto out = json{
{"title_id", fmt::format("{:016X}", title_id)},
@@ -200,8 +198,8 @@ Reporter::Reporter(System& system_) : system(system_) {
Reporter::~Reporter() = default;
-void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point,
- u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
+void Reporter::SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 entry_point, u64 sp,
+ u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
const std::array<u64, 31>& registers,
const std::array<u64, 32>& backtrace, u32 backtrace_size,
const std::string& arch, u32 unk10) const {
@@ -340,7 +338,7 @@ void Reporter::SavePlayReport(PlayReportType type, u64 title_id, std::vector<std
SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp));
}
-void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
+void Reporter::SaveErrorReport(u64 title_id, Result result,
std::optional<std::string> custom_text_main,
std::optional<std::string> custom_text_detail) const {
if (!IsReportingEnabled()) {
diff --git a/src/core/reporter.h b/src/core/reporter.h
index 6e9edeea3..68755cbde 100644
--- a/src/core/reporter.h
+++ b/src/core/reporter.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -10,7 +9,7 @@
#include <vector>
#include "common/common_types.h"
-union ResultCode;
+union Result;
namespace Kernel {
class HLERequestContext;
@@ -30,7 +29,7 @@ public:
~Reporter();
// Used by fatal services
- void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp,
+ void SaveCrashReport(u64 title_id, Result result, u64 set_flags, u64 entry_point, u64 sp,
u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace,
u32 backtrace_size, const std::string& arch, u32 unk10) const;
@@ -61,7 +60,7 @@ public:
std::optional<u64> process_id = {}, std::optional<u128> user_id = {}) const;
// Used by error applet
- void SaveErrorReport(u64 title_id, ResultCode result,
+ void SaveErrorReport(u64 title_id, Result result,
std::optional<std::string> custom_text_main = {},
std::optional<std::string> custom_text_detail = {}) const;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 654db0b52..abcf6eb11 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 6f3d45bea..887dc98f3 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index 032c71aff..98ebbbf32 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
@@ -26,7 +25,6 @@ u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
return memory.Read64(addr);
default:
UNREACHABLE();
- return 0;
}
}
@@ -55,8 +53,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m
: core_timing{core_timing_}, memory{memory_} {
event = Core::Timing::CreateEvent(
"MemoryFreezer::FrameCallback",
- [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ [this](std::uintptr_t user_data, s64 time,
+ std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
FrameCallback(user_data, ns_late);
+ return std::nullopt;
});
core_timing.ScheduleEvent(memory_freezer_ns, event);
}
@@ -80,7 +80,7 @@ bool Freezer::IsActive() const {
}
void Freezer::Clear() {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
@@ -88,7 +88,7 @@ void Freezer::Clear() {
}
u64 Freezer::Freeze(VAddr address, u32 width) {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
const auto current_value = MemoryReadWidth(memory, width, address);
entries.push_back({address, width, current_value});
@@ -101,7 +101,7 @@ u64 Freezer::Freeze(VAddr address, u32 width) {
}
void Freezer::Unfreeze(VAddr address) {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
@@ -109,13 +109,13 @@ void Freezer::Unfreeze(VAddr address) {
}
bool Freezer::IsFrozen(VAddr address) const {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
return FindEntry(address) != entries.cend();
}
void Freezer::SetFrozenValue(VAddr address, u64 value) {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
const auto iter = FindEntry(address);
@@ -132,7 +132,7 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) {
}
std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
const auto iter = FindEntry(address);
@@ -144,7 +144,7 @@ std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
}
std::vector<Freezer::Entry> Freezer::GetEntries() const {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
return entries;
}
@@ -165,7 +165,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
return;
}
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
for (const auto& entry : entries) {
LOG_DEBUG(Common_Memory,
@@ -178,7 +178,7 @@ void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) {
}
void Freezer::FillEntryReads() {
- std::lock_guard lock{entries_mutex};
+ std::scoped_lock lock{entries_mutex};
LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
index 067134e93..0d6df5217 100644
--- a/src/core/tools/freezer.h
+++ b/src/core/tools/freezer.h
@@ -1,6 +1,5 @@
-// Copyright 2019 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once