diff options
Diffstat (limited to 'src/core')
195 files changed, 5710 insertions, 1847 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b74e495ef..0abf7edc1 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -12,6 +12,16 @@ add_library(core STATIC core_timing.h core_timing_util.cpp core_timing_util.h + crypto/aes_util.cpp + crypto/aes_util.h + crypto/encryption_layer.cpp + crypto/encryption_layer.h + crypto/key_manager.cpp + crypto/key_manager.h + crypto/ctr_encryption_layer.cpp + crypto/ctr_encryption_layer.h + file_sys/card_image.cpp + file_sys/card_image.h file_sys/content_archive.cpp file_sys/content_archive.h file_sys/control_metadata.cpp @@ -23,6 +33,8 @@ add_library(core STATIC file_sys/partition_filesystem.h file_sys/program_metadata.cpp file_sys/program_metadata.h + file_sys/romfs.cpp + file_sys/romfs.h file_sys/romfs_factory.cpp file_sys/romfs_factory.h file_sys/savedata_factory.cpp @@ -35,6 +47,8 @@ add_library(core STATIC file_sys/vfs_offset.h file_sys/vfs_real.cpp file_sys/vfs_real.h + file_sys/vfs_vector.cpp + file_sys/vfs_vector.h frontend/emu_window.cpp frontend/emu_window.h frontend/framebuffer_layout.cpp @@ -59,12 +73,10 @@ add_library(core STATIC hle/kernel/hle_ipc.h hle/kernel/kernel.cpp hle/kernel/kernel.h - hle/kernel/memory.cpp - hle/kernel/memory.h hle/kernel/mutex.cpp hle/kernel/mutex.h - hle/kernel/object_address_table.cpp - hle/kernel/object_address_table.h + hle/kernel/object.cpp + hle/kernel/object.h hle/kernel/process.cpp hle/kernel/process.h hle/kernel/resource_limit.cpp @@ -110,23 +122,41 @@ add_library(core STATIC hle/service/am/applet_ae.h hle/service/am/applet_oe.cpp hle/service/am/applet_oe.h + hle/service/am/idle.cpp + hle/service/am/idle.h + hle/service/am/omm.cpp + hle/service/am/omm.h + hle/service/am/spsm.cpp + hle/service/am/spsm.h hle/service/aoc/aoc_u.cpp hle/service/aoc/aoc_u.h hle/service/apm/apm.cpp hle/service/apm/apm.h hle/service/apm/interface.cpp hle/service/apm/interface.h + hle/service/arp/arp.cpp + hle/service/arp/arp.h + hle/service/audio/audctl.cpp + hle/service/audio/audctl.h + hle/service/audio/auddbg.cpp + hle/service/audio/auddbg.h + hle/service/audio/audin_a.cpp + hle/service/audio/audin_a.h hle/service/audio/audin_u.cpp hle/service/audio/audin_u.h hle/service/audio/audio.cpp hle/service/audio/audio.h + hle/service/audio/audout_a.cpp + hle/service/audio/audout_a.h hle/service/audio/audout_u.cpp hle/service/audio/audout_u.h + hle/service/audio/audrec_a.cpp + hle/service/audio/audrec_a.h hle/service/audio/audrec_u.cpp hle/service/audio/audrec_u.h + hle/service/audio/audren_a.cpp + hle/service/audio/audren_a.h hle/service/audio/audren_u.cpp - hle/service/audio/audren_u.cpp - hle/service/audio/audren_u.h hle/service/audio/audren_u.h hle/service/audio/codecctl.cpp hle/service/audio/codecctl.h @@ -136,6 +166,14 @@ add_library(core STATIC hle/service/bcat/bcat.h hle/service/bcat/module.cpp hle/service/bcat/module.h + hle/service/bpc/bpc.cpp + hle/service/bpc/bpc.h + hle/service/btdrv/btdrv.cpp + hle/service/btdrv/btdrv.h + hle/service/btm/btm.cpp + hle/service/btm/btm.h + hle/service/caps/caps.cpp + hle/service/caps/caps.h hle/service/erpt/erpt.cpp hle/service/erpt/erpt.h hle/service/es/es.cpp @@ -150,8 +188,14 @@ add_library(core STATIC hle/service/fatal/fatal_u.h hle/service/filesystem/filesystem.cpp hle/service/filesystem/filesystem.h + hle/service/filesystem/fsp_ldr.cpp + hle/service/filesystem/fsp_ldr.h + hle/service/filesystem/fsp_pr.cpp + hle/service/filesystem/fsp_pr.h hle/service/filesystem/fsp_srv.cpp hle/service/filesystem/fsp_srv.h + hle/service/fgm/fgm.cpp + hle/service/fgm/fgm.h hle/service/friend/friend.cpp hle/service/friend/friend.h hle/service/friend/interface.cpp @@ -164,14 +208,24 @@ add_library(core STATIC hle/service/hid/irs.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h + hle/service/lbl/lbl.cpp + hle/service/lbl/lbl.h hle/service/ldn/ldn.cpp hle/service/ldn/ldn.h hle/service/ldr/ldr.cpp hle/service/ldr/ldr.h hle/service/lm/lm.cpp hle/service/lm/lm.h + hle/service/mig/mig.cpp + hle/service/mig/mig.h + hle/service/mii/mii.cpp + hle/service/mii/mii.h hle/service/mm/mm_u.cpp hle/service/mm/mm_u.h + hle/service/ncm/ncm.cpp + hle/service/ncm/ncm.h + hle/service/nfc/nfc.cpp + hle/service/nfc/nfc.h hle/service/nfp/nfp.cpp hle/service/nfp/nfp.h hle/service/nfp/nfp_user.cpp @@ -209,14 +263,20 @@ add_library(core STATIC hle/service/nvflinger/buffer_queue.h hle/service/nvflinger/nvflinger.cpp hle/service/nvflinger/nvflinger.h + hle/service/pcie/pcie.cpp + hle/service/pcie/pcie.h hle/service/pctl/module.cpp hle/service/pctl/module.h hle/service/pctl/pctl.cpp hle/service/pctl/pctl.h + hle/service/pcv/pcv.cpp + hle/service/pcv/pcv.h hle/service/pm/pm.cpp hle/service/pm/pm.h hle/service/prepo/prepo.cpp hle/service/prepo/prepo.h + hle/service/psc/psc.cpp + hle/service/psc/psc.h hle/service/service.cpp hle/service/service.h hle/service/set/set.cpp @@ -255,6 +315,8 @@ add_library(core STATIC hle/service/time/interface.h hle/service/time/time.cpp hle/service/time/time.h + hle/service/usb/usb.cpp + hle/service/usb/usb.h hle/service/vi/vi.cpp hle/service/vi/vi.h hle/service/vi/vi_m.cpp @@ -263,10 +325,8 @@ add_library(core STATIC hle/service/vi/vi_s.h hle/service/vi/vi_u.cpp hle/service/vi/vi_u.h - hw/hw.cpp - hw/hw.h - hw/lcd.cpp - hw/lcd.h + hle/service/wlan/wlan.cpp + hle/service/wlan/wlan.h loader/deconstructed_rom_directory.cpp loader/deconstructed_rom_directory.h loader/elf.cpp @@ -281,6 +341,8 @@ add_library(core STATIC loader/nro.h loader/nso.cpp loader/nso.h + loader/xci.cpp + loader/xci.h memory.cpp memory.h memory_hook.cpp @@ -299,8 +361,8 @@ add_library(core STATIC create_target_directory_groups(core) -target_link_libraries(core PUBLIC common PRIVATE video_core) -target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn) +target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn) if (ARCHITECTURE_x86_64) target_sources(core PRIVATE diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 57b8634b9..ceb3f7683 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -10,7 +10,7 @@ #include "core/arm/dynarmic/arm_dynarmic.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/hle/kernel/memory.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/svc.h" #include "core/memory.h" @@ -139,14 +139,12 @@ void ARM_Dynarmic::Step() { } ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index) - : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), - jit(MakeJit()), exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>( - exclusive_monitor)}, - core_index{core_index} { - ARM_Interface::ThreadContext ctx; + : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, + exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { + ThreadContext ctx; inner_unicorn.SaveContext(ctx); - LoadContext(ctx); PageTableChanged(); + LoadContext(ctx); } ARM_Dynarmic::~ARM_Dynarmic() = default; @@ -205,7 +203,7 @@ u64 ARM_Dynarmic::GetTlsAddress() const { return cb->tpidrro_el0; } -void ARM_Dynarmic::SetTlsAddress(u64 address) { +void ARM_Dynarmic::SetTlsAddress(VAddr address) { cb->tpidrro_el0 = address; } @@ -217,7 +215,7 @@ void ARM_Dynarmic::SetTPIDR_EL0(u64 value) { cb->tpidr_el0 = value; } -void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { +void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); ctx.pc = jit->GetPC(); @@ -226,7 +224,7 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { ctx.fpscr = jit->GetFpcr(); } -void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { +void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { jit->SetRegisters(ctx.cpu_registers); jit->SetSP(ctx.sp); jit->SetPC(ctx.pc); diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 4c11f35a4..6bc349460 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -203,7 +203,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) { } Kernel::Thread* thread = Kernel::GetCurrentThread(); SaveContext(thread->context); - if (last_bkpt_hit || (num_instructions == 1)) { + if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { last_bkpt_hit = false; GDBStub::Break(); GDBStub::SendTrap(thread, 5); diff --git a/src/core/core.cpp b/src/core/core.cpp index b7f4b4532..085ba68d0 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -15,11 +15,10 @@ #include "core/hle/service/service.h" #include "core/hle/service/sm/controller.h" #include "core/hle/service/sm/sm.h" -#include "core/hw/hw.h" #include "core/loader/loader.h" -#include "core/memory_setup.h" #include "core/settings.h" #include "file_sys/vfs_real.h" +#include "video_core/renderer_base.h" #include "video_core/video_core.h" namespace Core { @@ -63,7 +62,6 @@ System::ResultStatus System::RunLoop(bool tight_loop) { // execute. Otherwise, get out of the loop function. if (GDBStub::GetCpuHaltFlag()) { if (GDBStub::GetCpuStepFlag()) { - GDBStub::SetCpuStepFlag(false); tight_loop = false; } else { return ResultStatus::Success; @@ -79,6 +77,10 @@ System::ResultStatus System::RunLoop(bool tight_loop) { } } + if (GDBStub::IsServerEnabled()) { + GDBStub::SetCpuStepFlag(false); + } + return status; } @@ -86,7 +88,7 @@ System::ResultStatus System::SingleStep() { return RunLoop(false); } -System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { +System::ResultStatus System::Load(EmuWindow& emu_window, const std::string& filepath) { app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); if (!app_loader) { @@ -101,8 +103,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file static_cast<int>(system_mode.second)); switch (system_mode.second) { - case Loader::ResultStatus::ErrorEncrypted: - return ResultStatus::ErrorLoader_ErrorEncrypted; + case Loader::ResultStatus::ErrorMissingKeys: + return ResultStatus::ErrorLoader_ErrorMissingKeys; + case Loader::ResultStatus::ErrorDecrypting: + return ResultStatus::ErrorLoader_ErrorDecrypting; case Loader::ResultStatus::ErrorInvalidFormat: return ResultStatus::ErrorLoader_ErrorInvalidFormat; case Loader::ResultStatus::ErrorUnsupportedArch: @@ -112,7 +116,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file } } - ResultStatus init_result{Init(emu_window, system_mode.first.get())}; + ResultStatus init_result{Init(emu_window)}; if (init_result != ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", static_cast<int>(init_result)); @@ -126,8 +130,10 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file System::Shutdown(); switch (load_result) { - case Loader::ResultStatus::ErrorEncrypted: - return ResultStatus::ErrorLoader_ErrorEncrypted; + case Loader::ResultStatus::ErrorMissingKeys: + return ResultStatus::ErrorLoader_ErrorMissingKeys; + case Loader::ResultStatus::ErrorDecrypting: + return ResultStatus::ErrorLoader_ErrorDecrypting; case Loader::ResultStatus::ErrorInvalidFormat: return ResultStatus::ErrorLoader_ErrorInvalidFormat; case Loader::ResultStatus::ErrorUnsupportedArch: @@ -163,7 +169,7 @@ Cpu& System::CpuCore(size_t core_index) { return *cpu_cores[core_index]; } -System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { +System::ResultStatus System::Init(EmuWindow& emu_window) { LOG_DEBUG(HW_Memory, "initialized OK"); CoreTiming::Init(); @@ -176,19 +182,20 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); } - gpu_core = std::make_unique<Tegra::GPU>(); telemetry_session = std::make_unique<Core::TelemetrySession>(); service_manager = std::make_shared<Service::SM::ServiceManager>(); - HW::Init(); - Kernel::Init(system_mode); + Kernel::Init(); Service::Init(service_manager); GDBStub::Init(); - if (!VideoCore::Init(emu_window)) { + renderer = VideoCore::CreateRenderer(emu_window); + if (!renderer->Init()) { return ResultStatus::ErrorVideoCore; } + gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); + // Create threads for CPU cores 1-3, and build thread_to_cpu map // CPU core 0 is run on the main thread thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; @@ -220,11 +227,10 @@ void System::Shutdown() { perf_results.frametime * 1000.0); // Shutdown emulation session - VideoCore::Shutdown(); + renderer.reset(); GDBStub::Shutdown(); Service::Shutdown(); Kernel::Shutdown(); - HW::Shutdown(); service_manager.reset(); telemetry_session.reset(); gpu_core.reset(); diff --git a/src/core/core.h b/src/core/core.h index c123fe401..c8ca4b247 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "core/arm/exclusive_monitor.h" #include "core/core_cpu.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -27,6 +27,10 @@ namespace Service::SM { class ServiceManager; } +namespace VideoCore { +class RendererBase; +} + namespace Core { class System { @@ -43,12 +47,14 @@ public: /// Enumeration representing the return values of the System Initialize and Load process. enum class ResultStatus : u32 { - Success, ///< Succeeded - ErrorNotInitialized, ///< Error trying to use core prior to initialization - ErrorGetLoader, ///< Error finding the correct application loader - ErrorSystemMode, ///< Error determining the system mode - ErrorLoader, ///< Error loading the specified application - ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption + Success, ///< Succeeded + ErrorNotInitialized, ///< Error trying to use core prior to initialization + ErrorGetLoader, ///< Error finding the correct application loader + ErrorSystemMode, ///< Error determining the system mode + ErrorLoader, ///< Error loading the specified application + ErrorLoader_ErrorMissingKeys, ///< Error because the key/keys needed to run could not be + ///< found. + ErrorLoader_ErrorDecrypting, ///< Error loading the specified application due to encryption ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an /// invalid format ErrorSystemFiles, ///< Error in finding system files @@ -76,16 +82,28 @@ public: */ ResultStatus SingleStep(); + /** + * Invalidate the CPU instruction caches + * This function should only be used by GDB Stub to support breakpoints, memory updates and + * step/continue commands. + */ + void InvalidateCpuInstructionCaches() { + for (auto& cpu : cpu_cores) { + cpu->ArmInterface().ClearInstructionCache(); + } + } + /// Shutdown the emulated system. void Shutdown(); /** * Load an executable application. - * @param emu_window Pointer to the host-system window used for video output and keyboard input. + * @param emu_window Reference to the host-system window used for video output and keyboard + * input. * @param filepath String path to the executable application to load on the host file system. * @returns ResultStatus code, indicating if the operation succeeded. */ - ResultStatus Load(EmuWindow* emu_window, const std::string& filepath); + ResultStatus Load(EmuWindow& emu_window, const std::string& filepath); /** * Indicates if the emulated system is powered on (all subsystems initialized and able to run an @@ -126,11 +144,26 @@ public: /// Gets a CPU interface to the CPU core with the specified index Cpu& CpuCore(size_t core_index); - /// Gets the GPU interface + /// Gets a mutable reference to the GPU interface Tegra::GPU& GPU() { return *gpu_core; } + /// Gets an immutable reference to the GPU interface. + const Tegra::GPU& GPU() const { + return *gpu_core; + } + + /// Gets a mutable reference to the renderer. + VideoCore::RendererBase& Renderer() { + return *renderer; + } + + /// Gets an immutable reference to the renderer. + const VideoCore::RendererBase& Renderer() const { + return *renderer; + } + /// Gets the scheduler for the CPU core that is currently running Kernel::Scheduler& CurrentScheduler() { return *CurrentCpuCore().Scheduler(); @@ -186,14 +219,15 @@ private: /** * Initialize the emulated system. - * @param emu_window Pointer to the host-system window used for video output and keyboard input. - * @param system_mode The system mode. + * @param emu_window Reference to the host-system window used for video output and keyboard + * input. * @return ResultStatus code, indicating if the operation succeeded. */ - ResultStatus Init(EmuWindow* emu_window, u32 system_mode); + ResultStatus Init(EmuWindow& emu_window); /// AppLoader used to load the current executing application std::unique_ptr<Loader::AppLoader> app_loader; + std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<Tegra::GPU> gpu_core; std::shared_ptr<Tegra::DebugContext> debug_context; Kernel::SharedPtr<Kernel::Process> current_process; diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 54e15a701..46a522fcd 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -12,7 +12,6 @@ #include "core/arm/unicorn/arm_unicorn.h" #include "core/core_cpu.h" #include "core/core_timing.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/scheduler.h" #include "core/hle/kernel/thread.h" #include "core/settings.h" diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index a1b6f96f1..d3bb6f818 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -141,7 +141,7 @@ void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 user ForceExceptionCheck(cycles_into_future); event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata) { @@ -156,7 +156,7 @@ void UnscheduleEvent(const EventType* event_type, u64 userdata) { // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { event_queue.erase(itr, event_queue.end()); - std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -167,7 +167,7 @@ void RemoveEvent(const EventType* event_type) { // Removing random items breaks the invariant so we have to re-establish it. if (itr != event_queue.end()) { event_queue.erase(itr, event_queue.end()); - std::make_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -190,7 +190,7 @@ void MoveEvents() { for (Event ev; ts_queue.Pop(ev);) { ev.fifo_order = event_fifo_id++; event_queue.emplace_back(std::move(ev)); - std::push_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); } } @@ -205,7 +205,7 @@ void Advance() { while (!event_queue.empty() && event_queue.front().time <= global_timer) { Event evt = std::move(event_queue.front()); - std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>()); + std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); event_queue.pop_back(); evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time)); } @@ -226,8 +226,8 @@ void Idle() { downcount = 0; } -u64 GetGlobalTimeUs() { - return GetTicks() * 1000000 / BASE_CLOCK_RATE; +std::chrono::microseconds GetGlobalTimeUs() { + return std::chrono::microseconds{GetTicks() * 1000000 / BASE_CLOCK_RATE}; } int GetDowncount() { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 7fe6380ad..dfa161c0d 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -17,12 +17,17 @@ * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") */ +#include <chrono> #include <functional> #include <string> #include "common/common_types.h" namespace CoreTiming { +struct EventType; + +using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; + /** * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is * required to end slice -1 and start slice 0 before the first cycle of code is executed. @@ -30,8 +35,6 @@ namespace CoreTiming { void Init(); void Shutdown(); -typedef std::function<void(u64 userdata, int cycles_late)> TimedCallback; - /** * This should only be called from the emu thread, if you are calling it any other thread, you are * doing something evil @@ -40,8 +43,6 @@ u64 GetTicks(); u64 GetIdleTicks(); void AddTicks(u64 ticks); -struct EventType; - /** * Returns the event_type identifier. if name is not unique, it will assert. */ @@ -86,7 +87,7 @@ void ClearPendingEvents(); void ForceExceptionCheck(s64 cycles); -u64 GetGlobalTimeUs(); +std::chrono::microseconds GetGlobalTimeUs(); int GetDowncount(); diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp new file mode 100644 index 000000000..a9876c83e --- /dev/null +++ b/src/core/crypto/aes_util.cpp @@ -0,0 +1,115 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <mbedtls/cipher.h> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/crypto/aes_util.h" +#include "core/crypto/key_manager.h" + +namespace Core::Crypto { +namespace { +std::vector<u8> CalculateNintendoTweak(size_t sector_id) { + std::vector<u8> out(0x10); + for (size_t i = 0xF; i <= 0xF; --i) { + out[i] = sector_id & 0xFF; + sector_id >>= 8; + } + return out; +} +} // Anonymous namespace + +static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), + "CTR has incorrect value."); +static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB), + "ECB has incorrect value."); +static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS), + "XTS has incorrect value."); + +// Structure to hide mbedtls types from header file +struct CipherContext { + mbedtls_cipher_context_t encryption_context; + mbedtls_cipher_context_t decryption_context; +}; + +template <typename Key, size_t KeySize> +Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) + : ctx(std::make_unique<CipherContext>()) { + mbedtls_cipher_init(&ctx->encryption_context); + mbedtls_cipher_init(&ctx->decryption_context); + + ASSERT_MSG((mbedtls_cipher_setup( + &ctx->encryption_context, + mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) || + mbedtls_cipher_setup( + &ctx->decryption_context, + mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode)))) == 0, + "Failed to initialize mbedtls ciphers."); + + ASSERT( + !mbedtls_cipher_setkey(&ctx->encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT)); + ASSERT( + !mbedtls_cipher_setkey(&ctx->decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT)); + //"Failed to set key on mbedtls ciphers."); +} + +template <typename Key, size_t KeySize> +AESCipher<Key, KeySize>::~AESCipher() { + mbedtls_cipher_free(&ctx->encryption_context); + mbedtls_cipher_free(&ctx->decryption_context); +} + +template <typename Key, size_t KeySize> +void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { + ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || + mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, + "Failed to set IV on mbedtls ciphers."); +} + +template <typename Key, size_t KeySize> +void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const { + auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; + + mbedtls_cipher_reset(context); + + size_t written = 0; + if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { + mbedtls_cipher_update(context, src, size, dest, &written); + if (written != size) { + LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", + size, written); + } + } else { + const auto block_size = mbedtls_cipher_get_block_size(context); + + for (size_t offset = 0; offset < size; offset += block_size) { + auto length = std::min<size_t>(block_size, size - offset); + mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); + if (written != length) { + LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", + length, written); + } + } + } + + mbedtls_cipher_finish(context, nullptr, nullptr); +} + +template <typename Key, size_t KeySize> +void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, + size_t sector_size, Op op) { + if (size % sector_size > 0) { + LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size."); + return; + } + + for (size_t i = 0; i < size; i += sector_size) { + SetIV(CalculateNintendoTweak(sector_id++)); + Transcode<u8, u8>(src + i, sector_size, dest + i, op); + } +} + +template class AESCipher<Key128>; +template class AESCipher<Key256>; +} // namespace Core::Crypto
\ No newline at end of file diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h new file mode 100644 index 000000000..8ce9d6612 --- /dev/null +++ b/src/core/crypto/aes_util.h @@ -0,0 +1,64 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <type_traits> +#include <vector> +#include "common/common_types.h" +#include "core/file_sys/vfs.h" + +namespace Core::Crypto { + +struct CipherContext; + +enum class Mode { + CTR = 11, + ECB = 2, + XTS = 70, +}; + +enum class Op { + Encrypt, + Decrypt, +}; + +template <typename Key, size_t KeySize = sizeof(Key)> +class AESCipher { + static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8."); + static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256."); + +public: + AESCipher(Key key, Mode mode); + + ~AESCipher(); + + void SetIV(std::vector<u8> iv); + + template <typename Source, typename Dest> + void Transcode(const Source* src, size_t size, Dest* dest, Op op) const { + static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, + "Transcode source and destination types must be trivially copyable."); + Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op); + } + + void Transcode(const u8* src, size_t size, u8* dest, Op op) const; + + template <typename Source, typename Dest> + void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id, + size_t sector_size, Op op) { + static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, + "XTSTranscode source and destination types must be trivially copyable."); + XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, + sector_size, op); + } + + void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size, + Op op); + +private: + std::unique_ptr<CipherContext> ctx; +}; +} // namespace Core::Crypto diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp new file mode 100644 index 000000000..106db02b3 --- /dev/null +++ b/src/core/crypto/ctr_encryption_layer.cpp @@ -0,0 +1,56 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include "common/assert.h" +#include "core/crypto/ctr_encryption_layer.h" + +namespace Core::Crypto { + +CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset) + : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), + iv(16, 0) {} + +size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { + if (length == 0) + return 0; + + const auto sector_offset = offset & 0xF; + if (sector_offset == 0) { + UpdateIV(base_offset + offset); + std::vector<u8> raw = base->ReadBytes(length, offset); + if (raw.size() != length) + return Read(data, raw.size(), offset); + cipher.Transcode(raw.data(), length, data, Op::Decrypt); + return length; + } + + // offset does not fall on block boundary (0x10) + std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); + UpdateIV(base_offset + offset - sector_offset); + cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt); + size_t read = 0x10 - sector_offset; + + if (length + sector_offset < 0x10) { + std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); + return read; + } + std::memcpy(data, block.data() + sector_offset, read); + return read + Read(data + read, length - read, offset + read); +} + +void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { + const auto length = std::min(iv_.size(), iv.size()); + iv.assign(iv_.cbegin(), iv_.cbegin() + length); +} + +void CTREncryptionLayer::UpdateIV(size_t offset) const { + offset >>= 4; + for (size_t i = 0; i < 8; ++i) { + iv[16 - i - 1] = offset & 0xFF; + offset >>= 8; + } + cipher.SetIV(iv); +} +} // namespace Core::Crypto diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h new file mode 100644 index 000000000..11b8683c7 --- /dev/null +++ b/src/core/crypto/ctr_encryption_layer.h @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include "core/crypto/aes_util.h" +#include "core/crypto/encryption_layer.h" +#include "core/crypto/key_manager.h" + +namespace Core::Crypto { + +// Sits on top of a VirtualFile and provides CTR-mode AES decription. +class CTREncryptionLayer : public EncryptionLayer { +public: + CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset); + + size_t Read(u8* data, size_t length, size_t offset) const override; + + void SetIV(const std::vector<u8>& iv); + +private: + size_t base_offset; + + // Must be mutable as operations modify cipher contexts. + mutable AESCipher<Key128> cipher; + mutable std::vector<u8> iv; + + void UpdateIV(size_t offset) const; +}; + +} // namespace Core::Crypto diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp new file mode 100644 index 000000000..4204527e3 --- /dev/null +++ b/src/core/crypto/encryption_layer.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/crypto/encryption_layer.h" + +namespace Core::Crypto { + +EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {} + +std::string EncryptionLayer::GetName() const { + return base->GetName(); +} + +size_t EncryptionLayer::GetSize() const { + return base->GetSize(); +} + +bool EncryptionLayer::Resize(size_t new_size) { + return false; +} + +std::shared_ptr<FileSys::VfsDirectory> EncryptionLayer::GetContainingDirectory() const { + return base->GetContainingDirectory(); +} + +bool EncryptionLayer::IsWritable() const { + return false; +} + +bool EncryptionLayer::IsReadable() const { + return true; +} + +size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) { + return 0; +} + +bool EncryptionLayer::Rename(std::string_view name) { + return base->Rename(name); +} +} // namespace Core::Crypto diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h new file mode 100644 index 000000000..7f05af9b4 --- /dev/null +++ b/src/core/crypto/encryption_layer.h @@ -0,0 +1,33 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/file_sys/vfs.h" + +namespace Core::Crypto { + +// Basically non-functional class that implements all of the methods that are irrelevant to an +// EncryptionLayer. Reduces duplicate code. +class EncryptionLayer : public FileSys::VfsFile { +public: + explicit EncryptionLayer(FileSys::VirtualFile base); + + size_t Read(u8* data, size_t length, size_t offset) const override = 0; + + std::string GetName() const override; + size_t GetSize() const override; + bool Resize(size_t new_size) override; + std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override; + bool IsWritable() const override; + bool IsReadable() const override; + size_t Write(const u8* data, size_t length, size_t offset) override; + bool Rename(std::string_view name) override; + +protected: + FileSys::VirtualFile base; +}; + +} // namespace Core::Crypto diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp new file mode 100644 index 000000000..fc45e7ab5 --- /dev/null +++ b/src/core/crypto/key_manager.cpp @@ -0,0 +1,208 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <array> +#include <fstream> +#include <locale> +#include <sstream> +#include <string_view> +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/crypto/key_manager.h" +#include "core/settings.h" + +namespace Core::Crypto { + +static u8 ToHexNibble(char c1) { + if (c1 >= 65 && c1 <= 70) + return c1 - 55; + if (c1 >= 97 && c1 <= 102) + return c1 - 87; + if (c1 >= 48 && c1 <= 57) + return c1 - 48; + throw std::logic_error("Invalid hex digit"); +} + +template <size_t Size> +static std::array<u8, Size> HexStringToArray(std::string_view str) { + std::array<u8, Size> out{}; + for (size_t i = 0; i < 2 * Size; i += 2) { + auto d1 = str[i]; + auto d2 = str[i + 1]; + out[i / 2] = (ToHexNibble(d1) << 4) | ToHexNibble(d2); + } + return out; +} + +std::array<u8, 16> operator""_array16(const char* str, size_t len) { + if (len != 32) + throw std::logic_error("Not of correct size."); + return HexStringToArray<16>(str); +} + +std::array<u8, 32> operator""_array32(const char* str, size_t len) { + if (len != 64) + throw std::logic_error("Not of correct size."); + return HexStringToArray<32>(str); +} + +KeyManager::KeyManager() { + // Initialize keys + const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); + const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); + if (Settings::values.use_dev_keys) { + dev_mode = true; + AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); + } else { + dev_mode = false; + AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false); + } + + AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true); +} + +void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { + std::ifstream file(filename); + if (!file.is_open()) + return; + + std::string line; + while (std::getline(file, line)) { + std::vector<std::string> out; + std::stringstream stream(line); + std::string item; + while (std::getline(stream, item, '=')) + out.push_back(std::move(item)); + + if (out.size() != 2) + continue; + + out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); + out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); + + if (is_title_keys) { + auto rights_id_raw = HexStringToArray<16>(out[0]); + u128 rights_id{}; + std::memcpy(rights_id.data(), rights_id_raw.data(), rights_id_raw.size()); + Key128 key = HexStringToArray<16>(out[1]); + SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); + } else { + std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower); + if (s128_file_id.find(out[0]) != s128_file_id.end()) { + const auto index = s128_file_id.at(out[0]); + Key128 key = HexStringToArray<16>(out[1]); + SetKey(index.type, key, index.field1, index.field2); + } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { + const auto index = s256_file_id.at(out[0]); + Key256 key = HexStringToArray<32>(out[1]); + SetKey(index.type, key, index.field1, index.field2); + } + } + } +} + +void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, + const std::string& filename, bool title) { + if (FileUtil::Exists(dir1 + DIR_SEP + filename)) + LoadFromFile(dir1 + DIR_SEP + filename, title); + else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) + LoadFromFile(dir2 + DIR_SEP + filename, title); +} + +bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const { + return s128_keys.find({id, field1, field2}) != s128_keys.end(); +} + +bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const { + return s256_keys.find({id, field1, field2}) != s256_keys.end(); +} + +Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { + if (!HasKey(id, field1, field2)) + return {}; + return s128_keys.at({id, field1, field2}); +} + +Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { + if (!HasKey(id, field1, field2)) + return {}; + return s256_keys.at({id, field1, field2}); +} + +void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { + s128_keys[{id, field1, field2}] = key; +} + +void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { + s256_keys[{id, field1, field2}] = key; +} + +bool KeyManager::KeyFileExists(bool title) { + const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); + const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); + if (title) { + return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || + FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); + } + + if (Settings::values.use_dev_keys) { + return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || + FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); + } + + return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || + FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); +} + +const std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { + {"master_key_00", {S128KeyType::Master, 0, 0}}, + {"master_key_01", {S128KeyType::Master, 1, 0}}, + {"master_key_02", {S128KeyType::Master, 2, 0}}, + {"master_key_03", {S128KeyType::Master, 3, 0}}, + {"master_key_04", {S128KeyType::Master, 4, 0}}, + {"package1_key_00", {S128KeyType::Package1, 0, 0}}, + {"package1_key_01", {S128KeyType::Package1, 1, 0}}, + {"package1_key_02", {S128KeyType::Package1, 2, 0}}, + {"package1_key_03", {S128KeyType::Package1, 3, 0}}, + {"package1_key_04", {S128KeyType::Package1, 4, 0}}, + {"package2_key_00", {S128KeyType::Package2, 0, 0}}, + {"package2_key_01", {S128KeyType::Package2, 1, 0}}, + {"package2_key_02", {S128KeyType::Package2, 2, 0}}, + {"package2_key_03", {S128KeyType::Package2, 3, 0}}, + {"package2_key_04", {S128KeyType::Package2, 4, 0}}, + {"titlekek_00", {S128KeyType::Titlekek, 0, 0}}, + {"titlekek_01", {S128KeyType::Titlekek, 1, 0}}, + {"titlekek_02", {S128KeyType::Titlekek, 2, 0}}, + {"titlekek_03", {S128KeyType::Titlekek, 3, 0}}, + {"titlekek_04", {S128KeyType::Titlekek, 4, 0}}, + {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, + {"key_area_key_application_00", + {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_application_01", + {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_application_02", + {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_application_03", + {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_application_04", + {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}}, + {"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}}, + {"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}}, + {"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}}, + {"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}}, + {"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}}, + {"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}}, +}; + +const std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { + {"header_key", {S256KeyType::Header, 0, 0}}, + {"sd_card_save_key", {S256KeyType::SDSave, 0, 0}}, + {"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}}, +}; +} // namespace Core::Crypto diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h new file mode 100644 index 000000000..c4c53cefc --- /dev/null +++ b/src/core/crypto/key_manager.h @@ -0,0 +1,120 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <string> +#include <type_traits> +#include <unordered_map> +#include <vector> +#include <fmt/format.h> +#include "common/common_types.h" + +namespace Core::Crypto { + +using Key128 = std::array<u8, 0x10>; +using Key256 = std::array<u8, 0x20>; +using SHA256Hash = std::array<u8, 0x20>; + +static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); +static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big."); + +enum class S256KeyType : u64 { + Header, // + SDSave, // + SDNCA, // +}; + +enum class S128KeyType : u64 { + Master, // f1=crypto revision + Package1, // f1=crypto revision + Package2, // f1=crypto revision + Titlekek, // f1=crypto revision + ETicketRSAKek, // + KeyArea, // f1=crypto revision f2=type {app, ocean, system} + SDSeed, // + Titlekey, // f1=rights id LSB f2=rights id MSB +}; + +enum class KeyAreaKeyType : u8 { + Application, + Ocean, + System, +}; + +template <typename KeyType> +struct KeyIndex { + KeyType type; + u64 field1; + u64 field2; + + std::string DebugInfo() const { + u8 key_size = 16; + if constexpr (std::is_same_v<KeyType, S256KeyType>) + key_size = 32; + return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size, + static_cast<u8>(type), field1, field2); + } +}; + +// The following two (== and hash) are so KeyIndex can be a key in unordered_map + +template <typename KeyType> +bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { + return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2); +} + +template <typename KeyType> +bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { + return !operator==(lhs, rhs); +} + +} // namespace Core::Crypto + +namespace std { +template <typename KeyType> +struct hash<Core::Crypto::KeyIndex<KeyType>> { + size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const { + using std::hash; + + return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^ + (hash<u64>()(k.field2) << 1); + } +}; +} // namespace std + +namespace Core::Crypto { + +std::array<u8, 0x10> operator"" _array16(const char* str, size_t len); +std::array<u8, 0x20> operator"" _array32(const char* str, size_t len); + +class KeyManager { +public: + KeyManager(); + + bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; + bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; + + Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; + Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; + + void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); + void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); + + static bool KeyFileExists(bool title); + +private: + std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys; + std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys; + + bool dev_mode; + void LoadFromFile(const std::string& filename, bool is_title_keys); + void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, + const std::string& filename, bool title); + + static const std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id; + static const std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id; +}; +} // namespace Core::Crypto diff --git a/src/core/crypto/sha_util.cpp b/src/core/crypto/sha_util.cpp new file mode 100644 index 000000000..180008a85 --- /dev/null +++ b/src/core/crypto/sha_util.cpp @@ -0,0 +1,5 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +namespace Crypto {} // namespace Crypto diff --git a/src/core/crypto/sha_util.h b/src/core/crypto/sha_util.h new file mode 100644 index 000000000..fa3fa9d33 --- /dev/null +++ b/src/core/crypto/sha_util.h @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "core/file_sys/vfs.h" +#include "key_manager.h" +#include "mbedtls/cipher.h" + +namespace Crypto { +typedef std::array<u8, 0x20> SHA256Hash; + +inline SHA256Hash operator"" _HASH(const char* data, size_t len) { + if (len != 0x40) + return {}; +} + +} // namespace Crypto diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp new file mode 100644 index 000000000..395eea8ae --- /dev/null +++ b/src/core/file_sys/card_image.cpp @@ -0,0 +1,149 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <array> +#include <string> +#include <core/loader/loader.h> +#include "core/file_sys/card_image.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/vfs_offset.h" + +namespace FileSys { + +XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { + if (file->ReadObject(&header) != sizeof(GamecardHeader)) { + status = Loader::ResultStatus::ErrorInvalidFormat; + return; + } + + if (header.magic != Common::MakeMagic('H', 'E', 'A', 'D')) { + status = Loader::ResultStatus::ErrorInvalidFormat; + return; + } + + PartitionFilesystem main_hfs( + std::make_shared<OffsetVfsFile>(file, header.hfs_size, header.hfs_offset)); + + if (main_hfs.GetStatus() != Loader::ResultStatus::Success) { + status = main_hfs.GetStatus(); + return; + } + + static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", + "logo"}; + + for (XCIPartition partition : + {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { + auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]); + if (raw != nullptr) + partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw); + } + + auto result = AddNCAFromPartition(XCIPartition::Secure); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + + result = AddNCAFromPartition(XCIPartition::Update); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + + result = AddNCAFromPartition(XCIPartition::Normal); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + + if (GetFormatVersion() >= 0x2) { + result = AddNCAFromPartition(XCIPartition::Logo); + if (result != Loader::ResultStatus::Success) { + status = result; + return; + } + } + + status = Loader::ResultStatus::Success; +} + +Loader::ResultStatus XCI::GetStatus() const { + return status; +} + +VirtualDir XCI::GetPartition(XCIPartition partition) const { + return partitions[static_cast<size_t>(partition)]; +} + +VirtualDir XCI::GetSecurePartition() const { + return GetPartition(XCIPartition::Secure); +} + +VirtualDir XCI::GetNormalPartition() const { + return GetPartition(XCIPartition::Normal); +} + +VirtualDir XCI::GetUpdatePartition() const { + return GetPartition(XCIPartition::Update); +} + +VirtualDir XCI::GetLogoPartition() const { + return GetPartition(XCIPartition::Logo); +} + +std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { + const auto iter = + std::find_if(ncas.begin(), ncas.end(), + [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; }); + return iter == ncas.end() ? nullptr : *iter; +} + +VirtualFile XCI::GetNCAFileByType(NCAContentType type) const { + auto nca = GetNCAByType(type); + if (nca != nullptr) + return nca->GetBaseFile(); + return nullptr; +} + +std::vector<std::shared_ptr<VfsFile>> XCI::GetFiles() const { + return {}; +} + +std::vector<std::shared_ptr<VfsDirectory>> XCI::GetSubdirectories() const { + return std::vector<std::shared_ptr<VfsDirectory>>(); +} + +std::string XCI::GetName() const { + return file->GetName(); +} + +std::shared_ptr<VfsDirectory> XCI::GetParentDirectory() const { + return file->GetContainingDirectory(); +} + +bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { + return false; +} + +Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { + if (partitions[static_cast<size_t>(part)] == nullptr) { + return Loader::ResultStatus::ErrorInvalidFormat; + } + + for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) { + if (file->GetExtension() != "nca") + continue; + auto nca = std::make_shared<NCA>(file); + if (nca->GetStatus() == Loader::ResultStatus::Success) + ncas.push_back(std::move(nca)); + } + + return Loader::ResultStatus::Success; +} + +u8 XCI::GetFormatVersion() const { + return GetLogoPartition() == nullptr ? 0x1 : 0x2; +} +} // namespace FileSys diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h new file mode 100644 index 000000000..e089d737c --- /dev/null +++ b/src/core/file_sys/card_image.h @@ -0,0 +1,96 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <vector> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/vfs.h" +#include "core/loader/loader.h" + +namespace FileSys { + +enum class GamecardSize : u8 { + S_1GB = 0xFA, + S_2GB = 0xF8, + S_4GB = 0xF0, + S_8GB = 0xE0, + S_16GB = 0xE1, + S_32GB = 0xE2, +}; + +struct GamecardInfo { + std::array<u8, 0x70> data; +}; +static_assert(sizeof(GamecardInfo) == 0x70, "GamecardInfo has incorrect size."); + +struct GamecardHeader { + std::array<u8, 0x100> signature; + u32_le magic; + u32_le secure_area_start; + u32_le backup_area_start; + u8 kek_index; + GamecardSize size; + u8 header_version; + u8 flags; + u64_le package_id; + u64_le valid_data_end; + u128 info_iv; + u64_le hfs_offset; + u64_le hfs_size; + std::array<u8, 0x20> hfs_header_hash; + std::array<u8, 0x20> initial_data_hash; + u32_le secure_mode_flag; + u32_le title_key_flag; + u32_le key_flag; + u32_le normal_area_end; + GamecardInfo info; +}; +static_assert(sizeof(GamecardHeader) == 0x200, "GamecardHeader has incorrect size."); + +enum class XCIPartition : u8 { Update, Normal, Secure, Logo }; + +class XCI : public ReadOnlyVfsDirectory { +public: + explicit XCI(VirtualFile file); + + Loader::ResultStatus GetStatus() const; + + u8 GetFormatVersion() const; + + VirtualDir GetPartition(XCIPartition partition) const; + VirtualDir GetSecurePartition() const; + VirtualDir GetNormalPartition() const; + VirtualDir GetUpdatePartition() const; + VirtualDir GetLogoPartition() const; + + std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; + VirtualFile GetNCAFileByType(NCAContentType type) const; + + std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; + + std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; + + std::string GetName() const override; + + std::shared_ptr<VfsDirectory> GetParentDirectory() const override; + +protected: + bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; + +private: + Loader::ResultStatus AddNCAFromPartition(XCIPartition part); + + VirtualFile file; + GamecardHeader header{}; + + Loader::ResultStatus status; + + std::vector<VirtualDir> partitions; + std::vector<std::shared_ptr<NCA>> ncas; +}; +} // namespace FileSys diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index d6b20c047..3529166ac 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -4,9 +4,12 @@ #include <algorithm> #include <utility> - +#include <boost/optional.hpp> #include "common/logging/log.h" +#include "core/crypto/aes_util.h" +#include "core/crypto/ctr_encryption_layer.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/romfs.h" #include "core/file_sys/vfs_offset.h" #include "core/loader/loader.h" @@ -28,11 +31,19 @@ enum class NCASectionFilesystemType : u8 { struct NCASectionHeaderBlock { INSERT_PADDING_BYTES(3); NCASectionFilesystemType filesystem_type; - u8 crypto_type; + NCASectionCryptoType crypto_type; INSERT_PADDING_BYTES(3); }; static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); +struct NCASectionRaw { + NCASectionHeaderBlock header; + std::array<u8, 0x138> block_data; + std::array<u8, 0x8> section_ctr; + INSERT_PADDING_BYTES(0xB8); +}; +static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); + struct PFS0Superblock { NCASectionHeaderBlock header_block; std::array<u8, 0x20> hash; @@ -42,79 +53,200 @@ struct PFS0Superblock { u64_le hash_table_size; u64_le pfs0_header_offset; u64_le pfs0_size; - INSERT_PADDING_BYTES(432); + INSERT_PADDING_BYTES(0x1B0); }; static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); -struct IVFCLevel { - u64_le offset; - u64_le size; - u32_le block_size; - u32_le reserved; -}; -static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); - struct RomFSSuperblock { NCASectionHeaderBlock header_block; - u32_le magic; - u32_le magic_number; - INSERT_PADDING_BYTES(8); - std::array<IVFCLevel, 6> levels; - INSERT_PADDING_BYTES(64); + IVFCHeader ivfc; + INSERT_PADDING_BYTES(0x118); }; -static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); +static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); + +union NCASectionHeader { + NCASectionRaw raw; + PFS0Superblock pfs0; + RomFSSuperblock romfs; +}; +static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); + +bool IsValidNCA(const NCAHeader& header) { + // TODO(DarkLordZach): Add NCA2/NCA0 support. + return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); +} + +u8 NCA::GetCryptoRevision() const { + u8 master_key_id = header.crypto_type; + if (header.crypto_type_2 > master_key_id) + master_key_id = header.crypto_type_2; + if (master_key_id > 0) + --master_key_id; + return master_key_id; +} + +boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { + const auto master_key_id = GetCryptoRevision(); + + if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) + return boost::none; + + std::vector<u8> key_area(header.key_area.begin(), header.key_area.end()); + Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( + keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index), + Core::Crypto::Mode::ECB); + cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt); + + Core::Crypto::Key128 out; + if (type == NCASectionCryptoType::XTS) + std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); + else if (type == NCASectionCryptoType::CTR) + std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); + else + LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", + static_cast<u8>(type)); + u128 out_128{}; + memcpy(out_128.data(), out.data(), 16); + LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", + master_key_id, header.key_index, out_128[1], out_128[0]); + + return out; +} + +boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const { + const auto master_key_id = GetCryptoRevision(); + + u128 rights_id{}; + memcpy(rights_id.data(), header.rights_id.data(), 16); + if (rights_id == u128{}) + return boost::none; + + auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); + if (titlekey == Core::Crypto::Key128{}) + return boost::none; + Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( + keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB); + cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt); + + return titlekey; +} + +VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const { + if (!encrypted) + return in; + + switch (s_header.raw.header.crypto_type) { + case NCASectionCryptoType::NONE: + LOG_DEBUG(Crypto, "called with mode=NONE"); + return in; + case NCASectionCryptoType::CTR: + LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); + { + boost::optional<Core::Crypto::Key128> key = boost::none; + if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(), + [](char c) { return c == 0; }) == header.rights_id.end()) { + key = GetKeyAreaKey(NCASectionCryptoType::CTR); + } else { + key = GetTitlekey(); + } + + if (key == boost::none) + return nullptr; + auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( + std::move(in), key.value(), starting_offset); + std::vector<u8> iv(16); + for (u8 i = 0; i < 8; ++i) + iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; + out->SetIV(iv); + return std::static_pointer_cast<VfsFile>(out); + } + case NCASectionCryptoType::XTS: + // TODO(DarkLordZach): Implement XTSEncryptionLayer. + default: + LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}", + static_cast<u8>(s_header.raw.header.crypto_type)); + return nullptr; + } +} NCA::NCA(VirtualFile file_) : file(std::move(file_)) { if (sizeof(NCAHeader) != file->ReadObject(&header)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); + LOG_ERROR(Loader, "File reader errored out during header read."); + + encrypted = false; if (!IsValidNCA(header)) { - status = Loader::ResultStatus::ErrorInvalidFormat; - return; + NCAHeader dec_header{}; + Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, + Core::Crypto::Op::Decrypt); + if (IsValidNCA(dec_header)) { + header = dec_header; + encrypted = true; + } else { + if (!keys.HasKey(Core::Crypto::S256KeyType::Header)) + status = Loader::ResultStatus::ErrorMissingKeys; + else + status = Loader::ResultStatus::ErrorDecrypting; + return; + } } - std::ptrdiff_t number_sections = + const std::ptrdiff_t number_sections = std::count_if(std::begin(header.section_tables), std::end(header.section_tables), [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); + std::vector<NCASectionHeader> sections(number_sections); + const auto length_sections = SECTION_HEADER_SIZE * number_sections; + + if (encrypted) { + auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); + Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( + keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); + cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, + Core::Crypto::Op::Decrypt); + } else { + file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); + } + for (std::ptrdiff_t i = 0; i < number_sections; ++i) { - // Seek to beginning of this section. - NCASectionHeaderBlock block{}; - if (sizeof(NCASectionHeaderBlock) != - file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); - - if (block.filesystem_type == NCASectionFilesystemType::ROMFS) { - RomFSSuperblock sb{}; - if (sizeof(RomFSSuperblock) != - file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); + auto section = sections[i]; + if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { const size_t romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + - sb.levels[IVFC_MAX_LEVEL - 1].offset; - const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; - files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); - romfs = files.back(); - } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { - PFS0Superblock sb{}; - // Seek back to beginning of this section. - if (sizeof(PFS0Superblock) != - file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) - LOG_CRITICAL(Loader, "File reader errored out during header read."); - + section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; + const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; + auto dec = + Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset), + romfs_offset); + if (dec != nullptr) { + files.push_back(std::move(dec)); + romfs = files.back(); + } else { + status = Loader::ResultStatus::ErrorMissingKeys; + return; + } + } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * MEDIA_OFFSET_MULTIPLIER) + - sb.pfs0_header_offset; + section.pfs0.pfs0_header_offset; u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - header.section_tables[i].media_offset); - auto npfs = std::make_shared<PartitionFilesystem>( - std::make_shared<OffsetVfsFile>(file, size, offset)); + auto dec = + Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset); + if (dec != nullptr) { + auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec)); - if (npfs->GetStatus() == Loader::ResultStatus::Success) { - dirs.emplace_back(npfs); - if (IsDirectoryExeFS(dirs.back())) - exefs = dirs.back(); + if (npfs->GetStatus() == Loader::ResultStatus::Success) { + dirs.push_back(std::move(npfs)); + if (IsDirectoryExeFS(dirs.back())) + exefs = dirs.back(); + } + } else { + status = Loader::ResultStatus::ErrorMissingKeys; + return; } } } @@ -164,6 +296,10 @@ VirtualDir NCA::GetExeFS() const { return exefs; } +VirtualFile NCA::GetBaseFile() const { + return file; +} + bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { return false; } diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 0b8b9db61..a8879d9a8 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -8,14 +8,18 @@ #include <memory> #include <string> #include <vector> - +#include <boost/optional.hpp> #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/crypto/key_manager.h" #include "core/file_sys/partition_filesystem.h" +#include "core/loader/loader.h" namespace FileSys { +union NCASectionHeader; + enum class NCAContentType : u8 { Program = 0, Meta = 1, @@ -24,6 +28,13 @@ enum class NCAContentType : u8 { Data = 4, }; +enum class NCASectionCryptoType : u8 { + NONE = 1, + XTS = 2, + CTR = 3, + BKTR = 4, +}; + struct NCASectionTableEntry { u32_le media_offset; u32_le media_end_offset; @@ -48,7 +59,7 @@ struct NCAHeader { std::array<u8, 0x10> rights_id; std::array<NCASectionTableEntry, 0x4> section_tables; std::array<std::array<u8, 0x20>, 0x4> hash_tables; - std::array<std::array<u8, 0x10>, 0x4> key_area; + std::array<u8, 0x40> key_area; INSERT_PADDING_BYTES(0xC0); }; static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); @@ -58,10 +69,7 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) { return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; } -inline bool IsValidNCA(const NCAHeader& header) { - return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || - header.magic == Common::MakeMagic('N', 'C', 'A', '3'); -} +bool IsValidNCA(const NCAHeader& header); // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. // After construction, use GetStatus to determine if the file is valid and ready to be used. @@ -81,10 +89,17 @@ public: VirtualFile GetRomFS() const; VirtualDir GetExeFS() const; + VirtualFile GetBaseFile() const; + protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; private: + u8 GetCryptoRevision() const; + boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; + boost::optional<Core::Crypto::Key128> GetTitlekey() const; + VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; + std::vector<VirtualDir> dirs; std::vector<VirtualFile> files; @@ -95,6 +110,10 @@ private: NCAHeader header{}; Loader::ResultStatus status{}; + + bool encrypted; + + Core::Crypto::KeyManager keys; }; } // namespace FileSys diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 521e21078..47e032b19 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp @@ -97,9 +97,8 @@ void PartitionFilesystem::PrintDebugInfo() const { LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic); LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); for (u32 i = 0; i < pfs_header.num_entries; i++) { - LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, - pfs_files[i]->GetName(), pfs_files[i]->GetSize(), - dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset()); + LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes)", i, + pfs_files[i]->GetName(), pfs_files[i]->GetSize()); } } diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp new file mode 100644 index 000000000..ff3ddb29c --- /dev/null +++ b/src/core/file_sys/romfs.cpp @@ -0,0 +1,124 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_types.h" +#include "common/swap.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/vfs.h" +#include "core/file_sys/vfs_offset.h" +#include "core/file_sys/vfs_vector.h" + +namespace FileSys { + +constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF; + +struct TableLocation { + u64_le offset; + u64_le size; +}; +static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size."); + +struct RomFSHeader { + u64_le header_size; + TableLocation directory_hash; + TableLocation directory_meta; + TableLocation file_hash; + TableLocation file_meta; + u64_le data_offset; +}; +static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); + +struct DirectoryEntry { + u32_le sibling; + u32_le child_dir; + u32_le child_file; + u32_le hash; + u32_le name_length; +}; +static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); + +struct FileEntry { + u32_le parent; + u32_le sibling; + u64_le offset; + u64_le size; + u32_le hash; + u32_le name_length; +}; +static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); + +template <typename Entry> +static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) { + Entry entry{}; + if (file->ReadObject(&entry, offset) != sizeof(Entry)) + return {}; + std::string string(entry.name_length, '\0'); + if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size()) + return {}; + return {entry, string}; +} + +void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset, + std::shared_ptr<VectorVfsDirectory> parent) { + while (true) { + auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); + + parent->AddFile(std::make_shared<OffsetVfsFile>( + file, entry.first.size, entry.first.offset + data_offset, entry.second, parent)); + + if (entry.first.sibling == ROMFS_ENTRY_EMPTY) + break; + + this_file_offset = entry.first.sibling; + } +} + +void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset, + u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) { + while (true) { + auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); + auto current = std::make_shared<VectorVfsDirectory>( + std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second); + + if (entry.first.child_file != ROMFS_ENTRY_EMPTY) { + ProcessFile(file, file_offset, data_offset, entry.first.child_file, current); + } + + if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) { + ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir, + current); + } + + parent->AddDirectory(current); + if (entry.first.sibling == ROMFS_ENTRY_EMPTY) + break; + this_dir_offset = entry.first.sibling; + } +} + +VirtualDir ExtractRomFS(VirtualFile file) { + RomFSHeader header{}; + if (file->ReadObject(&header) != sizeof(RomFSHeader)) + return nullptr; + + if (header.header_size != sizeof(RomFSHeader)) + return nullptr; + + const u64 file_offset = header.file_meta.offset; + const u64 dir_offset = header.directory_meta.offset + 4; + + const auto root = + std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, + file->GetContainingDirectory(), file->GetName()); + + ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root); + + VirtualDir out = std::move(root); + + while (out->GetSubdirectory("") != nullptr) + out = out->GetSubdirectory(""); + + return out; +} +} // namespace FileSys diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h new file mode 100644 index 000000000..03a876d22 --- /dev/null +++ b/src/core/file_sys/romfs.h @@ -0,0 +1,35 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include "common/common_funcs.h" +#include "common/swap.h" +#include "core/file_sys/vfs.h" + +namespace FileSys { + +struct IVFCLevel { + u64_le offset; + u64_le size; + u32_le block_size; + u32_le reserved; +}; +static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); + +struct IVFCHeader { + u32_le magic; + u32_le magic_number; + INSERT_PADDING_BYTES(8); + std::array<IVFCLevel, 6> levels; + INSERT_PADDING_BYTES(64); +}; +static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); + +// Converts a RomFS binary blob to VFS Filesystem +// Returns nullptr on failure +VirtualDir ExtractRomFS(VirtualFile file); + +} // namespace FileSys diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index b99a4fd5b..dae1c16ef 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp @@ -46,6 +46,13 @@ size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) { return Write(data.data(), data.size(), offset); } +std::string VfsFile::GetFullPath() const { + if (GetContainingDirectory() == nullptr) + return "/" + GetName(); + + return GetContainingDirectory()->GetFullPath() + "/" + GetName(); +} + std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { auto vec = FileUtil::SplitPathComponents(path); vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), @@ -243,6 +250,13 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); } +std::string VfsDirectory::GetFullPath() const { + if (IsRoot()) + return GetName(); + + return GetParentDirectory()->GetFullPath() + "/" + GetName(); +} + bool ReadOnlyVfsDirectory::IsWritable() const { return false; } @@ -270,4 +284,33 @@ bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) { bool ReadOnlyVfsDirectory::Rename(std::string_view name) { return false; } + +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) { + if (file1->GetSize() != file2->GetSize()) + return false; + + std::vector<u8> f1_v(block_size); + std::vector<u8> f2_v(block_size); + for (size_t i = 0; i < file1->GetSize(); i += block_size) { + auto f1_vs = file1->Read(f1_v.data(), block_size, i); + auto f2_vs = file2->Read(f2_v.data(), block_size, i); + + if (f1_vs != f2_vs) + return false; + auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); + if (iters.first != f1_v.end() && iters.second != f2_v.end()) + return false; + } + + return true; +} + +bool VfsRawCopy(VirtualFile src, VirtualFile dest) { + if (src == nullptr || dest == nullptr) + return false; + if (!dest->Resize(src->GetSize())) + return false; + std::vector<u8> data = src->ReadAllBytes(); + return dest->WriteBytes(data, 0) == data.size(); +} } // namespace FileSys diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 4a13b8378..fab9e2b45 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h @@ -113,6 +113,9 @@ struct VfsFile : NonCopyable { // Renames the file to name. Returns whether or not the operation was successsful. virtual bool Rename(std::string_view name) = 0; + + // Returns the full path of this file as a string, recursively + virtual std::string GetFullPath() const; }; // A class representing a directory in an abstract filesystem. @@ -213,6 +216,17 @@ struct VfsDirectory : NonCopyable { return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p)); } + bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function, + const std::string& file) { + auto file_p = GetFile(file); + if (file_p == nullptr) + return false; + return ReplaceFileWithSubdirectory(file_p, function(file_p)); + } + + // Returns the full path of this directory as a string, recursively + virtual std::string GetFullPath() const; + protected: // Backend for InterpretAsDirectory. // Removes all references to file and adds a reference to dir in the directory's implementation. @@ -230,4 +244,13 @@ struct ReadOnlyVfsDirectory : public VfsDirectory { bool DeleteFile(std::string_view name) override; bool Rename(std::string_view name) override; }; + +// Compare the two files, byte-for-byte, in increments specificed by block_size +bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200); + +// A method that copies the raw data between two different implementations of VirtualFile. If you +// are using the same implementation, it is probably better to use the Copy method in the parent +// directory of src/dest. +bool VfsRawCopy(VirtualFile src, VirtualFile dest); + } // namespace FileSys diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index a40331cef..847cde2f5 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp @@ -10,8 +10,9 @@ namespace FileSys { OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, - std::string name_) - : file(std::move(file_)), offset(offset_), size(size_), name(std::move(name_)) {} + std::string name_, VirtualDir parent_) + : file(file_), offset(offset_), size(size_), name(std::move(name_)), + parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} std::string OffsetVfsFile::GetName() const { return name.empty() ? file->GetName() : name; @@ -35,7 +36,7 @@ bool OffsetVfsFile::Resize(size_t new_size) { } std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const { - return file->GetContainingDirectory(); + return parent; } bool OffsetVfsFile::IsWritable() const { diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h index 4f471e3ba..235970dc5 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs_offset.h @@ -17,7 +17,7 @@ namespace FileSys { // the size of this wrapper. struct OffsetVfsFile : public VfsFile { OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, - std::string new_name = ""); + std::string new_name = "", VirtualDir new_parent = nullptr); std::string GetName() const override; size_t GetSize() const override; @@ -44,6 +44,7 @@ private: size_t offset; size_t size; std::string name; + VirtualDir parent; }; } // namespace FileSys diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 9ce2e1efa..82d54da4a 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -195,6 +195,12 @@ bool RealVfsDirectory::Rename(std::string_view name) { return FileUtil::Rename(path, new_name); } +std::string RealVfsDirectory::GetFullPath() const { + auto out = path; + std::replace(out.begin(), out.end(), '\\', '/'); + return out; +} + bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { const auto iter = std::find(files.begin(), files.end(), file); if (iter == files.end()) diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 2151211c9..243d58576 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h @@ -41,7 +41,7 @@ private: // An implementation of VfsDirectory that represents a directory on the user's computer. struct RealVfsDirectory : public VfsDirectory { - RealVfsDirectory(const std::string& path, Mode perms); + RealVfsDirectory(const std::string& path, Mode perms = Mode::Read); std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; @@ -54,6 +54,7 @@ struct RealVfsDirectory : public VfsDirectory { bool DeleteSubdirectory(std::string_view name) override; bool DeleteFile(std::string_view name) override; bool Rename(std::string_view name) override; + std::string GetFullPath() const override; protected: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp new file mode 100644 index 000000000..fda603960 --- /dev/null +++ b/src/core/file_sys/vfs_vector.cpp @@ -0,0 +1,86 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <utility> +#include "core/file_sys/vfs_vector.h" + +namespace FileSys { +VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, + std::vector<VirtualDir> dirs_, VirtualDir parent_, + std::string name_) + : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), + name(std::move(name_)) {} + +std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { + return files; +} + +std::vector<std::shared_ptr<VfsDirectory>> VectorVfsDirectory::GetSubdirectories() const { + return dirs; +} + +bool VectorVfsDirectory::IsWritable() const { + return false; +} + +bool VectorVfsDirectory::IsReadable() const { + return true; +} + +std::string VectorVfsDirectory::GetName() const { + return name; +} + +std::shared_ptr<VfsDirectory> VectorVfsDirectory::GetParentDirectory() const { + return parent; +} + +template <typename T> +static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) { + const auto iter = + std::find_if(vec.begin(), vec.end(), [name](const T& e) { return e->GetName() == name; }); + if (iter == vec.end()) + return false; + + vec.erase(iter); + return true; +} + +bool VectorVfsDirectory::DeleteSubdirectory(std::string_view name) { + return FindAndRemoveVectorElement(dirs, name); +} + +bool VectorVfsDirectory::DeleteFile(std::string_view name) { + return FindAndRemoveVectorElement(files, name); +} + +bool VectorVfsDirectory::Rename(std::string_view name_) { + name = name_; + return true; +} + +std::shared_ptr<VfsDirectory> VectorVfsDirectory::CreateSubdirectory(std::string_view name) { + return nullptr; +} + +std::shared_ptr<VfsFile> VectorVfsDirectory::CreateFile(std::string_view name) { + return nullptr; +} + +void VectorVfsDirectory::AddFile(VirtualFile file) { + files.push_back(std::move(file)); +} + +void VectorVfsDirectory::AddDirectory(VirtualDir dir) { + dirs.push_back(std::move(dir)); +} + +bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { + if (!DeleteFile(file->GetName())) + return false; + dirs.emplace_back(std::move(dir)); + return true; +} +} // namespace FileSys diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h new file mode 100644 index 000000000..ba469647b --- /dev/null +++ b/src/core/file_sys/vfs_vector.h @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/file_sys/vfs.h" + +namespace FileSys { + +// An implementation of VfsDirectory that maintains two vectors for subdirectories and files. +// Vector data is supplied upon construction. +struct VectorVfsDirectory : public VfsDirectory { + explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, + std::vector<VirtualDir> dirs = {}, VirtualDir parent = nullptr, + std::string name = ""); + + std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; + std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; + bool IsWritable() const override; + bool IsReadable() const override; + std::string GetName() const override; + std::shared_ptr<VfsDirectory> GetParentDirectory() const override; + bool DeleteSubdirectory(std::string_view name) override; + bool DeleteFile(std::string_view name) override; + bool Rename(std::string_view name) override; + std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override; + std::shared_ptr<VfsFile> CreateFile(std::string_view name) override; + + virtual void AddFile(VirtualFile file); + virtual void AddDirectory(VirtualDir dir); + +protected: + bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; + +private: + std::vector<VirtualFile> files; + std::vector<VirtualDir> dirs; + + VirtualDir parent; + std::string name; +}; + +} // namespace FileSys diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 5ca573652..332e5c3d0 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -37,45 +37,46 @@ #include "core/core.h" #include "core/core_cpu.h" #include "core/gdbstub/gdbstub.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/scheduler.h" #include "core/loader/loader.h" #include "core/memory.h" -const int GDB_BUFFER_SIZE = 10000; +namespace GDBStub { +namespace { +constexpr int GDB_BUFFER_SIZE = 10000; -const char GDB_STUB_START = '$'; -const char GDB_STUB_END = '#'; -const char GDB_STUB_ACK = '+'; -const char GDB_STUB_NACK = '-'; +constexpr char GDB_STUB_START = '$'; +constexpr char GDB_STUB_END = '#'; +constexpr char GDB_STUB_ACK = '+'; +constexpr char GDB_STUB_NACK = '-'; #ifndef SIGTRAP -const u32 SIGTRAP = 5; +constexpr u32 SIGTRAP = 5; #endif #ifndef SIGTERM -const u32 SIGTERM = 15; +constexpr u32 SIGTERM = 15; #endif #ifndef MSG_WAITALL -const u32 MSG_WAITALL = 8; +constexpr u32 MSG_WAITALL = 8; #endif -const u32 LR_REGISTER = 30; -const u32 SP_REGISTER = 31; -const u32 PC_REGISTER = 32; -const u32 CPSR_REGISTER = 33; -const u32 UC_ARM64_REG_Q0 = 34; -const u32 FPSCR_REGISTER = 66; +constexpr u32 LR_REGISTER = 30; +constexpr u32 SP_REGISTER = 31; +constexpr u32 PC_REGISTER = 32; +constexpr u32 CPSR_REGISTER = 33; +constexpr u32 UC_ARM64_REG_Q0 = 34; +constexpr u32 FPSCR_REGISTER = 66; // TODO/WiP - Used while working on support for FPU -const u32 TODO_DUMMY_REG_997 = 997; -const u32 TODO_DUMMY_REG_998 = 998; +constexpr u32 TODO_DUMMY_REG_997 = 997; +constexpr u32 TODO_DUMMY_REG_998 = 998; // For sample XML files see the GDB source /gdb/features // GDB also wants the l character at the start // This XML defines what the registers are for this specific ARM device -static const char* target_xml = +constexpr char target_xml[] = R"(l<?xml version="1.0"?> <!DOCTYPE target SYSTEM "gdb-target.dtd"> <target version="1.0"> @@ -141,30 +142,28 @@ static const char* target_xml = </target> )"; -namespace GDBStub { - -static int gdbserver_socket = -1; +int gdbserver_socket = -1; -static u8 command_buffer[GDB_BUFFER_SIZE]; -static u32 command_length; +u8 command_buffer[GDB_BUFFER_SIZE]; +u32 command_length; -static u32 latest_signal = 0; -static bool memory_break = false; +u32 latest_signal = 0; +bool memory_break = false; -static Kernel::Thread* current_thread = nullptr; -static u32 current_core = 0; +Kernel::Thread* current_thread = nullptr; +u32 current_core = 0; // Binding to a port within the reserved ports range (0-1023) requires root permissions, // so default to a port outside of that range. -static u16 gdbstub_port = 24689; +u16 gdbstub_port = 24689; -static bool halt_loop = true; -static bool step_loop = false; -static bool send_trap = false; +bool halt_loop = true; +bool step_loop = false; +bool send_trap = false; // If set to false, the server will never be started and no // gdbstub-related functions will be executed. -static std::atomic<bool> server_enabled(false); +std::atomic<bool> server_enabled(false); #ifdef _WIN32 WSADATA InitData; @@ -172,23 +171,26 @@ WSADATA InitData; struct Breakpoint { bool active; - PAddr addr; + VAddr addr; u64 len; + std::array<u8, 4> inst; }; -static std::map<u64, Breakpoint> breakpoints_execute; -static std::map<u64, Breakpoint> breakpoints_read; -static std::map<u64, Breakpoint> breakpoints_write; +using BreakpointMap = std::map<VAddr, Breakpoint>; +BreakpointMap breakpoints_execute; +BreakpointMap breakpoints_read; +BreakpointMap breakpoints_write; struct Module { std::string name; - PAddr beg; - PAddr end; + VAddr beg; + VAddr end; }; -static std::vector<Module> modules; +std::vector<Module> modules; +} // Anonymous namespace -void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext) { +void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) { Module module; if (add_elf_ext) { Common::SplitPath(name, nullptr, &module.name, nullptr); @@ -419,11 +421,11 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) { } /** - * Get the list of breakpoints for a given breakpoint type. + * Get the map of breakpoints for a given breakpoint type. * - * @param type Type of breakpoint list. + * @param type Type of breakpoint map. */ -static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { +static BreakpointMap& GetBreakpointMap(BreakpointType type) { switch (type) { case BreakpointType::Execute: return breakpoints_execute; @@ -442,20 +444,24 @@ static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { * @param type Type of breakpoint. * @param addr Address of breakpoint. */ -static void RemoveBreakpoint(BreakpointType type, PAddr addr) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); +static void RemoveBreakpoint(BreakpointType type, VAddr addr) { + BreakpointMap& p = GetBreakpointMap(type); - auto bp = p.find(static_cast<u64>(addr)); - if (bp != p.end()) { - LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", - bp->second.len, bp->second.addr, static_cast<int>(type)); - p.erase(static_cast<u64>(addr)); + const auto bp = p.find(addr); + if (bp == p.end()) { + return; } + + LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: {:016X} bytes at {:016X} of type {}", + bp->second.len, bp->second.addr, static_cast<int>(type)); + Memory::WriteBlock(bp->second.addr, bp->second.inst.data(), bp->second.inst.size()); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); + p.erase(addr); } -BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); - auto next_breakpoint = p.lower_bound(static_cast<u64>(addr)); +BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, BreakpointType type) { + const BreakpointMap& p = GetBreakpointMap(type); + const auto next_breakpoint = p.lower_bound(addr); BreakpointAddress breakpoint; if (next_breakpoint != p.end()) { @@ -469,36 +475,38 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) return breakpoint; } -bool CheckBreakpoint(PAddr addr, BreakpointType type) { +bool CheckBreakpoint(VAddr addr, BreakpointType type) { if (!IsConnected()) { return false; } - std::map<u64, Breakpoint>& p = GetBreakpointList(type); + const BreakpointMap& p = GetBreakpointMap(type); + const auto bp = p.find(addr); - auto bp = p.find(static_cast<u64>(addr)); - if (bp != p.end()) { - u64 len = bp->second.len; + if (bp == p.end()) { + return false; + } - // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints - // no matter if it's a 4-byte or 2-byte instruction. When you execute a - // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on - // two instructions instead of the single instruction you placed the breakpoint - // on. So, as a way to make sure that execution breakpoints are only breaking - // on the instruction that was specified, set the length of an execution - // breakpoint to 1. This should be fine since the CPU should never begin executing - // an instruction anywhere except the beginning of the instruction. - if (type == BreakpointType::Execute) { - len = 1; - } + u64 len = bp->second.len; - if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { - LOG_DEBUG(Debug_GDBStub, - "Found breakpoint type {} @ {:016X}, range: {:016X}" - " - {:016X} ({:X} bytes)", - static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); - return true; - } + // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints + // no matter if it's a 4-byte or 2-byte instruction. When you execute a + // Thumb instruction with a 4-byte breakpoint set, it will set a breakpoint on + // two instructions instead of the single instruction you placed the breakpoint + // on. So, as a way to make sure that execution breakpoints are only breaking + // on the instruction that was specified, set the length of an execution + // breakpoint to 1. This should be fine since the CPU should never begin executing + // an instruction anywhere except the beginning of the instruction. + if (type == BreakpointType::Execute) { + len = 1; + } + + if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) { + LOG_DEBUG(Debug_GDBStub, + "Found breakpoint type {} @ {:016X}, range: {:016X}" + " - {:016X} ({:X} bytes)", + static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len); + return true; } return false; @@ -932,6 +940,7 @@ static void WriteMemory() { GdbHexToMem(data.data(), len_pos + 1, len); Memory::WriteBlock(addr, data.data(), len); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); SendReply("OK"); } @@ -951,6 +960,7 @@ static void Step() { step_loop = true; halt_loop = true; send_trap = true; + Core::System::GetInstance().InvalidateCpuInstructionCaches(); } /// Tell the CPU if we hit a memory breakpoint. @@ -967,6 +977,7 @@ static void Continue() { memory_break = false; step_loop = false; halt_loop = false; + Core::System::GetInstance().InvalidateCpuInstructionCaches(); } /** @@ -976,13 +987,17 @@ static void Continue() { * @param addr Address of breakpoint. * @param len Length of breakpoint. */ -static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { - std::map<u64, Breakpoint>& p = GetBreakpointList(type); +static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { + BreakpointMap& p = GetBreakpointMap(type); Breakpoint breakpoint; breakpoint.active = true; breakpoint.addr = addr; breakpoint.len = len; + Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); + static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}}; + Memory::WriteBlock(addr, btrap.data(), btrap.size()); + Core::System::GetInstance().InvalidateCpuInstructionCaches(); p.insert({addr, breakpoint}); LOG_DEBUG(Debug_GDBStub, "gdb: added {} breakpoint: {:016X} bytes at {:016X}", @@ -1016,7 +1031,7 @@ static void AddBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); start_offset = addr_pos + 1; u64 len = @@ -1065,7 +1080,7 @@ static void RemoveBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); if (type == BreakpointType::Access) { // Access is made up of Read and Write types, so add both breakpoints diff --git a/src/core/gdbstub/gdbstub.h b/src/core/gdbstub/gdbstub.h index a6b50c26c..5a36524b2 100644 --- a/src/core/gdbstub/gdbstub.h +++ b/src/core/gdbstub/gdbstub.h @@ -22,7 +22,7 @@ enum class BreakpointType { }; struct BreakpointAddress { - PAddr address; + VAddr address; BreakpointType type; }; @@ -53,7 +53,7 @@ bool IsServerEnabled(); bool IsConnected(); /// Register module. -void RegisterModule(std::string name, PAddr beg, PAddr end, bool add_elf_ext = true); +void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext = true); /** * Signal to the gdbstub server that it should halt CPU execution. @@ -74,7 +74,7 @@ void HandlePacket(); * @param addr Address to search from. * @param type Type of breakpoint. */ -BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointType type); +BreakpointAddress GetNextBreakpointFromAddress(VAddr addr, GDBStub::BreakpointType type); /** * Check if a breakpoint of the specified type exists at the given address. @@ -82,7 +82,7 @@ BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, GDBStub::BreakpointTy * @param addr Address of breakpoint. * @param type Type of breakpoint. */ -bool CheckBreakpoint(PAddr addr, GDBStub::BreakpointType type); +bool CheckBreakpoint(VAddr addr, GDBStub::BreakpointType type); /// If set to true, the CPU will halt at the beginning of the next CPU loop. bool GetCpuHaltFlag(); diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 7fb0da408..d3a734831 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -5,15 +5,18 @@ #pragma once #include <array> +#include <cstring> +#include <memory> #include <tuple> #include <type_traits> #include <utility> +#include "common/assert.h" +#include "common/common_types.h" #include "core/hle/ipc.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" namespace IPC { diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 233fdab25..03a954a9f 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -2,15 +2,17 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <vector> + #include "common/assert.h" -#include "common/common_funcs.h" #include "common/common_types.h" #include "core/core.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" -#include "core/hle/lock.h" +#include "core/hle/result.h" #include "core/memory.h" namespace Kernel { @@ -30,9 +32,8 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) { } // Gets the threads waiting on an address. -static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_threads, - VAddr address) { - auto RetrieveWaitingThreads = +static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { + const auto RetrieveWaitingThreads = [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); auto& thread_list = scheduler->GetThreadList(); @@ -43,16 +44,20 @@ static void GetThreadsWaitingOnAddress(std::vector<SharedPtr<Thread>>& waiting_t } }; - // Retrieve a list of all threads that are waiting for this address. - RetrieveWaitingThreads(0, waiting_threads, address); - RetrieveWaitingThreads(1, waiting_threads, address); - RetrieveWaitingThreads(2, waiting_threads, address); - RetrieveWaitingThreads(3, waiting_threads, address); + // Retrieve all threads that are waiting for this address. + std::vector<SharedPtr<Thread>> threads; + RetrieveWaitingThreads(0, threads, address); + RetrieveWaitingThreads(1, threads, address); + RetrieveWaitingThreads(2, threads, address); + RetrieveWaitingThreads(3, threads, address); + // Sort them by priority, such that the highest priority ones come first. - std::sort(waiting_threads.begin(), waiting_threads.end(), + std::sort(threads.begin(), threads.end(), [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { return lhs->current_priority < rhs->current_priority; }); + + return threads; } // Wake up num_to_wake (or all) threads in a vector. @@ -74,9 +79,7 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num // Signals an address being waited on. ResultCode SignalToAddress(VAddr address, s32 num_to_wake) { - // Get threads waiting on the address. - std::vector<SharedPtr<Thread>> waiting_threads; - GetThreadsWaitingOnAddress(waiting_threads, address); + std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); WakeThreads(waiting_threads, num_to_wake); return RESULT_SUCCESS; @@ -108,12 +111,11 @@ ResultCode ModifyByWaitingCountAndSignalToAddressIfEqual(VAddr address, s32 valu } // Get threads waiting on the address. - std::vector<SharedPtr<Thread>> waiting_threads; - GetThreadsWaitingOnAddress(waiting_threads, address); + std::vector<SharedPtr<Thread>> waiting_threads = GetThreadsWaitingOnAddress(address); // Determine the modified value depending on the waiting count. s32 updated_value; - if (waiting_threads.size() == 0) { + if (waiting_threads.empty()) { updated_value = value - 1; } else if (num_to_wake <= 0 || waiting_threads.size() <= static_cast<u32>(num_to_wake)) { updated_value = value + 1; diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index f20f3dbc0..e3657b8e9 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -4,7 +4,9 @@ #pragma once -#include "core/hle/result.h" +#include "common/common_types.h" + +union ResultCode; namespace Kernel { diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index fb2b6f7a3..134e41ebc 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -2,19 +2,20 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/assert.h" +#include <tuple> + #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" namespace Kernel { -ClientPort::ClientPort() {} -ClientPort::~ClientPort() {} +ClientPort::ClientPort() = default; +ClientPort::~ClientPort() = default; ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { // Note: Threads do not wait for the server endpoint to call @@ -39,4 +40,12 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); } +void ClientPort::ConnectionClosed() { + if (active_sessions == 0) { + return; + } + + --active_sessions; +} + } // namespace Kernel diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index a829aeb6d..b1269ea5c 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -6,7 +6,7 @@ #include <string> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/result.h" namespace Kernel { @@ -37,14 +37,20 @@ public: */ ResultVal<SharedPtr<ClientSession>> Connect(); - SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port. - u32 max_sessions; ///< Maximum number of simultaneous sessions the port can have - u32 active_sessions; ///< Number of currently open sessions to this port - std::string name; ///< Name of client port (optional) + /** + * Signifies that a previously active connection has been closed, + * decreasing the total number of active connections to this port. + */ + void ConnectionClosed(); private: ClientPort(); ~ClientPort() override; + + SharedPtr<ServerPort> server_port; ///< ServerPort associated with this client port. + u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have + u32 active_sessions = 0; ///< Number of currently open sessions to this port + std::string name; ///< Name of client port (optional) }; } // namespace Kernel diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 72773d8b1..fdffc648d 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/assert.h" - #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/hle_ipc.h" diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 2258f95bc..dabd93ed7 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -7,7 +7,7 @@ #include <memory> #include <string> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 9cae2369f..5623c4b6a 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -3,11 +3,9 @@ // Refer to the license.txt file included. #include <algorithm> -#include <map> -#include <vector> #include "common/assert.h" #include "core/hle/kernel/event.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" namespace Kernel { diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index e5c924a75..3c20c05e8 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -5,7 +5,7 @@ #pragma once #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" namespace Kernel { @@ -31,10 +31,9 @@ public: return HANDLE_TYPE; } - ResetType reset_type; ///< Current ResetType - - bool signaled; ///< Whether the event has already been signaled - std::string name; ///< Name of event (optional) + ResetType GetResetType() const { + return reset_type; + } bool ShouldWait(Thread* thread) const override; void Acquire(Thread* thread) override; @@ -47,6 +46,11 @@ public: private: Event(); ~Event() override; + + ResetType reset_type; ///< Current ResetType + + bool signaled; ///< Whether the event has already been signaled + std::string name; ///< Name of event (optional) }; } // namespace Kernel diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 7dd67f80f..28e21428a 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -8,7 +8,6 @@ #include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index ba968c666..22ddda630 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h @@ -7,7 +7,7 @@ #include <array> #include <cstddef> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index f24392520..5dd1b68d7 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -2,17 +2,22 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <array> +#include <sstream> #include <utility> #include <boost/range/algorithm_ext/erase.hpp> + #include "common/assert.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_session.h" #include "core/memory.h" diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 84727f748..9ce52db24 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -5,7 +5,6 @@ #pragma once #include <array> -#include <iterator> #include <memory> #include <string> #include <type_traits> @@ -14,7 +13,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/hle/ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1beb98566..1b0cd0abf 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -4,8 +4,6 @@ #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory.h" -#include "core/hle/kernel/object_address_table.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" @@ -16,9 +14,7 @@ namespace Kernel { unsigned int Object::next_object_id; /// Initialize the kernel -void Init(u32 system_mode) { - Kernel::MemoryInit(system_mode); - +void Init() { Kernel::ResourceLimitsInit(); Kernel::ThreadingInit(); Kernel::TimersInit(); @@ -33,13 +29,11 @@ void Init(u32 system_mode) { void Shutdown() { // Free all kernel objects g_handle_table.Clear(); - g_object_address_table.Clear(); Kernel::ThreadingShutdown(); Kernel::TimersShutdown(); Kernel::ResourceLimitsShutdown(); - Kernel::MemoryShutdown(); } } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 402ae900f..131311472 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,122 +4,12 @@ #pragma once -#include <cstddef> -#include <string> -#include <utility> -#include <boost/smart_ptr/intrusive_ptr.hpp> -#include "common/assert.h" #include "common/common_types.h" namespace Kernel { -using Handle = u32; - -enum class HandleType : u32 { - Unknown, - Event, - SharedMemory, - Thread, - Process, - AddressArbiter, - Timer, - ResourceLimit, - CodeSet, - ClientPort, - ServerPort, - ClientSession, - ServerSession, -}; - -enum class ResetType { - OneShot, - Sticky, - Pulse, -}; - -class Object : NonCopyable { -public: - virtual ~Object() {} - - /// Returns a unique identifier for the object. For debugging purposes only. - unsigned int GetObjectId() const { - return object_id; - } - - virtual std::string GetTypeName() const { - return "[BAD KERNEL OBJECT TYPE]"; - } - virtual std::string GetName() const { - return "[UNKNOWN KERNEL OBJECT]"; - } - virtual Kernel::HandleType GetHandleType() const = 0; - - /** - * Check if a thread can wait on the object - * @return True if a thread can wait on the object, otherwise false - */ - bool IsWaitable() const { - switch (GetHandleType()) { - case HandleType::Event: - case HandleType::Thread: - case HandleType::Timer: - case HandleType::ServerPort: - case HandleType::ServerSession: - return true; - - case HandleType::Unknown: - case HandleType::SharedMemory: - case HandleType::Process: - case HandleType::AddressArbiter: - case HandleType::ResourceLimit: - case HandleType::CodeSet: - case HandleType::ClientPort: - case HandleType::ClientSession: - return false; - } - - UNREACHABLE(); - } - -public: - static unsigned int next_object_id; - -private: - friend void intrusive_ptr_add_ref(Object*); - friend void intrusive_ptr_release(Object*); - - unsigned int ref_count = 0; - unsigned int object_id = next_object_id++; -}; - -// Special functions used by boost::instrusive_ptr to do automatic ref-counting -inline void intrusive_ptr_add_ref(Object* object) { - ++object->ref_count; -} - -inline void intrusive_ptr_release(Object* object) { - if (--object->ref_count == 0) { - delete object; - } -} - -template <typename T> -using SharedPtr = boost::intrusive_ptr<T>; - -/** - * Attempts to downcast the given Object pointer to a pointer to T. - * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. - */ -template <typename T> -inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { - if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { - return boost::static_pointer_cast<T>(std::move(object)); - } - return nullptr; -} - /// Initialize the kernel with the specified system mode. -void Init(u32 system_mode); +void Init(); /// Shutdown the kernel void Shutdown(); diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp deleted file mode 100644 index 94eac677c..000000000 --- a/src/core/hle/kernel/memory.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <cinttypes> -#include <map> -#include <memory> -#include <utility> -#include <vector> -#include "common/assert.h" -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hle/kernel/memory.h" -#include "core/hle/kernel/vm_manager.h" -#include "core/hle/result.h" -#include "core/memory.h" -#include "core/memory_setup.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -namespace Kernel { - -MemoryRegionInfo memory_regions[3]; - -/// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system -/// memory configuration type. -static const u32 memory_region_sizes[8][3] = { - // Old 3DS layouts - {0x04000000, 0x02C00000, 0x01400000}, // 0 - {/* This appears to be unused. */}, // 1 - {0x06000000, 0x00C00000, 0x01400000}, // 2 - {0x05000000, 0x01C00000, 0x01400000}, // 3 - {0x04800000, 0x02400000, 0x01400000}, // 4 - {0x02000000, 0x04C00000, 0x01400000}, // 5 - - // New 3DS layouts - {0x07C00000, 0x06400000, 0x02000000}, // 6 - {0x0B200000, 0x02E00000, 0x02000000}, // 7 -}; - -void MemoryInit(u32 mem_type) { - // TODO(yuriks): On the n3DS, all o3DS configurations (<=5) are forced to 6 instead. - ASSERT_MSG(mem_type <= 5, "New 3DS memory configuration aren't supported yet!"); - ASSERT(mem_type != 1); - - // The kernel allocation regions (APPLICATION, SYSTEM and BASE) are laid out in sequence, with - // the sizes specified in the memory_region_sizes table. - VAddr base = 0; - for (int i = 0; i < 3; ++i) { - memory_regions[i].base = base; - memory_regions[i].size = memory_region_sizes[mem_type][i]; - memory_regions[i].used = 0; - memory_regions[i].linear_heap_memory = std::make_shared<std::vector<u8>>(); - // Reserve enough space for this region of FCRAM. - // We do not want this block of memory to be relocated when allocating from it. - memory_regions[i].linear_heap_memory->reserve(memory_regions[i].size); - - base += memory_regions[i].size; - } - - // We must've allocated the entire FCRAM by the end - ASSERT(base == Memory::FCRAM_SIZE); -} - -void MemoryShutdown() { - for (auto& region : memory_regions) { - region.base = 0; - region.size = 0; - region.used = 0; - region.linear_heap_memory = nullptr; - } -} - -MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { - switch (region) { - case MemoryRegion::APPLICATION: - return &memory_regions[0]; - case MemoryRegion::SYSTEM: - return &memory_regions[1]; - case MemoryRegion::BASE: - return &memory_regions[2]; - default: - UNREACHABLE(); - } -} - -void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) {} - -void MapSharedPages(VMManager& address_space) {} - -} // namespace Kernel diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h deleted file mode 100644 index 61e30c679..000000000 --- a/src/core/hle/kernel/memory.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 Citra 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" -#include "core/hle/kernel/process.h" - -namespace Kernel { - -class VMManager; - -struct MemoryRegionInfo { - u64 base; // Not an address, but offset from start of FCRAM - u64 size; - u64 used; - - std::shared_ptr<std::vector<u8>> linear_heap_memory; -}; - -void MemoryInit(u32 mem_type); -void MemoryShutdown(); -MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); - -void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); -void MapSharedPages(VMManager& address_space); - -extern MemoryRegionInfo memory_regions[3]; -} // namespace Kernel diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index feb7b88d2..cb7f58b35 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -3,16 +3,19 @@ // Refer to the license.txt file included. #include <map> +#include <utility> #include <vector> + #include <boost/range/algorithm_ext/erase.hpp> + #include "common/assert.h" #include "core/core.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" -#include "core/hle/kernel/object_address_table.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 3117e7c70..45268bbe9 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -4,12 +4,10 @@ #pragma once -#include <string> #include "common/common_types.h" -#include "common/swap.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/wait_object.h" -#include "core/hle/result.h" +#include "core/hle/kernel/object.h" + +union ResultCode; namespace Kernel { diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp new file mode 100644 index 000000000..cdba272f5 --- /dev/null +++ b/src/core/hle/kernel/object.cpp @@ -0,0 +1,35 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "core/hle/kernel/object.h" + +namespace Kernel { + +Object::~Object() = default; + +bool Object::IsWaitable() const { + switch (GetHandleType()) { + case HandleType::Event: + case HandleType::Thread: + case HandleType::Timer: + case HandleType::ServerPort: + case HandleType::ServerSession: + return true; + + case HandleType::Unknown: + case HandleType::SharedMemory: + case HandleType::Process: + case HandleType::AddressArbiter: + case HandleType::ResourceLimit: + case HandleType::CodeSet: + case HandleType::ClientPort: + case HandleType::ClientSession: + return false; + } + + UNREACHABLE(); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h new file mode 100644 index 000000000..83df68dfd --- /dev/null +++ b/src/core/hle/kernel/object.h @@ -0,0 +1,100 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <utility> + +#include <boost/smart_ptr/intrusive_ptr.hpp> + +#include "common/common_types.h" + +namespace Kernel { + +using Handle = u32; + +enum class HandleType : u32 { + Unknown, + Event, + SharedMemory, + Thread, + Process, + AddressArbiter, + Timer, + ResourceLimit, + CodeSet, + ClientPort, + ServerPort, + ClientSession, + ServerSession, +}; + +enum class ResetType { + OneShot, + Sticky, + Pulse, +}; + +class Object : NonCopyable { +public: + virtual ~Object(); + + /// Returns a unique identifier for the object. For debugging purposes only. + unsigned int GetObjectId() const { + return object_id; + } + + virtual std::string GetTypeName() const { + return "[BAD KERNEL OBJECT TYPE]"; + } + virtual std::string GetName() const { + return "[UNKNOWN KERNEL OBJECT]"; + } + virtual HandleType GetHandleType() const = 0; + + /** + * Check if a thread can wait on the object + * @return True if a thread can wait on the object, otherwise false + */ + bool IsWaitable() const; + +public: + static unsigned int next_object_id; + +private: + friend void intrusive_ptr_add_ref(Object*); + friend void intrusive_ptr_release(Object*); + + unsigned int ref_count = 0; + unsigned int object_id = next_object_id++; +}; + +// Special functions used by boost::instrusive_ptr to do automatic ref-counting +inline void intrusive_ptr_add_ref(Object* object) { + ++object->ref_count; +} + +inline void intrusive_ptr_release(Object* object) { + if (--object->ref_count == 0) { + delete object; + } +} + +template <typename T> +using SharedPtr = boost::intrusive_ptr<T>; + +/** + * Attempts to downcast the given Object pointer to a pointer to T. + * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. + */ +template <typename T> +inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { + if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { + return boost::static_pointer_cast<T>(std::move(object)); + } + return nullptr; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/object_address_table.cpp b/src/core/hle/kernel/object_address_table.cpp deleted file mode 100644 index ca8a833a1..000000000 --- a/src/core/hle/kernel/object_address_table.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <utility> - -#include "common/assert.h" -#include "core/hle/kernel/object_address_table.h" - -namespace Kernel { - -ObjectAddressTable g_object_address_table; - -void ObjectAddressTable::Insert(VAddr addr, SharedPtr<Object> obj) { - ASSERT_MSG(objects.find(addr) == objects.end(), "Object already exists with addr=0x{:X}", addr); - objects[addr] = std::move(obj); -} - -void ObjectAddressTable::Close(VAddr addr) { - ASSERT_MSG(objects.find(addr) != objects.end(), "Object does not exist with addr=0x{:X}", addr); - objects.erase(addr); -} - -SharedPtr<Object> ObjectAddressTable::GetGeneric(VAddr addr) const { - auto iter = objects.find(addr); - if (iter != objects.end()) { - return iter->second; - } - return {}; -} - -void ObjectAddressTable::Clear() { - objects.clear(); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/object_address_table.h b/src/core/hle/kernel/object_address_table.h deleted file mode 100644 index a09004b32..000000000 --- a/src/core/hle/kernel/object_address_table.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <map> -#include "common/common_types.h" -#include "core/hle/kernel/kernel.h" - -namespace Kernel { - -/** - * This class is used to keep a table of Kernel objects and their respective addresses in emulated - * memory. For certain Switch SVCs, Kernel objects are referenced by an address to an object the - * guest application manages, so we use this table to look these kernel objects up. This is similiar - * to the HandleTable class. - */ -class ObjectAddressTable final : NonCopyable { -public: - ObjectAddressTable() = default; - - /** - * Inserts an object and address pair into the table. - */ - void Insert(VAddr addr, SharedPtr<Object> obj); - - /** - * Closes an object by its address, removing it from the table and decreasing the object's - * ref-count. - * @return `RESULT_SUCCESS` or one of the following errors: - * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. - */ - void Close(VAddr addr); - - /** - * Looks up an object by its address. - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. - */ - SharedPtr<Object> GetGeneric(VAddr addr) const; - - /** - * Looks up an object by its address while verifying its type. - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its - * type differs from the requested one. - */ - template <class T> - SharedPtr<T> Get(VAddr addr) const { - return DynamicObjectCast<T>(GetGeneric(addr)); - } - - /// Closes all addresses held in this table. - void Clear(); - -private: - /// Stores the Object referenced by the address - std::map<VAddr, SharedPtr<Object>> objects; -}; - -extern ObjectAddressTable g_object_address_table; - -} // namespace Kernel diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 0c0506085..edf34c5a3 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -8,7 +8,6 @@ #include "common/common_funcs.h" #include "common/logging/log.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" @@ -125,14 +124,6 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Mapped) .Unwrap(); - misc_memory_used += stack_size; - memory_region->used += stack_size; - - // Map special address mappings - MapSharedPages(vm_manager); - for (const auto& mapping : address_mappings) { - HandleSpecialMapping(vm_manager, mapping); - } vm_manager.LogLayout(); status = ProcessStatus::Running; @@ -141,37 +132,19 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { } void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { - memory_region = GetMemoryRegion(flags.memory_region); - - auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, - MemoryState memory_state) { + const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions, + MemoryState memory_state) { auto vma = vm_manager .MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset, segment.size, memory_state) .Unwrap(); vm_manager.Reprotect(vma, permissions); - misc_memory_used += segment.size; - memory_region->used += segment.size; }; // Map CodeSet segments - MapSegment(module_->code, VMAPermission::ReadExecute, MemoryState::CodeStatic); - MapSegment(module_->rodata, VMAPermission::Read, MemoryState::CodeMutable); - MapSegment(module_->data, VMAPermission::ReadWrite, MemoryState::CodeMutable); -} - -VAddr Process::GetLinearHeapAreaAddress() const { - // Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of - // the extra RAM in the n3DS. - return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; -} - -VAddr Process::GetLinearHeapBase() const { - return GetLinearHeapAreaAddress() + memory_region->base; -} - -VAddr Process::GetLinearHeapLimit() const { - return GetLinearHeapBase() + memory_region->size; + MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); + MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); + MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); } ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { @@ -206,7 +179,6 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per vm_manager.Reprotect(vma, perms); heap_used = size; - memory_region->used += size; return MakeResult<VAddr>(heap_end - size); } @@ -226,52 +198,6 @@ ResultCode Process::HeapFree(VAddr target, u32 size) { return result; heap_used -= size; - memory_region->used -= size; - - return RESULT_SUCCESS; -} - -ResultVal<VAddr> Process::LinearAllocate(VAddr target, u32 size, VMAPermission perms) { - UNIMPLEMENTED(); - return {}; -} - -ResultCode Process::LinearFree(VAddr target, u32 size) { - auto& linheap_memory = memory_region->linear_heap_memory; - - if (target < GetLinearHeapBase() || target + size > GetLinearHeapLimit() || - target + size < target) { - - return ERR_INVALID_ADDRESS; - } - - if (size == 0) { - return RESULT_SUCCESS; - } - - VAddr heap_end = GetLinearHeapBase() + (u32)linheap_memory->size(); - if (target + size > heap_end) { - return ERR_INVALID_ADDRESS_STATE; - } - - ResultCode result = vm_manager.UnmapRange(target, size); - if (result.IsError()) - return result; - - linear_heap_used -= size; - memory_region->used -= size; - - if (target + size == heap_end) { - // End of linear heap has been freed, so check what's the last allocated block in it and - // reduce the size. - auto vma = vm_manager.FindVMA(target); - ASSERT(vma != vm_manager.vma_map.end()); - ASSERT(vma->second.type == VMAType::Free); - VAddr new_end = vma->second.base; - if (new_end >= GetLinearHeapBase()) { - linheap_memory->resize(new_end - GetLinearHeapBase()); - } - } return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 68e77a4d1..992689186 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -4,6 +4,7 @@ #pragma once +#include <array> #include <bitset> #include <cstddef> #include <memory> @@ -12,7 +13,7 @@ #include <boost/container/static_vector.hpp> #include "common/bit_field.h" #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" @@ -53,9 +54,14 @@ union ProcessFlags { enum class ProcessStatus { Created, Running, Exited }; class ResourceLimit; -struct MemoryRegionInfo; struct CodeSet final : public Object { + struct Segment { + size_t offset = 0; + VAddr addr = 0; + u32 size = 0; + }; + static SharedPtr<CodeSet> Create(std::string name); std::string GetTypeName() const override { @@ -70,24 +76,38 @@ struct CodeSet final : public Object { return HANDLE_TYPE; } - /// Name of the process - std::string name; + Segment& CodeSegment() { + return segments[0]; + } - std::shared_ptr<std::vector<u8>> memory; + const Segment& CodeSegment() const { + return segments[0]; + } - struct Segment { - size_t offset = 0; - VAddr addr = 0; - u32 size = 0; - }; + Segment& RODataSegment() { + return segments[1]; + } + + const Segment& RODataSegment() const { + return segments[1]; + } + + Segment& DataSegment() { + return segments[2]; + } + + const Segment& DataSegment() const { + return segments[2]; + } - Segment segments[3]; - Segment& code = segments[0]; - Segment& rodata = segments[1]; - Segment& data = segments[2]; + std::shared_ptr<std::vector<u8>> memory; + std::array<Segment, 3> segments; VAddr entrypoint; + /// Name of the process + std::string name; + private: CodeSet(); ~CodeSet() override; @@ -163,12 +183,11 @@ public: // This makes deallocation and reallocation of holes fast and keeps process memory contiguous // in the emulator address space, allowing Memory::GetPointer to be reasonably safe. std::shared_ptr<std::vector<u8>> heap_memory; - // The left/right bounds of the address space covered by heap_memory. - VAddr heap_start = 0, heap_end = 0; - u64 heap_used = 0, linear_heap_used = 0, misc_memory_used = 0; - - MemoryRegionInfo* memory_region = nullptr; + // The left/right bounds of the address space covered by heap_memory. + VAddr heap_start = 0; + VAddr heap_end = 0; + u64 heap_used = 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 @@ -179,16 +198,9 @@ public: std::string name; - VAddr GetLinearHeapAreaAddress() const; - VAddr GetLinearHeapBase() const; - VAddr GetLinearHeapLimit() const; - ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); ResultCode HeapFree(VAddr target, u32 size); - ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms); - ResultCode LinearFree(VAddr target, u32 size); - ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index cc689a27a..0fa141db3 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -5,7 +5,7 @@ #pragma once #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" namespace Kernel { diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index e307eec98..94065c736 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp @@ -2,8 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <utility> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/arm/arm_interface.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/process.h" diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index a3b5fb8ca..1a4ee8f36 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h @@ -8,9 +8,11 @@ #include <vector> #include "common/common_types.h" #include "common/thread_queue_list.h" -#include "core/arm/arm_interface.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" +class ARM_Interface; + namespace Kernel { class Scheduler final { diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 0b7061403..7b6211fd8 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -6,7 +6,7 @@ #include "common/assert.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/thread.h" diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 9ef4ecc35..7f6d6b3eb 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -7,8 +7,9 @@ #include <memory> #include <string> #include <tuple> +#include <vector> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 29b163528..93560152f 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -5,6 +5,8 @@ #include <tuple> #include <utility> +#include "common/assert.h" +#include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" @@ -25,7 +27,7 @@ ServerSession::~ServerSession() { // Decrease the port's connection count. if (parent->port) - parent->port->active_sessions--; + parent->port->ConnectionClosed(); // TODO(Subv): Wake up all the ClientSession's waiting threads and set // the SendSyncRequest result to 0xC920181A. diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 2da807042..2bce54fee 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -6,12 +6,12 @@ #include <memory> #include <string> -#include "common/assert.h" +#include <vector> + #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" -#include "core/memory.h" namespace Kernel { diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index e69b034a7..7a551f5e4 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -4,7 +4,7 @@ #pragma once -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" namespace Kernel { diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 4bf11c7e2..21ddc2f7d 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -3,10 +3,11 @@ // Refer to the license.txt file included. #include <utility> + +#include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/errors.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/shared_memory.h" #include "core/memory.h" @@ -28,35 +29,17 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u shared_memory->other_permissions = other_permissions; if (address == 0) { - // We need to allocate a block from the Linear Heap ourselves. - // We'll manually allocate some memory from the linear heap in the specified region. - MemoryRegionInfo* memory_region = GetMemoryRegion(region); - auto& linheap_memory = memory_region->linear_heap_memory; - - ASSERT_MSG(linheap_memory->size() + size <= memory_region->size, - "Not enough space in region to allocate shared memory!"); - - shared_memory->backing_block = linheap_memory; - shared_memory->backing_block_offset = linheap_memory->size(); - // Allocate some memory from the end of the linear heap for this region. - linheap_memory->insert(linheap_memory->end(), size, 0); - memory_region->used += size; - - shared_memory->linear_heap_phys_address = - Memory::FCRAM_PADDR + memory_region->base + - static_cast<PAddr>(shared_memory->backing_block_offset); - - // Increase the amount of used linear heap memory for the owner process. - if (shared_memory->owner_process != nullptr) { - shared_memory->owner_process->linear_heap_used += size; - } + shared_memory->backing_block = std::make_shared<std::vector<u8>>(size); + shared_memory->backing_block_offset = 0; // Refresh the address mappings for the current process. if (Core::CurrentProcess() != nullptr) { - Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); + Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings( + shared_memory->backing_block.get()); } } else { auto& vm_manager = shared_memory->owner_process->vm_manager; + // The memory is already available and mapped in the owner process. auto vma = vm_manager.FindVMA(address); ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); @@ -72,6 +55,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(SharedPtr<Process> owner_process, u } shared_memory->base_address = address; + return shared_memory; } @@ -122,11 +106,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi VAddr target_address = address; - if (base_address == 0 && target_address == 0) { - // Calculate the address at which to map the memory block. - target_address = Memory::PhysicalToVirtualAddress(linear_heap_phys_address).value(); - } - // Map the memory block into the target process auto result = target_process->vm_manager.MapMemoryBlock( target_address, backing_block, backing_block_offset, size, MemoryState::Shared); diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 86f818e90..c50fee615 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -4,9 +4,12 @@ #pragma once +#include <memory> #include <string> +#include <vector> + #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/result.h" @@ -108,9 +111,6 @@ public: SharedPtr<Process> owner_process; /// Address of shared memory block in the owner process if specified. VAddr base_address; - /// Physical address of the shared memory block in the linear heap if no address was specified - /// during creation. - PAddr linear_heap_phys_address; /// Backing memory for this shared memory block. std::shared_ptr<std::vector<u8>> backing_block; /// Offset into the backing block for this shared memory. diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 0b439401a..5db2db687 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -5,7 +5,10 @@ #include <algorithm> #include <cinttypes> #include <iterator> +#include <mutex> +#include <vector> +#include "common/assert.h" #include "common/logging/log.h" #include "common/microprofile.h" #include "common/string_util.h" @@ -17,7 +20,6 @@ #include "core/hle/kernel/event.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/mutex.h" -#include "core/hle/kernel/object_address_table.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/shared_memory.h" @@ -265,7 +267,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 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); - auto& vm_manager = Core::CurrentProcess()->vm_manager; + const auto& vm_manager = Core::CurrentProcess()->vm_manager; switch (static_cast<GetInfoType>(info_id)) { case GetInfoType::AllowedCpuIdBitmask: diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 94735c86e..b9022feae 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,8 +4,11 @@ #include <algorithm> #include <cinttypes> -#include <list> #include <vector> + +#include <boost/optional.hpp> +#include <boost/range/algorithm_ext/erase.hpp> + #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -17,9 +20,7 @@ #include "core/core_timing_util.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory.h" -#include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" #include "core/hle/result.h" @@ -79,8 +80,8 @@ void Thread::Stop() { wait_objects.clear(); // Mark the TLS slot in the thread's page as free. - u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; - u64 tls_slot = + const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; + const u64 tls_slot = ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot); } @@ -250,13 +251,14 @@ void Thread::ResumeFromWait() { * slot: The index of the first free slot in the indicated page. * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). */ -std::tuple<u32, u32, bool> GetFreeThreadLocalSlot(std::vector<std::bitset<8>>& tls_slots) { +static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot( + const std::vector<std::bitset<8>>& tls_slots) { // Iterate over all the allocated pages, and try to find one where not all slots are used. - for (unsigned page = 0; page < tls_slots.size(); ++page) { + for (std::size_t page = 0; page < tls_slots.size(); ++page) { const auto& page_tls_slots = tls_slots[page]; if (!page_tls_slots.all()) { // We found a page with at least one free slot, find which slot it is - for (unsigned slot = 0; slot < page_tls_slots.size(); ++slot) { + for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) { if (!page_tls_slots.test(slot)) { return std::make_tuple(page, slot, false); } @@ -331,42 +333,22 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, // Find the next available TLS index, and mark it as used auto& tls_slots = owner_process->tls_slots; - bool needs_allocation = true; - u32 available_page; // Which allocated page has free space - u32 available_slot; // Which slot within the page is free - - std::tie(available_page, available_slot, needs_allocation) = GetFreeThreadLocalSlot(tls_slots); + auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots); if (needs_allocation) { - // There are no already-allocated pages with free slots, lets allocate a new one. - // TLS pages are allocated from the BASE region in the linear heap. - MemoryRegionInfo* memory_region = GetMemoryRegion(MemoryRegion::BASE); - auto& linheap_memory = memory_region->linear_heap_memory; - - if (linheap_memory->size() + Memory::PAGE_SIZE > memory_region->size) { - LOG_ERROR(Kernel_SVC, - "Not enough space in region to allocate a new TLS page for thread"); - return ERR_OUT_OF_MEMORY; - } - - size_t offset = linheap_memory->size(); - - // Allocate some memory from the end of the linear heap for this region. - linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); - memory_region->used += Memory::PAGE_SIZE; - owner_process->linear_heap_used += Memory::PAGE_SIZE; - tls_slots.emplace_back(0); // The page is completely available at the start - available_page = static_cast<u32>(tls_slots.size() - 1); + available_page = tls_slots.size() - 1; available_slot = 0; // Use the first slot in the new page + // Allocate some memory from the end of the linear heap for this region. + const size_t offset = thread->tls_memory->size(); + thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0); + auto& vm_manager = owner_process->vm_manager; - vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); + vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get()); - // Map the page to the current process' address space. - // TODO(Subv): Find the correct MemoryState for this region. vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, - linheap_memory, offset, Memory::PAGE_SIZE, + thread->tls_memory, 0, Memory::PAGE_SIZE, MemoryState::ThreadLocal); } diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 6218960d2..adc804248 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -4,15 +4,14 @@ #pragma once +#include <functional> #include <memory> #include <string> -#include <unordered_map> #include <vector> -#include <boost/container/flat_map.hpp> -#include <boost/container/flat_set.hpp> + #include "common/common_types.h" #include "core/arm/arm_interface.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" @@ -266,6 +265,8 @@ public: private: Thread(); ~Thread() override; + + std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>(); }; /** @@ -289,12 +290,6 @@ Thread* GetCurrentThread(); void WaitCurrentThread_Sleep(); /** - * Waits the current thread from an ArbitrateAddress call - * @param wait_address Arbitration address used to resume from wait - */ -void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); - -/** * Stops the current thread and removes it from the thread_list */ void ExitCurrentThread(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 904a3d0a5..282360745 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -8,7 +8,7 @@ #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index c63f0ed90..4dddc67e0 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -5,7 +5,7 @@ #pragma once #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 9d26fd781..479cacb62 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <iterator> #include <utility> #include "common/assert.h" @@ -175,9 +176,9 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { ResultCode VMManager::UnmapRange(VAddr target, u64 size) { CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); - VAddr target_end = target + size; + const VAddr target_end = target + size; - VMAIter end = vma_map.end(); + const VMAIter end = vma_map.end(); // The comparison against the end of the range must be done using addresses since VMAs can be // merged during this process, causing invalidation of the iterators. while (vma != end && vma->second.base < target_end) { @@ -207,9 +208,9 @@ VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission ne ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) { CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); - VAddr target_end = target + size; + const VAddr target_end = target + size; - VMAIter end = vma_map.end(); + const VMAIter end = vma_map.end(); // The comparison against the end of the range must be done using addresses since VMAs can be // merged during this process, causing invalidation of the iterators. while (vma != end && vma->second.base < target_end) { @@ -258,14 +259,14 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) { return ERR_INVALID_ADDRESS; } - VirtualMemoryArea& vma = vma_handle->second; + const VirtualMemoryArea& vma = vma_handle->second; if (vma.type != VMAType::Free) { // Region is already allocated return ERR_INVALID_ADDRESS_STATE; } - u64 start_in_vma = base - vma.base; - u64 end_in_vma = start_in_vma + size; + const VAddr start_in_vma = base - vma.base; + const VAddr end_in_vma = start_in_vma + size; if (end_in_vma > vma.size) { // Requested allocation doesn't fit inside VMA @@ -288,17 +289,16 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) { ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size); ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", target); - VAddr target_end = target + size; + const VAddr target_end = target + size; ASSERT(target_end >= target); ASSERT(target_end <= MAX_ADDRESS); ASSERT(size > 0); VMAIter begin_vma = StripIterConstness(FindVMA(target)); - VMAIter i_end = vma_map.lower_bound(target_end); - for (auto i = begin_vma; i != i_end; ++i) { - if (i->second.type == VMAType::Free) { - return ERR_INVALID_ADDRESS_STATE; - } + const VMAIter i_end = vma_map.lower_bound(target_end); + if (std::any_of(begin_vma, i_end, + [](const auto& entry) { return entry.second.type == VMAType::Free; })) { + return ERR_INVALID_ADDRESS_STATE; } if (target != begin_vma->second.base) { @@ -346,7 +346,7 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) { } VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { - VMAIter next_vma = std::next(iter); + const VMAIter next_vma = std::next(iter); if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) { iter->second.size += next_vma->second.size; vma_map.erase(next_vma); @@ -382,22 +382,22 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { } } -u64 VMManager::GetTotalMemoryUsage() { +u64 VMManager::GetTotalMemoryUsage() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0xF8000000; } -u64 VMManager::GetTotalHeapUsage() { +u64 VMManager::GetTotalHeapUsage() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0x0; } -VAddr VMManager::GetAddressSpaceBaseAddr() { +VAddr VMManager::GetAddressSpaceBaseAddr() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0x8000000; } -u64 VMManager::GetAddressSpaceSize() { +u64 VMManager::GetAddressSpaceSize() const { LOG_WARNING(Kernel, "(STUBBED) called"); return MAX_ADDRESS; } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 38e4ebcd3..98bd04bea 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -190,16 +190,16 @@ public: void LogLayout() const; /// Gets the total memory usage, used by svcGetInfo - u64 GetTotalMemoryUsage(); + u64 GetTotalMemoryUsage() const; /// Gets the total heap usage, used by svcGetInfo - u64 GetTotalHeapUsage(); + u64 GetTotalHeapUsage() const; /// Gets the total address space base address, used by svcGetInfo - VAddr GetAddressSpaceBaseAddr(); + VAddr GetAddressSpaceBaseAddr() const; /// Gets the total address space address size, used by svcGetInfo - u64 GetAddressSpaceSize(); + u64 GetAddressSpaceSize() const; /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index 23af346d0..7681cdee7 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -5,11 +5,8 @@ #include <algorithm> #include "common/assert.h" #include "common/logging/log.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory.h" +#include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h index 78bfd8c6c..b5fbc647b 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/wait_object.h @@ -7,7 +7,7 @@ #include <vector> #include <boost/smart_ptr/intrusive_ptr.hpp> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" namespace Kernel { diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 0b158e015..6d15b46ed 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -10,6 +10,7 @@ #include "core/hle/service/acc/acc_su.h" #include "core/hle/service/acc/acc_u0.h" #include "core/hle/service/acc/acc_u1.h" +#include "core/settings.h" namespace Service::Account { @@ -31,13 +32,14 @@ struct ProfileBase { }; static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase structure has incorrect size"); +// TODO(ogniK): Generate a real user id based on username, md5(username) maybe? static constexpr u128 DEFAULT_USER_ID{1ull, 0ull}; class IProfile final : public ServiceFramework<IProfile> { public: explicit IProfile(u128 user_id) : ServiceFramework("IProfile"), user_id(user_id) { static const FunctionInfo functions[] = { - {0, nullptr, "Get"}, + {0, &IProfile::Get, "Get"}, {1, &IProfile::GetBase, "GetBase"}, {10, nullptr, "GetImageSize"}, {11, nullptr, "LoadImage"}, @@ -46,14 +48,36 @@ public: } private: + void Get(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_ACC, "(STUBBED) called"); + ProfileBase profile_base{}; + profile_base.user_id = user_id; + if (Settings::values.username.size() > profile_base.username.size()) { + std::copy_n(Settings::values.username.begin(), profile_base.username.size(), + profile_base.username.begin()); + } else { + std::copy(Settings::values.username.begin(), Settings::values.username.end(), + profile_base.username.begin()); + } + + IPC::ResponseBuilder rb{ctx, 16}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(profile_base); + } + void GetBase(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_ACC, "(STUBBED) called"); // TODO(Subv): Retrieve this information from somewhere. ProfileBase profile_base{}; profile_base.user_id = user_id; - profile_base.username = {'y', 'u', 'z', 'u'}; - + if (Settings::values.username.size() > profile_base.username.size()) { + std::copy_n(Settings::values.username.begin(), profile_base.username.size(), + profile_base.username.begin()); + } else { + std::copy(Settings::values.username.begin(), Settings::values.username.end(), + profile_base.username.begin()); + } IPC::ResponseBuilder rb{ctx, 16}; rb.Push(RESULT_SUCCESS); rb.PushRaw(profile_base); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 97ef07bf9..9404d6b8c 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -11,6 +11,9 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" +#include "core/hle/service/am/idle.h" +#include "core/hle/service/am/omm.h" +#include "core/hle/service/am/spsm.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/nvflinger/nvflinger.h" @@ -649,7 +652,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { // TODO(bunnei): This should be configurable IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u64>(Service::Set::LanguageCode::EN_US)); + rb.Push( + static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index))); LOG_DEBUG(Service_AM, "called"); } @@ -689,6 +693,9 @@ void InstallInterfaces(SM::ServiceManager& service_manager, std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager); std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager); + std::make_shared<IdleSys>()->InstallAsService(service_manager); + std::make_shared<OMM>()->InstallAsService(service_manager); + std::make_shared<SPSM>()->InstallAsService(service_manager); } IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") { diff --git a/src/core/hle/service/am/idle.cpp b/src/core/hle/service/am/idle.cpp new file mode 100644 index 000000000..af46e9494 --- /dev/null +++ b/src/core/hle/service/am/idle.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/am/idle.h" + +namespace Service::AM { + +IdleSys::IdleSys() : ServiceFramework{"idle:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetAutoPowerDownEvent"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/idle.h b/src/core/hle/service/am/idle.h new file mode 100644 index 000000000..1eb68d2c9 --- /dev/null +++ b/src/core/hle/service/am/idle.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class IdleSys final : public ServiceFramework<IdleSys> { +public: + explicit IdleSys(); +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/omm.cpp b/src/core/hle/service/am/omm.cpp new file mode 100644 index 000000000..447fe8669 --- /dev/null +++ b/src/core/hle/service/am/omm.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/am/omm.h" + +namespace Service::AM { + +OMM::OMM() : ServiceFramework{"omm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetOperationMode"}, + {1, nullptr, "GetOperationModeChangeEvent"}, + {2, nullptr, "EnableAudioVisual"}, + {3, nullptr, "DisableAudioVisual"}, + {4, nullptr, "EnterSleepAndWait"}, + {5, nullptr, "GetCradleStatus"}, + {6, nullptr, "FadeInDisplay"}, + {7, nullptr, "FadeOutDisplay"}, + {8, nullptr, "Unknown1"}, + {9, nullptr, "Unknown2"}, + {10, nullptr, "Unknown3"}, + {11, nullptr, "Unknown4"}, + {12, nullptr, "Unknown5"}, + {13, nullptr, "Unknown6"}, + {14, nullptr, "Unknown7"}, + {15, nullptr, "Unknown8"}, + {16, nullptr, "Unknown9"}, + {17, nullptr, "Unknown10"}, + {18, nullptr, "Unknown11"}, + {19, nullptr, "Unknown12"}, + {20, nullptr, "Unknown13"}, + {21, nullptr, "Unknown14"}, + {22, nullptr, "Unknown15"}, + {23, nullptr, "Unknown16"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/omm.h b/src/core/hle/service/am/omm.h new file mode 100644 index 000000000..49e5d331c --- /dev/null +++ b/src/core/hle/service/am/omm.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class OMM final : public ServiceFramework<OMM> { +public: + explicit OMM(); +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/am/spsm.cpp b/src/core/hle/service/am/spsm.cpp new file mode 100644 index 000000000..a05d433d0 --- /dev/null +++ b/src/core/hle/service/am/spsm.cpp @@ -0,0 +1,30 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/am/spsm.h" + +namespace Service::AM { + +SPSM::SPSM() : ServiceFramework{"spsm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetState"}, + {1, nullptr, "SleepSystemAndWaitAwake"}, + {2, nullptr, "Unknown1"}, + {3, nullptr, "Unknown2"}, + {4, nullptr, "GetNotificationMessageEventHandle"}, + {5, nullptr, "Unknown3"}, + {6, nullptr, "Unknown4"}, + {7, nullptr, "Unknown5"}, + {8, nullptr, "AnalyzePerformanceLogForLastSleepWakeSequence"}, + {9, nullptr, "ChangeHomeButtonLongPressingTime"}, + {10, nullptr, "Unknown6"}, + {11, nullptr, "Unknown7"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::AM diff --git a/src/core/hle/service/am/spsm.h b/src/core/hle/service/am/spsm.h new file mode 100644 index 000000000..57dde62e1 --- /dev/null +++ b/src/core/hle/service/am/spsm.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::AM { + +class SPSM final : public ServiceFramework<SPSM> { +public: + explicit SPSM(); +}; + +} // namespace Service::AM diff --git a/src/core/hle/service/apm/apm.cpp b/src/core/hle/service/apm/apm.cpp index 7a185c6c8..4109cb7f7 100644 --- a/src/core/hle/service/apm/apm.cpp +++ b/src/core/hle/service/apm/apm.cpp @@ -13,6 +13,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) { auto module_ = std::make_shared<Module>(); std::make_shared<APM>(module_, "apm")->InstallAsService(service_manager); std::make_shared<APM>(module_, "apm:p")->InstallAsService(service_manager); + std::make_shared<APM_Sys>()->InstallAsService(service_manager); } } // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp index ce943d829..4cd8132f5 100644 --- a/src/core/hle/service/apm/interface.cpp +++ b/src/core/hle/service/apm/interface.cpp @@ -74,6 +74,31 @@ void APM::OpenSession(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<ISession>(); + + LOG_DEBUG(Service_APM, "called"); +} + +APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestPerformanceMode"}, + {1, &APM_Sys::GetPerformanceEvent, "GetPerformanceEvent"}, + {2, nullptr, "GetThrottlingState"}, + {3, nullptr, "GetLastThrottlingState"}, + {4, nullptr, "ClearLastThrottlingState"}, + {5, nullptr, "LoadAndApplySettings"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISession>(); + + LOG_DEBUG(Service_APM, "called"); } } // namespace Service::APM diff --git a/src/core/hle/service/apm/interface.h b/src/core/hle/service/apm/interface.h index fa68c7d93..d14264ad7 100644 --- a/src/core/hle/service/apm/interface.h +++ b/src/core/hle/service/apm/interface.h @@ -19,4 +19,12 @@ private: std::shared_ptr<Module> apm; }; +class APM_Sys final : public ServiceFramework<APM_Sys> { +public: + explicit APM_Sys(); + +private: + void GetPerformanceEvent(Kernel::HLERequestContext& ctx); +}; + } // namespace Service::APM diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp new file mode 100644 index 000000000..358ef2576 --- /dev/null +++ b/src/core/hle/service/arp/arp.cpp @@ -0,0 +1,75 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/arp/arp.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::ARP { + +class ARP_R final : public ServiceFramework<ARP_R> { +public: + explicit ARP_R() : ServiceFramework{"arp:r"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetApplicationLaunchProperty"}, + {1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"}, + {2, nullptr, "GetApplicationControlProperty"}, + {3, nullptr, "GetApplicationControlPropertyWithApplicationId"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IRegistrar final : public ServiceFramework<IRegistrar> { +public: + explicit IRegistrar() : ServiceFramework{"IRegistrar"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Issue"}, + {1, nullptr, "SetApplicationLaunchProperty"}, + {2, nullptr, "SetApplicationControlProperty"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class ARP_W final : public ServiceFramework<ARP_W> { +public: + explicit ARP_W() : ServiceFramework{"arp:w"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"}, + {1, nullptr, "DeleteProperties"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void AcquireRegistrar(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IRegistrar>(); + + LOG_DEBUG(Service_ARP, "called"); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<ARP_R>()->InstallAsService(sm); + std::make_shared<ARP_W>()->InstallAsService(sm); +} + +} // namespace Service::ARP diff --git a/src/core/hle/service/arp/arp.h b/src/core/hle/service/arp/arp.h new file mode 100644 index 000000000..9d100187c --- /dev/null +++ b/src/core/hle/service/arp/arp.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::ARP { + +/// Registers all ARP services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::ARP diff --git a/src/core/hle/service/audio/audctl.cpp b/src/core/hle/service/audio/audctl.cpp new file mode 100644 index 000000000..37c3fdcac --- /dev/null +++ b/src/core/hle/service/audio/audctl.cpp @@ -0,0 +1,45 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audctl.h" + +namespace Service::Audio { + +AudCtl::AudCtl() : ServiceFramework{"audctl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetTargetVolume"}, + {1, nullptr, "SetTargetVolume"}, + {2, nullptr, "GetTargetVolumeMin"}, + {3, nullptr, "GetTargetVolumeMax"}, + {4, nullptr, "IsTargetMute"}, + {5, nullptr, "SetTargetMute"}, + {6, nullptr, "IsTargetConnected"}, + {7, nullptr, "SetDefaultTarget"}, + {8, nullptr, "GetDefaultTarget"}, + {9, nullptr, "GetAudioOutputMode"}, + {10, nullptr, "SetAudioOutputMode"}, + {11, nullptr, "SetForceMutePolicy"}, + {12, nullptr, "GetForceMutePolicy"}, + {13, nullptr, "GetOutputModeSetting"}, + {14, nullptr, "SetOutputModeSetting"}, + {15, nullptr, "SetOutputTarget"}, + {16, nullptr, "SetInputTargetForceEnabled"}, + {17, nullptr, "SetHeadphoneOutputLevelMode"}, + {18, nullptr, "GetHeadphoneOutputLevelMode"}, + {19, nullptr, "AcquireAudioVolumeUpdateEventForPlayReport"}, + {20, nullptr, "AcquireAudioOutputDeviceUpdateEventForPlayReport"}, + {21, nullptr, "GetAudioOutputTargetForPlayReport"}, + {22, nullptr, "NotifyHeadphoneVolumeWarningDisplayedEvent"}, + {23, nullptr, "SetSystemOutputMasterVolume"}, + {24, nullptr, "GetSystemOutputMasterVolume"}, + {25, nullptr, "GetAudioVolumeDataForPlayReport"}, + {26, nullptr, "UpdateHeadphoneSettings"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audctl.h b/src/core/hle/service/audio/audctl.h new file mode 100644 index 000000000..ed837bdf2 --- /dev/null +++ b/src/core/hle/service/audio/audctl.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudCtl final : public ServiceFramework<AudCtl> { +public: + explicit AudCtl(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/auddbg.cpp b/src/core/hle/service/audio/auddbg.cpp new file mode 100644 index 000000000..b08c21a20 --- /dev/null +++ b/src/core/hle/service/audio/auddbg.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/auddbg.h" + +namespace Service::Audio { + +AudDbg::AudDbg(const char* name) : ServiceFramework{name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendForDebug"}, + {1, nullptr, "RequestResumeForDebug"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/auddbg.h b/src/core/hle/service/audio/auddbg.h new file mode 100644 index 000000000..a2f540b75 --- /dev/null +++ b/src/core/hle/service/audio/auddbg.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudDbg final : public ServiceFramework<AudDbg> { +public: + explicit AudDbg(const char* name); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audin_a.cpp b/src/core/hle/service/audio/audin_a.cpp new file mode 100644 index 000000000..a70d5bca4 --- /dev/null +++ b/src/core/hle/service/audio/audin_a.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audin_a.h" + +namespace Service::Audio { + +AudInA::AudInA() : ServiceFramework{"audin:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendAudioIns"}, + {1, nullptr, "RequestResumeAudioIns"}, + {2, nullptr, "GetAudioInsProcessMasterVolume"}, + {3, nullptr, "SetAudioInsProcessMasterVolume"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audin_a.h b/src/core/hle/service/audio/audin_a.h new file mode 100644 index 000000000..e4c75510f --- /dev/null +++ b/src/core/hle/service/audio/audin_a.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudInA final : public ServiceFramework<AudInA> { +public: + explicit AudInA(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audio.cpp b/src/core/hle/service/audio/audio.cpp index d231e91e1..6b5e15633 100644 --- a/src/core/hle/service/audio/audio.cpp +++ b/src/core/hle/service/audio/audio.cpp @@ -2,10 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/service/audio/audctl.h" +#include "core/hle/service/audio/auddbg.h" +#include "core/hle/service/audio/audin_a.h" #include "core/hle/service/audio/audin_u.h" #include "core/hle/service/audio/audio.h" +#include "core/hle/service/audio/audout_a.h" #include "core/hle/service/audio/audout_u.h" +#include "core/hle/service/audio/audrec_a.h" #include "core/hle/service/audio/audrec_u.h" +#include "core/hle/service/audio/audren_a.h" #include "core/hle/service/audio/audren_u.h" #include "core/hle/service/audio/codecctl.h" #include "core/hle/service/audio/hwopus.h" @@ -13,12 +19,22 @@ namespace Service::Audio { void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<AudCtl>()->InstallAsService(service_manager); + std::make_shared<AudOutA>()->InstallAsService(service_manager); std::make_shared<AudOutU>()->InstallAsService(service_manager); + std::make_shared<AudInA>()->InstallAsService(service_manager); std::make_shared<AudInU>()->InstallAsService(service_manager); + std::make_shared<AudRecA>()->InstallAsService(service_manager); std::make_shared<AudRecU>()->InstallAsService(service_manager); + std::make_shared<AudRenA>()->InstallAsService(service_manager); std::make_shared<AudRenU>()->InstallAsService(service_manager); std::make_shared<CodecCtl>()->InstallAsService(service_manager); std::make_shared<HwOpus>()->InstallAsService(service_manager); + + std::make_shared<AudDbg>("audin:d")->InstallAsService(service_manager); + std::make_shared<AudDbg>("audout:d")->InstallAsService(service_manager); + std::make_shared<AudDbg>("audrec:d")->InstallAsService(service_manager); + std::make_shared<AudDbg>("audren:d")->InstallAsService(service_manager); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_a.cpp b/src/core/hle/service/audio/audout_a.cpp new file mode 100644 index 000000000..bf8d40157 --- /dev/null +++ b/src/core/hle/service/audio/audout_a.cpp @@ -0,0 +1,24 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audout_a.h" + +namespace Service::Audio { + +AudOutA::AudOutA() : ServiceFramework{"audout:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendAudioOuts"}, + {1, nullptr, "RequestResumeAudioOuts"}, + {2, nullptr, "GetAudioOutsProcessMasterVolume"}, + {3, nullptr, "SetAudioOutsProcessMasterVolume"}, + {4, nullptr, "GetAudioOutsProcessRecordVolume"}, + {5, nullptr, "SetAudioOutsProcessRecordVolume"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_a.h b/src/core/hle/service/audio/audout_a.h new file mode 100644 index 000000000..91a069152 --- /dev/null +++ b/src/core/hle/service/audio/audout_a.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudOutA final : public ServiceFramework<AudOutA> { +public: + explicit AudOutA(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 1dcd84d98..108a7c6eb 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -4,9 +4,10 @@ #include <array> #include <vector> + +#include "audio_core/codec.h" #include "common/logging/log.h" -#include "core/core_timing.h" -#include "core/core_timing_util.h" +#include "core/core.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/hle_ipc.h" @@ -14,17 +15,21 @@ namespace Service::Audio { -/// Switch sample rate frequency -constexpr u32 sample_rate{48000}; -/// TODO(st4rk): dynamic number of channels, as I think Switch has support -/// to more audio channels (probably when Docked I guess) -constexpr u32 audio_channels{2}; -/// TODO(st4rk): find a proper value for the audio_ticks -constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 500)}; +namespace ErrCodes { +enum { + ErrorUnknown = 2, + BufferCountExceeded = 8, +}; +} + +constexpr std::array<char, 10> DefaultDevice{{"DeviceOut"}}; +constexpr int DefaultSampleRate{48000}; class IAudioOut final : public ServiceFramework<IAudioOut> { public: - IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) { + IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core) + : ServiceFramework("IAudioOut"), audio_params(audio_params), audio_core(audio_core) { + static const FunctionInfo functions[] = { {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, @@ -32,66 +37,65 @@ public: {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"}, - {6, nullptr, "ContainsAudioOutBuffer"}, + {6, &IAudioOut::ContainsAudioOutBuffer, "ContainsAudioOutBuffer"}, {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, - {9, nullptr, "GetAudioOutBufferCount"}, + {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, {10, nullptr, "GetAudioOutPlayedSampleCount"}, {11, nullptr, "FlushAudioOutBuffers"}, }; RegisterHandlers(functions); // This is the event handle used to check if the audio buffer was released - buffer_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent"); - - // Register event callback to update the Audio Buffer - audio_event = CoreTiming::RegisterEvent( - "IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) { - UpdateAudioBuffersCallback(); - CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); - }); - - // Start the audio event - CoreTiming::ScheduleEvent(audio_ticks, audio_event); - } + buffer_event = Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioOutBufferReleased"); - ~IAudioOut() { - CoreTiming::UnscheduleEvent(audio_event, 0); + stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count, + "IAudioOut", [=]() { buffer_event->Signal(); }); } 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"); + void GetAudioOutState(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "called"); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(static_cast<u32>(audio_out_state)); + rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped)); } void StartAudioOut(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - // Start audio - audio_out_state = AudioState::Started; + if (stream->IsPlaying()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::ErrorUnknown)); + return; + } + + audio_core.StartStream(stream); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void StopAudioOut(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - - // Stop audio - audio_out_state = AudioState::Stopped; + LOG_DEBUG(Service_Audio, "called"); - queue_keys.clear(); + audio_core.StopStream(stream); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void RegisterBufferEvent(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); @@ -99,101 +103,107 @@ private: } void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "(STUBBED) called {}", ctx.Description()); IPC::RequestParser rp{ctx}; - const u64 key{rp.Pop<u64>()}; - queue_keys.insert(queue_keys.begin(), key); + 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>()}; + + std::vector<s16> samples(audio_buffer.buffer_size / sizeof(s16)); + Memory::ReadBlock(audio_buffer.buffer, samples.data(), audio_buffer.buffer_size); + + if (!audio_core.QueueBuffer(stream, tag, std::move(samples))) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::Audio, ErrCodes::BufferCountExceeded)); + } IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); - - // TODO(st4rk): This is how libtransistor currently implements the - // GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address - // is used to know which buffer should be filled with data and send again to the service - // through AppendAudioOutBuffer. Check if this is the proper way to do it. - u64 key{0}; - - if (queue_keys.size()) { - key = queue_keys.back(); - queue_keys.pop_back(); - } + LOG_DEBUG(Service_Audio, "called {}", ctx.Description()); + IPC::RequestParser rp{ctx}; + const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)}; + const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)}; - ctx.WriteBuffer(&key, sizeof(u64)); + std::vector<u64> tags{released_buffers}; + tags.resize(max_count); + ctx.WriteBuffer(tags); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - // TODO(st4rk): This might be the total of released buffers, needs to be verified on - // hardware - rb.Push<u32>(static_cast<u32>(queue_keys.size())); + rb.Push<u32>(static_cast<u32>(released_buffers.size())); } - void UpdateAudioBuffersCallback() { - if (audio_out_state != AudioState::Started) { - return; - } - - if (queue_keys.empty()) { - return; - } + void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::RequestParser rp{ctx}; + const u64 tag{rp.Pop<u64>()}; + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(stream->ContainsBuffer(tag)); + } - buffer_event->Signal(); + void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u32>(stream->GetQueueSize())); } - enum class AudioState : u32 { - Started, - Stopped, - }; + AudioCore::AudioOut& audio_core; + AudioCore::StreamPtr stream; - /// This is used to trigger the audio event callback that is going to read the samples from the - /// audio_buffer list and enqueue the samples using the sink (audio_core). - CoreTiming::EventType* audio_event; + AudoutParams audio_params{}; /// This is the evend handle used to check if the audio buffer was released Kernel::SharedPtr<Kernel::Event> buffer_event; - - /// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor - /// uses the key as an address in the App, so we need to return when the - /// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because - /// libtransistor uses the key returned as an pointer. - std::vector<u64> queue_keys; - - AudioState audio_out_state; }; void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); IPC::RequestParser rp{ctx}; - constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; - ctx.WriteBuffer(audio_interface); + ctx.WriteBuffer(DefaultDevice); IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); rb.Push(RESULT_SUCCESS); - // TODO(st4rk): We're currently returning only one audio interface (stringlist size). However, - // it's highly possible to have more than one interface (despite that libtransistor requires - // only one). - rb.Push<u32>(1); + rb.Push<u32>(1); // Amount of audio devices } void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + LOG_DEBUG(Service_Audio, "called"); - if (!audio_out_interface) { - audio_out_interface = std::make_shared<IAudioOut>(); + ctx.WriteBuffer(DefaultDevice); + 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; } + if (!params.sample_rate) { + params.sample_rate = DefaultSampleRate; + } + + // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl + // will likely need to be updated as well. + ASSERT_MSG(!audio_out_interface, "Unimplemented"); + audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core); IPC::ResponseBuilder rb{ctx, 6, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(sample_rate); - rb.Push<u32>(audio_channels); - rb.Push<u32>(static_cast<u32>(PcmFormat::Int16)); - rb.Push<u32>(0); // This field is unknown + 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<Audio::IAudioOut>(audio_out_interface); } @@ -203,6 +213,7 @@ AudOutU::AudOutU() : ServiceFramework("audout:u") { {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}}; RegisterHandlers(functions); + audio_core = std::make_unique<AudioCore::AudioOut>(); } } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index 847d86aa6..fd491f65d 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h @@ -4,6 +4,7 @@ #pragma once +#include "audio_core/audio_out.h" #include "core/hle/service/service.h" namespace Kernel { @@ -12,6 +13,18 @@ class HLERequestContext; namespace Service::Audio { +struct AudoutParams { + s32_le sample_rate; + u16_le channel_count; + INSERT_PADDING_BYTES(2); +}; +static_assert(sizeof(AudoutParams) == 0x8, "AudoutParams is an invalid size"); + +enum class AudioState : u32 { + Started, + Stopped, +}; + class IAudioOut; class AudOutU final : public ServiceFramework<AudOutU> { @@ -21,19 +34,10 @@ public: private: std::shared_ptr<IAudioOut> audio_out_interface; + std::unique_ptr<AudioCore::AudioOut> audio_core; void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); - - enum class PcmFormat : u32 { - Invalid = 0, - Int8 = 1, - Int16 = 2, - Int24 = 3, - Int32 = 4, - PcmFloat = 5, - Adpcm = 6, - }; }; } // namespace Service::Audio diff --git a/src/core/hle/service/audio/audrec_a.cpp b/src/core/hle/service/audio/audrec_a.cpp new file mode 100644 index 000000000..016eabf53 --- /dev/null +++ b/src/core/hle/service/audio/audrec_a.cpp @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audrec_a.h" + +namespace Service::Audio { + +AudRecA::AudRecA() : ServiceFramework{"audrec:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendFinalOutputRecorders"}, + {1, nullptr, "RequestResumeFinalOutputRecorders"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audrec_a.h b/src/core/hle/service/audio/audrec_a.h new file mode 100644 index 000000000..9685047f2 --- /dev/null +++ b/src/core/hle/service/audio/audrec_a.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudRecA final : public ServiceFramework<AudRecA> { +public: + explicit AudRecA(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_a.cpp b/src/core/hle/service/audio/audren_a.cpp new file mode 100644 index 000000000..616ff3dc4 --- /dev/null +++ b/src/core/hle/service/audio/audren_a.cpp @@ -0,0 +1,26 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/audio/audren_a.h" + +namespace Service::Audio { + +AudRenA::AudRenA() : ServiceFramework{"audren:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestSuspendAudioRenderers"}, + {1, nullptr, "RequestResumeAudioRenderers"}, + {2, nullptr, "GetAudioRenderersProcessMasterVolume"}, + {3, nullptr, "SetAudioRenderersProcessMasterVolume"}, + {4, nullptr, "RegisterAppletResourceUserId"}, + {5, nullptr, "UnregisterAppletResourceUserId"}, + {6, nullptr, "GetAudioRenderersProcessRecordVolume"}, + {7, nullptr, "SetAudioRenderersProcessRecordVolume"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_a.h b/src/core/hle/service/audio/audren_a.h new file mode 100644 index 000000000..5ecf2e184 --- /dev/null +++ b/src/core/hle/service/audio/audren_a.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::Audio { + +class AudRenA final : public ServiceFramework<AudRenA> { +public: + explicit AudRenA(); +}; + +} // namespace Service::Audio diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 6aed9e2fa..f99304de5 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -15,13 +15,10 @@ namespace Service::Audio { -/// TODO(bunnei): Find a proper value for the audio_ticks -constexpr u64 audio_ticks{static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / 200)}; - class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { public: - explicit IAudioRenderer(AudioRendererParameter audren_params) - : ServiceFramework("IAudioRenderer"), worker_params(audren_params) { + explicit IAudioRenderer(AudioCore::AudioRendererParameter audren_params) + : ServiceFramework("IAudioRenderer") { static const FunctionInfo functions[] = { {0, nullptr, "GetAudioRendererSampleRate"}, {1, nullptr, "GetAudioRendererSampleCount"}, @@ -39,21 +36,8 @@ public: RegisterHandlers(functions); system_event = - Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioRenderer:SystemEvent"); - - // Register event callback to update the Audio Buffer - audio_event = CoreTiming::RegisterEvent( - "IAudioRenderer::UpdateAudioCallback", [this](u64 userdata, int cycles_late) { - UpdateAudioCallback(); - CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event); - }); - - // Start the audio event - CoreTiming::ScheduleEvent(audio_ticks, audio_event); - voice_status_list.resize(worker_params.voice_count); - } - ~IAudioRenderer() { - CoreTiming::UnscheduleEvent(audio_event, 0); + Kernel::Event::Create(Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent"); + renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event); } private: @@ -62,60 +46,9 @@ private: } void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) { - UpdateDataHeader config{}; - auto buf = ctx.ReadBuffer(); - std::memcpy(&config, buf.data(), sizeof(UpdateDataHeader)); - u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); - - std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count); - std::memcpy(mem_pool_info.data(), - buf.data() + sizeof(UpdateDataHeader) + config.behavior_size, - memory_pool_count * sizeof(MemoryPoolInfo)); - - std::vector<VoiceInfo> voice_info(worker_params.voice_count); - std::memcpy(voice_info.data(), - buf.data() + sizeof(UpdateDataHeader) + config.behavior_size + - config.memory_pools_size + config.voice_resource_size, - worker_params.voice_count * sizeof(VoiceInfo)); - - UpdateDataHeader response_data{worker_params}; - - ASSERT(ctx.GetWriteBufferSize() == response_data.total_size); - - std::vector<u8> output(response_data.total_size); - std::memcpy(output.data(), &response_data, sizeof(UpdateDataHeader)); - std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); - for (unsigned i = 0; i < memory_pool.size(); i++) { - if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestAttach) - memory_pool[i].state = MemoryPoolStates::Attached; - else if (mem_pool_info[i].pool_state == MemoryPoolStates::RequestDetach) - memory_pool[i].state = MemoryPoolStates::Detached; - } - std::memcpy(output.data() + sizeof(UpdateDataHeader), memory_pool.data(), - response_data.memory_pools_size); - - for (unsigned i = 0; i < voice_info.size(); i++) { - if (voice_info[i].is_new) { - voice_status_list[i].played_sample_count = 0; - voice_status_list[i].wave_buffer_consumed = 0; - } else if (voice_info[i].play_state == (u8)PlayStates::Started) { - for (u32 buff_idx = 0; buff_idx < voice_info[i].wave_buffer_count; buff_idx++) { - voice_status_list[i].played_sample_count += - (voice_info[i].wave_buffer[buff_idx].end_sample_offset - - voice_info[i].wave_buffer[buff_idx].start_sample_offset) / - 2; - voice_status_list[i].wave_buffer_consumed++; - } - } - } - std::memcpy(output.data() + sizeof(UpdateDataHeader) + response_data.memory_pools_size, - voice_status_list.data(), response_data.voices_size); - - ctx.WriteBuffer(output); - + ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_Audio, "(STUBBED) called"); } @@ -136,8 +69,6 @@ private: } void QuerySystemEvent(Kernel::HLERequestContext& ctx) { - // system_event->Signal(); - IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(RESULT_SUCCESS); rb.PushCopyObjects(system_event); @@ -145,131 +76,8 @@ private: LOG_WARNING(Service_Audio, "(STUBBED) called"); } - enum class MemoryPoolStates : u32 { // Should be LE - Invalid = 0x0, - Unknown = 0x1, - RequestDetach = 0x2, - Detached = 0x3, - RequestAttach = 0x4, - Attached = 0x5, - Released = 0x6, - }; - - enum class PlayStates : u8 { - Started = 0, - Stopped = 1, - }; - - struct MemoryPoolEntry { - MemoryPoolStates state; - u32_le unknown_4; - u32_le unknown_8; - u32_le unknown_c; - }; - static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size"); - - struct MemoryPoolInfo { - u64_le pool_address; - u64_le pool_size; - MemoryPoolStates pool_state; - INSERT_PADDING_WORDS(3); // Unknown - }; - static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size"); - - struct UpdateDataHeader { - UpdateDataHeader() {} - - explicit UpdateDataHeader(const AudioRendererParameter& config) { - revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision - behavior_size = 0xb0; - memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; - voices_size = config.voice_count * 0x10; - voice_resource_size = 0x0; - effects_size = config.effect_count * 0x10; - mixes_size = 0x0; - sinks_size = config.sink_count * 0x20; - performance_manager_size = 0x10; - total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + - voices_size + effects_size + sinks_size + performance_manager_size; - } - - u32_le revision; - u32_le behavior_size; - u32_le memory_pools_size; - u32_le voices_size; - u32_le voice_resource_size; - u32_le effects_size; - u32_le mixes_size; - u32_le sinks_size; - u32_le performance_manager_size; - INSERT_PADDING_WORDS(6); - u32_le total_size; - }; - static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); - - struct BiquadFilter { - u8 enable; - INSERT_PADDING_BYTES(1); - s16_le numerator[3]; - s16_le denominator[2]; - }; - static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size"); - - struct WaveBuffer { - u64_le buffer_addr; - u64_le buffer_sz; - s32_le start_sample_offset; - s32_le end_sample_offset; - u8 loop; - u8 end_of_stream; - u8 sent_to_server; - INSERT_PADDING_BYTES(5); - u64 context_addr; - u64 context_sz; - INSERT_PADDING_BYTES(8); - }; - static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); - - struct VoiceInfo { - u32_le id; - u32_le node_id; - u8 is_new; - u8 is_in_use; - u8 play_state; - u8 sample_format; - u32_le sample_rate; - u32_le priority; - u32_le sorting_order; - u32_le channel_count; - float_le pitch; - float_le volume; - BiquadFilter biquad_filter[2]; - u32_le wave_buffer_count; - u16_le wave_buffer_head; - INSERT_PADDING_BYTES(6); - u64_le additional_params_addr; - u64_le additional_params_sz; - u32_le mix_id; - u32_le splitter_info_id; - WaveBuffer wave_buffer[4]; - u32_le voice_channel_resource_ids[6]; - INSERT_PADDING_BYTES(24); - }; - static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size"); - - struct VoiceOutStatus { - u64_le played_sample_count; - u32_le wave_buffer_consumed; - INSERT_PADDING_WORDS(1); - }; - static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size"); - - /// This is used to trigger the audio event callback. - CoreTiming::EventType* audio_event; - Kernel::SharedPtr<Kernel::Event> system_event; - AudioRendererParameter worker_params; - std::vector<VoiceOutStatus> voice_status_list; + std::unique_ptr<AudioCore::AudioRenderer> renderer; }; class IAudioDevice final : public ServiceFramework<IAudioDevice> { @@ -368,7 +176,7 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") { void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<AudioRendererParameter>(); + auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); @@ -379,7 +187,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto params = rp.PopRaw<AudioRendererParameter>(); + auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); u64 buffer_sz = Common::AlignUp(4 * params.unknown_8, 0x40); buffer_sz += params.unknown_c * 1024; diff --git a/src/core/hle/service/audio/audren_u.h b/src/core/hle/service/audio/audren_u.h index b9b81db4f..14907f8ae 100644 --- a/src/core/hle/service/audio/audren_u.h +++ b/src/core/hle/service/audio/audren_u.h @@ -4,6 +4,7 @@ #pragma once +#include "audio_core/audio_renderer.h" #include "core/hle/service/service.h" namespace Kernel { @@ -12,24 +13,6 @@ class HLERequestContext; namespace Service::Audio { -struct AudioRendererParameter { - u32_le sample_rate; - u32_le sample_count; - u32_le unknown_8; - u32_le unknown_c; - u32_le voice_count; - u32_le sink_count; - u32_le effect_count; - u32_le unknown_1c; - u8 unknown_20; - INSERT_PADDING_BYTES(3); - u32_le splitter_count; - u32_le unknown_2c; - INSERT_PADDING_WORDS(1); - u32_le revision; -}; -static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size"); - class AudRenU final : public ServiceFramework<AudRenU> { public: explicit AudRenU(); diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 844df382c..371cd4997 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> +#include <opus.h> #include "common/logging/log.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" @@ -9,19 +11,142 @@ namespace Service::Audio { +struct OpusDeleter { + void operator()(void* ptr) const { + operator delete(ptr); + } +}; + +class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { +public: + IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoder, OpusDeleter> decoder, u32 sample_rate, + u32 channel_count) + : ServiceFramework("IHardwareOpusDecoderManager"), decoder(std::move(decoder)), + sample_rate(sample_rate), channel_count(channel_count) { + static const FunctionInfo functions[] = { + {0, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, + {1, nullptr, "SetContext"}, + {2, nullptr, "DecodeInterleavedForMultiStream"}, + {3, nullptr, "SetContextForMultiStream"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + }; + RegisterHandlers(functions); + } + +private: + void DecodeInterleaved(Kernel::HLERequestContext& ctx) { + u32 consumed = 0; + u32 sample_count = 0; + std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); + if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) { + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(ogniK): Use correct error code + rb.Push(ResultCode(-1)); + return; + } + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(consumed); + rb.Push<u32>(sample_count); + ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); + } + + bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, + std::vector<opus_int16>& output) { + size_t raw_output_sz = output.size() * sizeof(opus_int16); + if (sizeof(OpusHeader) > input.size()) + return false; + OpusHeader hdr{}; + std::memcpy(&hdr, input.data(), sizeof(OpusHeader)); + if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) { + return false; + } + auto frame = input.data() + sizeof(OpusHeader); + auto decoded_sample_count = opus_packet_get_nb_samples( + frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)), + static_cast<opus_int32>(sample_rate)); + if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) + return false; + auto out_sample_count = + opus_decode(decoder.get(), frame, hdr.sz, output.data(), + (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0); + if (out_sample_count < 0) + return false; + sample_count = out_sample_count; + consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz); + return true; + } + + struct OpusHeader { + u32_be sz; // Needs to be BE for some odd reason + INSERT_PADDING_WORDS(1); + }; + static_assert(sizeof(OpusHeader) == 0x8, "OpusHeader is an invalid size"); + + std::unique_ptr<OpusDecoder, OpusDeleter> decoder; + u32 sample_rate; + u32 channel_count; +}; + +static size_t WorkerBufferSize(u32 channel_count) { + ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + return opus_decoder_get_size(static_cast<int>(channel_count)); +} + void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_Audio, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + auto sample_rate = rp.Pop<u32>(); + auto channel_count = rp.Pop<u32>(); + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || + sample_rate == 12000 || sample_rate == 8000, + "Invalid sample rate"); + ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count)); + LOG_DEBUG(Audio, "called worker_buffer_sz={}", worker_buffer_sz); + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push<u32>(0x4000); + rb.Push<u32>(worker_buffer_sz); +} + +void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto sample_rate = rp.Pop<u32>(); + auto channel_count = rp.Pop<u32>(); + auto buffer_sz = rp.Pop<u32>(); + LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, + channel_count, buffer_sz); + ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || + sample_rate == 12000 || sample_rate == 8000, + "Invalid sample rate"); + ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); + + size_t worker_sz = WorkerBufferSize(channel_count); + ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); + std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ + static_cast<OpusDecoder*>(operator new(worker_sz))}; + if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { + IPC::ResponseBuilder rb{ctx, 2}; + // TODO(ogniK): Use correct error code + rb.Push(ResultCode(-1)); + return; + } + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IHardwareOpusDecoderManager>(std::move(decoder), sample_rate, + channel_count); } HwOpus::HwOpus() : ServiceFramework("hwopus") { static const FunctionInfo functions[] = { - {0, nullptr, "Initialize"}, + {0, &HwOpus::OpenOpusDecoder, "OpenOpusDecoder"}, {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, - {2, nullptr, "InitializeMultiStream"}, - {3, nullptr, "GetWorkBufferSizeMultiStream"}, + {2, nullptr, "OpenOpusDecoderForMultiStream"}, + {3, nullptr, "GetWorkBufferSizeForMultiStream"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index 090b8c825..5258d59f3 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h @@ -14,6 +14,7 @@ public: ~HwOpus() = default; private: + void OpenOpusDecoder(Kernel::HLERequestContext& ctx); void GetWorkBufferSize(Kernel::HLERequestContext& ctx); }; diff --git a/src/core/hle/service/bpc/bpc.cpp b/src/core/hle/service/bpc/bpc.cpp new file mode 100644 index 000000000..1c1ecdb60 --- /dev/null +++ b/src/core/hle/service/bpc/bpc.cpp @@ -0,0 +1,57 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/bpc/bpc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::BPC { + +class BPC final : public ServiceFramework<BPC> { +public: + explicit BPC() : ServiceFramework{"bpc"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ShutdownSystem"}, + {1, nullptr, "RebootSystem"}, + {2, nullptr, "GetWakeupReason"}, + {3, nullptr, "GetShutdownReason"}, + {4, nullptr, "GetAcOk"}, + {5, nullptr, "GetBoardPowerControlEvent"}, + {6, nullptr, "GetSleepButtonState"}, + {7, nullptr, "GetPowerEvent"}, + {8, nullptr, "Unknown1"}, + {9, nullptr, "Unknown2"}, + {10, nullptr, "Unknown3"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class BPC_R final : public ServiceFramework<BPC_R> { +public: + explicit BPC_R() : ServiceFramework{"bpc:r"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetExternalRtcValue"}, + {1, nullptr, "SetExternalRtcValue"}, + {2, nullptr, "ReadExternalRtcResetFlag"}, + {3, nullptr, "ClearExternalRtcResetFlag"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<BPC>()->InstallAsService(sm); + std::make_shared<BPC_R>()->InstallAsService(sm); +} + +} // namespace Service::BPC diff --git a/src/core/hle/service/bpc/bpc.h b/src/core/hle/service/bpc/bpc.h new file mode 100644 index 000000000..eaa37be8d --- /dev/null +++ b/src/core/hle/service/bpc/bpc.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::BPC { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::BPC diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp new file mode 100644 index 000000000..d0a15cc4c --- /dev/null +++ b/src/core/hle/service/btdrv/btdrv.cpp @@ -0,0 +1,72 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/btdrv/btdrv.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::BtDrv { + +class BtDrv final : public ServiceFramework<BtDrv> { +public: + explicit BtDrv() : ServiceFramework{"btdrv"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown"}, + {1, nullptr, "Init"}, + {2, nullptr, "Enable"}, + {3, nullptr, "Disable"}, + {4, nullptr, "CleanupAndShutdown"}, + {5, nullptr, "GetAdapterProperties"}, + {6, nullptr, "GetAdapterProperty"}, + {7, nullptr, "SetAdapterProperty"}, + {8, nullptr, "StartDiscovery"}, + {9, nullptr, "CancelDiscovery"}, + {10, nullptr, "CreateBond"}, + {11, nullptr, "RemoveBond"}, + {12, nullptr, "CancelBond"}, + {13, nullptr, "PinReply"}, + {14, nullptr, "SspReply"}, + {15, nullptr, "Unknown2"}, + {16, nullptr, "InitInterfaces"}, + {17, nullptr, "HidHostInterface_Connect"}, + {18, nullptr, "HidHostInterface_Disconnect"}, + {19, nullptr, "HidHostInterface_SendData"}, + {20, nullptr, "HidHostInterface_SendData2"}, + {21, nullptr, "HidHostInterface_SetReport"}, + {22, nullptr, "HidHostInterface_GetReport"}, + {23, nullptr, "HidHostInterface_WakeController"}, + {24, nullptr, "HidHostInterface_AddPairedDevice"}, + {25, nullptr, "HidHostInterface_GetPairedDevice"}, + {26, nullptr, "HidHostInterface_CleanupAndShutdown"}, + {27, nullptr, "Unknown3"}, + {28, nullptr, "ExtInterface_SetTSI"}, + {29, nullptr, "ExtInterface_SetBurstMode"}, + {30, nullptr, "ExtInterface_SetZeroRetran"}, + {31, nullptr, "ExtInterface_SetMcMode"}, + {32, nullptr, "ExtInterface_StartLlrMode"}, + {33, nullptr, "ExtInterface_ExitLlrMode"}, + {34, nullptr, "ExtInterface_SetRadio"}, + {35, nullptr, "ExtInterface_SetVisibility"}, + {36, nullptr, "Unknown4"}, + {37, nullptr, "Unknown5"}, + {38, nullptr, "HidHostInterface_GetLatestPlr"}, + {39, nullptr, "ExtInterface_GetPendingConnections"}, + {40, nullptr, "HidHostInterface_GetChannelMap"}, + {41, nullptr, "SetIsBluetoothBoostEnabled"}, + {42, nullptr, "GetIsBluetoothBoostEnabled"}, + {43, nullptr, "SetIsBluetoothAfhEnabled"}, + {44, nullptr, "GetIsBluetoothAfhEnabled"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<BtDrv>()->InstallAsService(sm); +} + +} // namespace Service::BtDrv diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h new file mode 100644 index 000000000..164e56f43 --- /dev/null +++ b/src/core/hle/service/btdrv/btdrv.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::BtDrv { + +/// Registers all BtDrv services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::BtDrv diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp new file mode 100644 index 000000000..b949bfabd --- /dev/null +++ b/src/core/hle/service/btm/btm.cpp @@ -0,0 +1,121 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/btm/btm.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::BTM { + +class BTM final : public ServiceFramework<BTM> { +public: + explicit BTM() : ServiceFramework{"btm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "RegisterSystemEventForConnectedDeviceConditionImpl"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "RegisterSystemEventForRegisteredDeviceInfoImpl"}, + {9, nullptr, "Unknown8"}, + {10, nullptr, "Unknown9"}, + {11, nullptr, "Unknown10"}, + {12, nullptr, "Unknown11"}, + {13, nullptr, "Unknown12"}, + {14, nullptr, "EnableRadioImpl"}, + {15, nullptr, "DisableRadioImpl"}, + {16, nullptr, "Unknown13"}, + {17, nullptr, "Unknown14"}, + {18, nullptr, "Unknown15"}, + {19, nullptr, "Unknown16"}, + {20, nullptr, "Unknown17"}, + {21, nullptr, "Unknown18"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class BTM_DBG final : public ServiceFramework<BTM_DBG> { +public: + explicit BTM_DBG() : ServiceFramework{"btm:dbg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RegisterSystemEventForDiscoveryImpl"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "Unknown8"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IBtmSystemCore final : public ServiceFramework<IBtmSystemCore> { +public: + explicit IBtmSystemCore() : ServiceFramework{"IBtmSystemCore"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "StartGamepadPairingImpl"}, + {1, nullptr, "CancelGamepadPairingImpl"}, + {2, nullptr, "ClearGamepadPairingDatabaseImpl"}, + {3, nullptr, "GetPairedGamepadCountImpl"}, + {4, nullptr, "EnableRadioImpl"}, + {5, nullptr, "DisableRadioImpl"}, + {6, nullptr, "GetRadioOnOffImpl"}, + {7, nullptr, "AcquireRadioEventImpl"}, + {8, nullptr, "AcquireGamepadPairingEventImpl"}, + {9, nullptr, "IsGamepadPairingStartedImpl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class BTM_SYS final : public ServiceFramework<BTM_SYS> { +public: + explicit BTM_SYS() : ServiceFramework{"btm:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &BTM_SYS::GetCoreImpl, "GetCoreImpl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetCoreImpl(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IBtmSystemCore>(); + + LOG_DEBUG(Service_BTM, "called"); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<BTM>()->InstallAsService(sm); + std::make_shared<BTM_DBG>()->InstallAsService(sm); + std::make_shared<BTM_SYS>()->InstallAsService(sm); +} + +} // namespace Service::BTM diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h new file mode 100644 index 000000000..e6425a7e3 --- /dev/null +++ b/src/core/hle/service/btm/btm.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::BTM { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::BTM diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp new file mode 100644 index 000000000..ae7b0720b --- /dev/null +++ b/src/core/hle/service/caps/caps.cpp @@ -0,0 +1,152 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/caps/caps.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Capture { + +class CAPS_A final : public ServiceFramework<CAPS_A> { +public: + explicit CAPS_A() : ServiceFramework{"caps:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "Unknown7"}, + {7, nullptr, "Unknown8"}, + {8, nullptr, "Unknown9"}, + {9, nullptr, "Unknown10"}, + {10, nullptr, "Unknown11"}, + {11, nullptr, "Unknown12"}, + {12, nullptr, "Unknown13"}, + {13, nullptr, "Unknown14"}, + {14, nullptr, "Unknown15"}, + {301, nullptr, "Unknown16"}, + {401, nullptr, "Unknown17"}, + {501, nullptr, "Unknown18"}, + {1001, nullptr, "Unknown19"}, + {1002, nullptr, "Unknown20"}, + {8001, nullptr, "Unknown21"}, + {8002, nullptr, "Unknown22"}, + {8011, nullptr, "Unknown23"}, + {8012, nullptr, "Unknown24"}, + {8021, nullptr, "Unknown25"}, + {10011, nullptr, "Unknown26"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_C final : public ServiceFramework<CAPS_C> { +public: + explicit CAPS_C() : ServiceFramework{"caps:c"} { + // clang-format off + static const FunctionInfo functions[] = { + {2001, nullptr, "Unknown1"}, + {2002, nullptr, "Unknown2"}, + {2011, nullptr, "Unknown3"}, + {2012, nullptr, "Unknown4"}, + {2013, nullptr, "Unknown5"}, + {2014, nullptr, "Unknown6"}, + {2101, nullptr, "Unknown7"}, + {2102, nullptr, "Unknown8"}, + {2201, nullptr, "Unknown9"}, + {2301, nullptr, "Unknown10"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_SC final : public ServiceFramework<CAPS_SC> { +public: + explicit CAPS_SC() : ServiceFramework{"caps:sc"} { + // clang-format off + static const FunctionInfo functions[] = { + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {1001, nullptr, "Unknown3"}, + {1002, nullptr, "Unknown4"}, + {1003, nullptr, "Unknown5"}, + {1011, nullptr, "Unknown6"}, + {1012, nullptr, "Unknown7"}, + {1201, nullptr, "Unknown8"}, + {1202, nullptr, "Unknown9"}, + {1203, nullptr, "Unknown10"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_SS final : public ServiceFramework<CAPS_SS> { +public: + explicit CAPS_SS() : ServiceFramework{"caps:ss"} { + // clang-format off + static const FunctionInfo functions[] = { + {201, nullptr, "Unknown1"}, + {202, nullptr, "Unknown2"}, + {203, nullptr, "Unknown3"}, + {204, nullptr, "Unknown4"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_SU final : public ServiceFramework<CAPS_SU> { +public: + explicit CAPS_SU() : ServiceFramework{"caps:su"} { + // clang-format off + static const FunctionInfo functions[] = { + {201, nullptr, "SaveScreenShot"}, + {203, nullptr, "SaveScreenShotEx0"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class CAPS_U final : public ServiceFramework<CAPS_U> { +public: + explicit CAPS_U() : ServiceFramework{"caps:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {102, nullptr, "GetAlbumFileListByAruid"}, + {103, nullptr, "DeleteAlbumFileByAruid"}, + {104, nullptr, "GetAlbumFileSizeByAruid"}, + {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, + {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, + {60002, nullptr, "OpenAccessorSessionForApplication"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<CAPS_A>()->InstallAsService(sm); + std::make_shared<CAPS_C>()->InstallAsService(sm); + std::make_shared<CAPS_SC>()->InstallAsService(sm); + std::make_shared<CAPS_SS>()->InstallAsService(sm); + std::make_shared<CAPS_SU>()->InstallAsService(sm); + std::make_shared<CAPS_U>()->InstallAsService(sm); +} + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h new file mode 100644 index 000000000..471185dfa --- /dev/null +++ b/src/core/hle/service/caps/caps.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::Capture { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::Capture diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp new file mode 100644 index 000000000..566fbf924 --- /dev/null +++ b/src/core/hle/service/fgm/fgm.cpp @@ -0,0 +1,75 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/fgm/fgm.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::FGM { + +class IRequest final : public ServiceFramework<IRequest> { +public: + explicit IRequest() : ServiceFramework{"IRequest"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Set"}, + {2, nullptr, "Get"}, + {3, nullptr, "Cancel"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class FGM final : public ServiceFramework<FGM> { +public: + explicit FGM(const char* name) : ServiceFramework{name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &FGM::Initialize, "Initialize"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void Initialize(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IRequest>(); + + LOG_DEBUG(Service_FGM, "called"); + } +}; + +class FGM_DBG final : public ServiceFramework<FGM_DBG> { +public: + explicit FGM_DBG() : ServiceFramework{"fgm:dbg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Read"}, + {2, nullptr, "Cancel"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<FGM>("fgm")->InstallAsService(sm); + std::make_shared<FGM>("fgm:0")->InstallAsService(sm); + std::make_shared<FGM>("fgm:9")->InstallAsService(sm); + std::make_shared<FGM_DBG>()->InstallAsService(sm); +} + +} // namespace Service::FGM diff --git a/src/core/hle/service/fgm/fgm.h b/src/core/hle/service/fgm/fgm.h new file mode 100644 index 000000000..e59691264 --- /dev/null +++ b/src/core/hle/service/fgm/fgm.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::FGM { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::FGM diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index fdd2fda18..e17d637e4 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -14,6 +14,8 @@ #include "core/file_sys/vfs_offset.h" #include "core/file_sys/vfs_real.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_ldr.h" +#include "core/hle/service/filesystem/fsp_pr.h" #include "core/hle/service/filesystem/fsp_srv.h" namespace Service::FileSystem { @@ -298,6 +300,8 @@ void RegisterFileSystems() { void InstallInterfaces(SM::ServiceManager& service_manager) { RegisterFileSystems(); + std::make_shared<FSP_LDR>()->InstallAsService(service_manager); + std::make_shared<FSP_PR>()->InstallAsService(service_manager); std::make_shared<FSP_SRV>()->InstallAsService(service_manager); } diff --git a/src/core/hle/service/filesystem/fsp_ldr.cpp b/src/core/hle/service/filesystem/fsp_ldr.cpp new file mode 100644 index 000000000..0ab9c2606 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_ldr.cpp @@ -0,0 +1,22 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/filesystem/fsp_ldr.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +FSP_LDR::FSP_LDR() : ServiceFramework{"fsp:ldr"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "OpenCodeFileSystem"}, + {1, nullptr, "IsArchivedProgram"}, + {2, nullptr, "SetCurrentProcess"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_ldr.h b/src/core/hle/service/filesystem/fsp_ldr.h new file mode 100644 index 000000000..fa8e11b4c --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_ldr.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class FSP_LDR final : public ServiceFramework<FSP_LDR> { +public: + explicit FSP_LDR(); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_pr.cpp b/src/core/hle/service/filesystem/fsp_pr.cpp new file mode 100644 index 000000000..32b0ae454 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_pr.cpp @@ -0,0 +1,23 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/filesystem/fsp_pr.h" +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +FSP_PR::FSP_PR() : ServiceFramework{"fsp:pr"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RegisterProgram"}, + {1, nullptr, "UnregisterProgram"}, + {2, nullptr, "SetCurrentProcess"}, + {256, nullptr, "SetEnabledProgramVerification"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/filesystem/fsp_pr.h b/src/core/hle/service/filesystem/fsp_pr.h new file mode 100644 index 000000000..62edcd08a --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_pr.h @@ -0,0 +1,16 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service::FileSystem { + +class FSP_PR final : public ServiceFramework<FSP_PR> { +public: + explicit FSP_PR(); +}; + +} // namespace Service::FileSystem diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index e4619a547..8f0262e34 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -326,7 +326,7 @@ public: {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, {80, nullptr, "GetGyroscopeZeroDriftMode"}, {81, nullptr, "ResetGyroscopeZeroDriftMode"}, - {82, nullptr, "IsSixAxisSensorAtRest"}, + {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, {91, nullptr, "ActivateGesture"}, {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, @@ -337,13 +337,14 @@ public: "AcquireNpadStyleSetUpdateEventHandle"}, {107, nullptr, "DisconnectNpad"}, {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, + {109, nullptr, "ActivateNpadWithRevision"}, {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"}, {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, - {125, nullptr, "MergeSingleJoyAsDualJoy"}, + {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, {126, nullptr, "StartLrAssignmentMode"}, {127, nullptr, "StopLrAssignmentMode"}, {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, @@ -455,6 +456,14 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + // TODO (Hexagon12): Properly implement reading gyroscope values from controllers. + rb.Push(true); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -530,6 +539,12 @@ private: LOG_WARNING(Service_HID, "(STUBBED) called"); } + void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_HID, "(STUBBED) called"); + } + void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp new file mode 100644 index 000000000..8fc8b1057 --- /dev/null +++ b/src/core/hle/service/lbl/lbl.cpp @@ -0,0 +1,90 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/lbl/lbl.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::LBL { + +class LBL final : public ServiceFramework<LBL> { +public: + explicit LBL() : ServiceFramework{"lbl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "TurnOffBacklight"}, + {7, nullptr, "TurnOnBacklight"}, + {8, nullptr, "GetBacklightStatus"}, + {9, nullptr, "Unknown7"}, + {10, nullptr, "Unknown8"}, + {11, nullptr, "Unknown9"}, + {12, nullptr, "Unknown10"}, + {13, nullptr, "Unknown11"}, + {14, nullptr, "Unknown12"}, + {15, nullptr, "Unknown13"}, + {16, nullptr, "ReadRawLightSensor"}, + {17, nullptr, "Unknown14"}, + {18, nullptr, "Unknown15"}, + {19, nullptr, "Unknown16"}, + {20, nullptr, "Unknown17"}, + {21, nullptr, "Unknown18"}, + {22, nullptr, "Unknown19"}, + {23, nullptr, "Unknown20"}, + {24, nullptr, "Unknown21"}, + {25, nullptr, "Unknown22"}, + {26, &LBL::EnableVrMode, "EnableVrMode"}, + {27, &LBL::DisableVrMode, "DisableVrMode"}, + {28, &LBL::GetVrMode, "GetVrMode"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void EnableVrMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + vr_mode_enabled = true; + + LOG_DEBUG(Service_LBL, "called"); + } + + void DisableVrMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + + vr_mode_enabled = false; + + LOG_DEBUG(Service_LBL, "called"); + } + + void GetVrMode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(vr_mode_enabled); + + LOG_DEBUG(Service_LBL, "called"); + } + + bool vr_mode_enabled = false; +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<LBL>()->InstallAsService(sm); +} + +} // namespace Service::LBL diff --git a/src/core/hle/service/lbl/lbl.h b/src/core/hle/service/lbl/lbl.h new file mode 100644 index 000000000..bf6f400f8 --- /dev/null +++ b/src/core/hle/service/lbl/lbl.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::LBL { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::LBL diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index b497376d7..2e99ddf51 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -13,11 +13,11 @@ namespace Service::LM { -class Logger final : public ServiceFramework<Logger> { +class ILogger final : public ServiceFramework<ILogger> { public: - Logger() : ServiceFramework("Logger") { + ILogger() : ServiceFramework("ILogger") { static const FunctionInfo functions[] = { - {0x00000000, &Logger::Initialize, "Initialize"}, + {0x00000000, &ILogger::Initialize, "Initialize"}, {0x00000001, nullptr, "SetDestination"}, }; RegisterHandlers(functions); @@ -182,7 +182,7 @@ public: void OpenLogger(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(RESULT_SUCCESS); - rb.PushIpcInterface<Logger>(); + rb.PushIpcInterface<ILogger>(); LOG_DEBUG(Service_LM, "called"); } diff --git a/src/core/hle/service/mig/mig.cpp b/src/core/hle/service/mig/mig.cpp new file mode 100644 index 000000000..d16367f2c --- /dev/null +++ b/src/core/hle/service/mig/mig.cpp @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/mig/mig.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Migration { + +class MIG_USR final : public ServiceFramework<MIG_USR> { +public: + explicit MIG_USR() : ServiceFramework{"mig:usr"} { + // clang-format off + static const FunctionInfo functions[] = { + {10, nullptr, "TryGetLastMigrationInfo"}, + {100, nullptr, "CreateServer"}, + {101, nullptr, "ResumeServer"}, + {200, nullptr, "CreateClient"}, + {201, nullptr, "ResumeClient"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<MIG_USR>()->InstallAsService(sm); +} + +} // namespace Service::Migration diff --git a/src/core/hle/service/mig/mig.h b/src/core/hle/service/mig/mig.h new file mode 100644 index 000000000..288c1c1b3 --- /dev/null +++ b/src/core/hle/service/mig/mig.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::Migration { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::Migration diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp new file mode 100644 index 000000000..a6197124a --- /dev/null +++ b/src/core/hle/service/mii/mii.cpp @@ -0,0 +1,107 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/mii/mii.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::Mii { + +class IDatabaseService final : public ServiceFramework<IDatabaseService> { +public: + explicit IDatabaseService() : ServiceFramework{"IDatabaseService"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "IsUpdated"}, + {1, nullptr, "IsFullDatabase"}, + {2, nullptr, "GetCount"}, + {3, nullptr, "Get"}, + {4, nullptr, "Get1"}, + {5, nullptr, "UpdateLatest"}, + {6, nullptr, "BuildRandom"}, + {7, nullptr, "BuildDefault"}, + {8, nullptr, "Get2"}, + {9, nullptr, "Get3"}, + {10, nullptr, "UpdateLatest1"}, + {11, nullptr, "FindIndex"}, + {12, nullptr, "Move"}, + {13, nullptr, "AddOrReplace"}, + {14, nullptr, "Delete"}, + {15, nullptr, "DestroyFile"}, + {16, nullptr, "DeleteFile"}, + {17, nullptr, "Format"}, + {18, nullptr, "Import"}, + {19, nullptr, "Export"}, + {20, nullptr, "IsBrokenDatabaseWithClearFlag"}, + {21, nullptr, "GetIndex"}, + {22, nullptr, "SetInterfaceVersion"}, + {23, nullptr, "Convert"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class MiiDBModule final : public ServiceFramework<MiiDBModule> { +public: + explicit MiiDBModule(const char* name) : ServiceFramework{name} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetDatabaseService(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IDatabaseService>(); + + LOG_DEBUG(Service_Mii, "called"); + } +}; + +class MiiImg final : public ServiceFramework<MiiImg> { +public: + explicit MiiImg() : ServiceFramework{"miiimg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {10, nullptr, "Reload"}, + {11, nullptr, "GetCount"}, + {12, nullptr, "IsEmpty"}, + {13, nullptr, "IsFull"}, + {14, nullptr, "GetAttribute"}, + {15, nullptr, "LoadImage"}, + {16, nullptr, "AddOrUpdateImage"}, + {17, nullptr, "DeleteImages"}, + {100, nullptr, "DeleteFile"}, + {101, nullptr, "DestroyFile"}, + {102, nullptr, "ImportFile"}, + {103, nullptr, "ExportFile"}, + {104, nullptr, "ForceInitialize"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<MiiDBModule>("mii:e")->InstallAsService(sm); + std::make_shared<MiiDBModule>("mii:u")->InstallAsService(sm); + + std::make_shared<MiiImg>()->InstallAsService(sm); +} + +} // namespace Service::Mii diff --git a/src/core/hle/service/mii/mii.h b/src/core/hle/service/mii/mii.h new file mode 100644 index 000000000..7ce9be50e --- /dev/null +++ b/src/core/hle/service/mii/mii.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::Mii { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::Mii diff --git a/src/core/hle/service/ncm/ncm.cpp b/src/core/hle/service/ncm/ncm.cpp new file mode 100644 index 000000000..0297edca0 --- /dev/null +++ b/src/core/hle/service/ncm/ncm.cpp @@ -0,0 +1,59 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/ncm/ncm.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::NCM { + +class LocationResolver final : public ServiceFramework<LocationResolver> { +public: + explicit LocationResolver() : ServiceFramework{"lr"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "OpenLocationResolver"}, + {1, nullptr, "OpenRegisteredLocationResolver"}, + {2, nullptr, "RefreshLocationResolver"}, + {3, nullptr, "OpenAddOnContentLocationResolver"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NCM final : public ServiceFramework<NCM> { +public: + explicit NCM() : ServiceFramework{"ncm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateContentStorage"}, + {1, nullptr, "CreateContentMetaDatabase"}, + {2, nullptr, "VerifyContentStorage"}, + {3, nullptr, "VerifyContentMetaDatabase"}, + {4, nullptr, "OpenContentStorage"}, + {5, nullptr, "OpenContentMetaDatabase"}, + {6, nullptr, "CloseContentStorageForcibly"}, + {7, nullptr, "CloseContentMetaDatabaseForcibly"}, + {8, nullptr, "CleanupContentMetaDatabase"}, + {9, nullptr, "OpenContentStorage2"}, + {10, nullptr, "CloseContentStorage"}, + {11, nullptr, "OpenContentMetaDatabase2"}, + {12, nullptr, "CloseContentMetaDatabase"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<LocationResolver>()->InstallAsService(sm); + std::make_shared<NCM>()->InstallAsService(sm); +} + +} // namespace Service::NCM diff --git a/src/core/hle/service/ncm/ncm.h b/src/core/hle/service/ncm/ncm.h new file mode 100644 index 000000000..7bc8518a6 --- /dev/null +++ b/src/core/hle/service/ncm/ncm.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::NCM { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::NCM diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp new file mode 100644 index 000000000..8fec97db8 --- /dev/null +++ b/src/core/hle/service/nfc/nfc.cpp @@ -0,0 +1,222 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/nfc/nfc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::NFC { + +class IAm final : public ServiceFramework<IAm> { +public: + explicit IAm() : ServiceFramework{"IAm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "NotifyForegroundApplet"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NFC_AM final : public ServiceFramework<NFC_AM> { +public: + explicit NFC_AM() : ServiceFramework{"nfc:am"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NFC_AM::CreateAmInterface, "CreateAmInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void CreateAmInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IAm>(); + + LOG_DEBUG(Service_NFC, "called"); + } +}; + +class MFIUser final : public ServiceFramework<MFIUser> { +public: + explicit MFIUser() : ServiceFramework{"IUser"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "ListDevices"}, + {3, nullptr, "StartDetection"}, + {4, nullptr, "StopDetection"}, + {5, nullptr, "Read"}, + {6, nullptr, "Write"}, + {7, nullptr, "GetTagInfo"}, + {8, nullptr, "GetActivateEventHandle"}, + {9, nullptr, "GetDeactivateEventHandle"}, + {10, nullptr, "GetState"}, + {11, nullptr, "GetDeviceState"}, + {12, nullptr, "GetNpadId"}, + {13, nullptr, "GetAvailabilityChangeEventHandle"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NFC_MF_U final : public ServiceFramework<NFC_MF_U> { +public: + explicit NFC_MF_U() : ServiceFramework{"nfc:mf:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NFC_MF_U::CreateUserInterface, "CreateUserInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void CreateUserInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<MFIUser>(); + + LOG_DEBUG(Service_NFC, "called"); + } +}; + +class IUser final : public ServiceFramework<IUser> { +public: + explicit IUser() : ServiceFramework{"IUser"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "GetState"}, + {3, nullptr, "IsNfcEnabled"}, + {400, nullptr, "Initialize"}, + {401, nullptr, "Finalize"}, + {402, nullptr, "GetState"}, + {403, nullptr, "IsNfcEnabled"}, + {404, nullptr, "ListDevices"}, + {405, nullptr, "GetDeviceState"}, + {406, nullptr, "GetNpadId"}, + {407, nullptr, "AttachAvailabilityChangeEvent"}, + {408, nullptr, "StartDetection"}, + {409, nullptr, "StopDetection"}, + {410, nullptr, "GetTagInfo"}, + {411, nullptr, "AttachActivateEvent"}, + {412, nullptr, "AttachDeactivateEvent"}, + {1000, nullptr, "ReadMifare"}, + {1001, nullptr, "WriteMifare"}, + {1300, nullptr, "SendCommandByPassThrough"}, + {1301, nullptr, "KeepPassThroughSession"}, + {1302, nullptr, "ReleasePassThroughSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NFC_U final : public ServiceFramework<NFC_U> { +public: + explicit NFC_U() : ServiceFramework{"nfc:u"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NFC_U::CreateUserInterface, "CreateUserInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void CreateUserInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IUser>(); + + LOG_DEBUG(Service_NFC, "called"); + } +}; + +class ISystem final : public ServiceFramework<ISystem> { +public: + explicit ISystem() : ServiceFramework{"ISystem"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "Finalize"}, + {2, nullptr, "GetState"}, + {3, nullptr, "IsNfcEnabled"}, + {100, nullptr, "SetNfcEnabled"}, + {400, nullptr, "InitializeSystem"}, + {401, nullptr, "FinalizeSystem"}, + {402, nullptr, "GetState"}, + {403, nullptr, "IsNfcEnabled"}, + {404, nullptr, "ListDevices"}, + {405, nullptr, "GetDeviceState"}, + {406, nullptr, "GetNpadId"}, + {407, nullptr, "AttachAvailabilityChangeEvent"}, + {408, nullptr, "StartDetection"}, + {409, nullptr, "StopDetection"}, + {410, nullptr, "GetTagInfo"}, + {411, nullptr, "AttachActivateEvent"}, + {412, nullptr, "AttachDeactivateEvent"}, + {500, nullptr, "SetNfcEnabled"}, + {1000, nullptr, "ReadMifare"}, + {1001, nullptr, "WriteMifare"}, + {1300, nullptr, "SendCommandByPassThrough"}, + {1301, nullptr, "KeepPassThroughSession"}, + {1302, nullptr, "ReleasePassThroughSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NFC_SYS final : public ServiceFramework<NFC_SYS> { +public: + explicit NFC_SYS() : ServiceFramework{"nfc:sys"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &NFC_SYS::CreateSystemInterface, "CreateSystemInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void CreateSystemInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystem>(); + + LOG_DEBUG(Service_NFC, "called"); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<NFC_AM>()->InstallAsService(sm); + std::make_shared<NFC_MF_U>()->InstallAsService(sm); + std::make_shared<NFC_U>()->InstallAsService(sm); + std::make_shared<NFC_SYS>()->InstallAsService(sm); +} + +} // namespace Service::NFC diff --git a/src/core/hle/service/nfc/nfc.h b/src/core/hle/service/nfc/nfc.h new file mode 100644 index 000000000..4d2d815f9 --- /dev/null +++ b/src/core/hle/service/nfc/nfc.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::NFC { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::NFC diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 89c703310..98017267c 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp @@ -2,12 +2,459 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/ns/pl_u.h" namespace Service::NS { +class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { +public: + explicit IAccountProxyInterface() : ServiceFramework{"IAccountProxyInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "CreateUserAccount"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> { +public: + explicit IApplicationManagerInterface() : ServiceFramework{"IApplicationManagerInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ListApplicationRecord"}, + {1, nullptr, "GenerateApplicationRecordCount"}, + {2, nullptr, "GetApplicationRecordUpdateSystemEvent"}, + {3, nullptr, "GetApplicationViewDeprecated"}, + {4, nullptr, "DeleteApplicationEntity"}, + {5, nullptr, "DeleteApplicationCompletely"}, + {6, nullptr, "IsAnyApplicationEntityRedundant"}, + {7, nullptr, "DeleteRedundantApplicationEntity"}, + {8, nullptr, "IsApplicationEntityMovable"}, + {9, nullptr, "MoveApplicationEntity"}, + {11, nullptr, "CalculateApplicationOccupiedSize"}, + {16, nullptr, "PushApplicationRecord"}, + {17, nullptr, "ListApplicationRecordContentMeta"}, + {19, nullptr, "LaunchApplication"}, + {21, nullptr, "GetApplicationContentPath"}, + {22, nullptr, "TerminateApplication"}, + {23, nullptr, "ResolveApplicationContentPath"}, + {26, nullptr, "BeginInstallApplication"}, + {27, nullptr, "DeleteApplicationRecord"}, + {30, nullptr, "RequestApplicationUpdateInfo"}, + {32, nullptr, "CancelApplicationDownload"}, + {33, nullptr, "ResumeApplicationDownload"}, + {35, nullptr, "UpdateVersionList"}, + {36, nullptr, "PushLaunchVersion"}, + {37, nullptr, "ListRequiredVersion"}, + {38, nullptr, "CheckApplicationLaunchVersion"}, + {39, nullptr, "CheckApplicationLaunchRights"}, + {40, nullptr, "GetApplicationLogoData"}, + {41, nullptr, "CalculateApplicationDownloadRequiredSize"}, + {42, nullptr, "CleanupSdCard"}, + {43, nullptr, "CheckSdCardMountStatus"}, + {44, nullptr, "GetSdCardMountStatusChangedEvent"}, + {45, nullptr, "GetGameCardAttachmentEvent"}, + {46, nullptr, "GetGameCardAttachmentInfo"}, + {47, nullptr, "GetTotalSpaceSize"}, + {48, nullptr, "GetFreeSpaceSize"}, + {49, nullptr, "GetSdCardRemovedEvent"}, + {52, nullptr, "GetGameCardUpdateDetectionEvent"}, + {53, nullptr, "DisableApplicationAutoDelete"}, + {54, nullptr, "EnableApplicationAutoDelete"}, + {55, nullptr, "GetApplicationDesiredLanguage"}, + {56, nullptr, "SetApplicationTerminateResult"}, + {57, nullptr, "ClearApplicationTerminateResult"}, + {58, nullptr, "GetLastSdCardMountUnexpectedResult"}, + {59, nullptr, "ConvertApplicationLanguageToLanguageCode"}, + {60, nullptr, "ConvertLanguageCodeToApplicationLanguage"}, + {61, nullptr, "GetBackgroundDownloadStressTaskInfo"}, + {62, nullptr, "GetGameCardStopper"}, + {63, nullptr, "IsSystemProgramInstalled"}, + {64, nullptr, "StartApplyDeltaTask"}, + {65, nullptr, "GetRequestServerStopper"}, + {66, nullptr, "GetBackgroundApplyDeltaStressTaskInfo"}, + {67, nullptr, "CancelApplicationApplyDelta"}, + {68, nullptr, "ResumeApplicationApplyDelta"}, + {69, nullptr, "CalculateApplicationApplyDeltaRequiredSize"}, + {70, nullptr, "ResumeAll"}, + {71, nullptr, "GetStorageSize"}, + {80, nullptr, "RequestDownloadApplication"}, + {81, nullptr, "RequestDownloadAddOnContent"}, + {82, nullptr, "DownloadApplication"}, + {83, nullptr, "CheckApplicationResumeRights"}, + {84, nullptr, "GetDynamicCommitEvent"}, + {85, nullptr, "RequestUpdateApplication2"}, + {86, nullptr, "EnableApplicationCrashReport"}, + {87, nullptr, "IsApplicationCrashReportEnabled"}, + {90, nullptr, "BoostSystemMemoryResourceLimit"}, + {100, nullptr, "ResetToFactorySettings"}, + {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, + {102, nullptr, "ResetToFactorySettingsForRefurbishment"}, + {200, nullptr, "CalculateUserSaveDataStatistics"}, + {201, nullptr, "DeleteUserSaveDataAll"}, + {210, nullptr, "DeleteUserSystemSaveData"}, + {220, nullptr, "UnregisterNetworkServiceAccount"}, + {300, nullptr, "GetApplicationShellEvent"}, + {301, nullptr, "PopApplicationShellEventInfo"}, + {302, nullptr, "LaunchLibraryApplet"}, + {303, nullptr, "TerminateLibraryApplet"}, + {304, nullptr, "LaunchSystemApplet"}, + {305, nullptr, "TerminateSystemApplet"}, + {306, nullptr, "LaunchOverlayApplet"}, + {307, nullptr, "TerminateOverlayApplet"}, + {400, nullptr, "GetApplicationControlData"}, + {401, nullptr, "InvalidateAllApplicationControlCache"}, + {402, nullptr, "RequestDownloadApplicationControlData"}, + {403, nullptr, "GetMaxApplicationControlCacheCount"}, + {404, nullptr, "InvalidateApplicationControlCache"}, + {405, nullptr, "ListApplicationControlCacheEntryInfo"}, + {502, nullptr, "RequestCheckGameCardRegistration"}, + {503, nullptr, "RequestGameCardRegistrationGoldPoint"}, + {504, nullptr, "RequestRegisterGameCard"}, + {505, nullptr, "GetGameCardMountFailureEvent"}, + {506, nullptr, "IsGameCardInserted"}, + {507, nullptr, "EnsureGameCardAccess"}, + {508, nullptr, "GetLastGameCardMountFailureResult"}, + {509, nullptr, "ListApplicationIdOnGameCard"}, + {600, nullptr, "CountApplicationContentMeta"}, + {601, nullptr, "ListApplicationContentMetaStatus"}, + {602, nullptr, "ListAvailableAddOnContent"}, + {603, nullptr, "GetOwnedApplicationContentMetaStatus"}, + {604, nullptr, "RegisterContentsExternalKey"}, + {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, + {606, nullptr, "GetContentMetaStorage"}, + {700, nullptr, "PushDownloadTaskList"}, + {701, nullptr, "ClearTaskStatusList"}, + {702, nullptr, "RequestDownloadTaskList"}, + {703, nullptr, "RequestEnsureDownloadTask"}, + {704, nullptr, "ListDownloadTaskStatus"}, + {705, nullptr, "RequestDownloadTaskListData"}, + {800, nullptr, "RequestVersionList"}, + {801, nullptr, "ListVersionList"}, + {802, nullptr, "RequestVersionListData"}, + {900, nullptr, "GetApplicationRecord"}, + {901, nullptr, "GetApplicationRecordProperty"}, + {902, nullptr, "EnableApplicationAutoUpdate"}, + {903, nullptr, "DisableApplicationAutoUpdate"}, + {904, nullptr, "TouchApplication"}, + {905, nullptr, "RequestApplicationUpdate"}, + {906, nullptr, "IsApplicationUpdateRequested"}, + {907, nullptr, "WithdrawApplicationUpdateRequest"}, + {908, nullptr, "ListApplicationRecordInstalledContentMeta"}, + {909, nullptr, "WithdrawCleanupAddOnContentsWithNoRightsRecommendation"}, + {1000, nullptr, "RequestVerifyApplicationDeprecated"}, + {1001, nullptr, "CorruptApplicationForDebug"}, + {1002, nullptr, "RequestVerifyAddOnContentsRights"}, + {1003, nullptr, "RequestVerifyApplication"}, + {1004, nullptr, "CorruptContentForDebug"}, + {1200, nullptr, "NeedsUpdateVulnerability"}, + {1300, nullptr, "IsAnyApplicationEntityInstalled"}, + {1301, nullptr, "DeleteApplicationContentEntities"}, + {1302, nullptr, "CleanupUnrecordedApplicationEntity"}, + {1303, nullptr, "CleanupAddOnContentsWithNoRights"}, + {1304, nullptr, "DeleteApplicationContentEntity"}, + {1305, nullptr, "TryDeleteRunningApplicationEntity"}, + {1306, nullptr, "TryDeleteRunningApplicationCompletely"}, + {1307, nullptr, "TryDeleteRunningApplicationContentEntities"}, + {1400, nullptr, "PrepareShutdown"}, + {1500, nullptr, "FormatSdCard"}, + {1501, nullptr, "NeedsSystemUpdateToFormatSdCard"}, + {1502, nullptr, "GetLastSdCardFormatUnexpectedResult"}, + {1504, nullptr, "InsertSdCard"}, + {1505, nullptr, "RemoveSdCard"}, + {1600, nullptr, "GetSystemSeedForPseudoDeviceId"}, + {1601, nullptr, "ResetSystemSeedForPseudoDeviceId"}, + {1700, nullptr, "ListApplicationDownloadingContentMeta"}, + {1701, nullptr, "GetApplicationView"}, + {1702, nullptr, "GetApplicationDownloadTaskStatus"}, + {1703, nullptr, "GetApplicationViewDownloadErrorContext"}, + {1800, nullptr, "IsNotificationSetupCompleted"}, + {1801, nullptr, "GetLastNotificationInfoCount"}, + {1802, nullptr, "ListLastNotificationInfo"}, + {1803, nullptr, "ListNotificationTask"}, + {1900, nullptr, "IsActiveAccount"}, + {1901, nullptr, "RequestDownloadApplicationPrepurchasedRights"}, + {1902, nullptr, "GetApplicationTicketInfo"}, + {2000, nullptr, "GetSystemDeliveryInfo"}, + {2001, nullptr, "SelectLatestSystemDeliveryInfo"}, + {2002, nullptr, "VerifyDeliveryProtocolVersion"}, + {2003, nullptr, "GetApplicationDeliveryInfo"}, + {2004, nullptr, "HasAllContentsToDeliver"}, + {2005, nullptr, "CompareApplicationDeliveryInfo"}, + {2006, nullptr, "CanDeliverApplication"}, + {2007, nullptr, "ListContentMetaKeyToDeliverApplication"}, + {2008, nullptr, "NeedsSystemUpdateToDeliverApplication"}, + {2009, nullptr, "EstimateRequiredSize"}, + {2010, nullptr, "RequestReceiveApplication"}, + {2011, nullptr, "CommitReceiveApplication"}, + {2012, nullptr, "GetReceiveApplicationProgress"}, + {2013, nullptr, "RequestSendApplication"}, + {2014, nullptr, "GetSendApplicationProgress"}, + {2015, nullptr, "CompareSystemDeliveryInfo"}, + {2016, nullptr, "ListNotCommittedContentMeta"}, + {2017, nullptr, "CreateDownloadTask"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { +public: + explicit IApplicationVersionInterface() : ServiceFramework{"IApplicationVersionInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetLaunchRequiredVersion"}, + {1, nullptr, "UpgradeLaunchRequiredVersion"}, + {35, nullptr, "UpdateVersionList"}, + {36, nullptr, "PushLaunchVersion"}, + {37, nullptr, "ListRequiredVersion"}, + {800, nullptr, "RequestVersionList"}, + {801, nullptr, "ListVersionList"}, + {802, nullptr, "RequestVersionListData"}, + {1000, nullptr, "PerformAutoUpdate"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> { +public: + explicit IContentManagerInterface() : ServiceFramework{"IContentManagerInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {11, nullptr, "CalculateApplicationOccupiedSize"}, + {43, nullptr, "CheckSdCardMountStatus"}, + {47, nullptr, "GetTotalSpaceSize"}, + {48, nullptr, "GetFreeSpaceSize"}, + {600, nullptr, "CountApplicationContentMeta"}, + {601, nullptr, "ListApplicationContentMetaStatus"}, + {605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"}, + {607, nullptr, "IsAnyApplicationRunning"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IDocumentInterface final : public ServiceFramework<IDocumentInterface> { +public: + explicit IDocumentInterface() : ServiceFramework{"IDocumentInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {21, nullptr, "GetApplicationContentPath"}, + {23, nullptr, "ResolveApplicationContentPath"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> { +public: + explicit IDownloadTaskInterface() : ServiceFramework{"IDownloadTaskInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {701, nullptr, "ClearTaskStatusList"}, + {702, nullptr, "RequestDownloadTaskList"}, + {703, nullptr, "RequestEnsureDownloadTask"}, + {704, nullptr, "ListDownloadTaskStatus"}, + {705, nullptr, "RequestDownloadTaskListData"}, + {706, nullptr, "TryCommitCurrentApplicationDownloadTask"}, + {707, nullptr, "EnableAutoCommit"}, + {708, nullptr, "DisableAutoCommit"}, + {709, nullptr, "TriggerDynamicCommitEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IECommerceInterface final : public ServiceFramework<IECommerceInterface> { +public: + explicit IECommerceInterface() : ServiceFramework{"IECommerceInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RequestLinkDevice"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> { +public: + explicit IFactoryResetInterface() : ServiceFramework{"IFactoryResetInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {100, nullptr, "ResetToFactorySettings"}, + {101, nullptr, "ResetToFactorySettingsWithoutUserSaveData"}, + {102, nullptr, "ResetToFactorySettingsForRefurbishment "}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NS final : public ServiceFramework<NS> { +public: + explicit NS(const char* name) : ServiceFramework{name} { + // clang-format off + static const FunctionInfo functions[] = { + {7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"}, + {7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"}, + {7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"}, + {7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"}, + {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"}, + {7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"}, + {7998, &NS::PushInterface<IContentManagerInterface>, "GetContentManagementInterface"}, + {7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + template <typename T> + void PushInterface(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<T>(); + + LOG_DEBUG(Service_NS, "called"); + } +}; + +class NS_DEV final : public ServiceFramework<NS_DEV> { +public: + explicit NS_DEV() : ServiceFramework{"ns:dev"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "LaunchProgram"}, + {1, nullptr, "TerminateProcess"}, + {2, nullptr, "TerminateProgram"}, + {3, nullptr, "GetShellEventHandle"}, + {4, nullptr, "GetShellEventInfo"}, + {5, nullptr, "TerminateApplication"}, + {6, nullptr, "PrepareLaunchProgramFromHost"}, + {7, nullptr, "LaunchApplication"}, + {8, nullptr, "LaunchApplicationWithStorageId"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class ISystemUpdateControl final : public ServiceFramework<ISystemUpdateControl> { +public: + explicit ISystemUpdateControl() : ServiceFramework{"ISystemUpdateControl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "HasDownloaded"}, + {1, nullptr, "RequestCheckLatestUpdate"}, + {2, nullptr, "RequestDownloadLatestUpdate"}, + {3, nullptr, "GetDownloadProgress"}, + {4, nullptr, "ApplyDownloadedUpdate"}, + {5, nullptr, "RequestPrepareCardUpdate"}, + {6, nullptr, "GetPrepareCardUpdateProgress"}, + {7, nullptr, "HasPreparedCardUpdate"}, + {8, nullptr, "ApplyCardUpdate"}, + {9, nullptr, "GetDownloadedEulaDataSize"}, + {10, nullptr, "GetDownloadedEulaData"}, + {11, nullptr, "SetupCardUpdate"}, + {12, nullptr, "GetPreparedCardUpdateEulaDataSize"}, + {13, nullptr, "GetPreparedCardUpdateEulaData"}, + {14, nullptr, "SetupCardUpdateViaSystemUpdater"}, + {15, nullptr, "HasReceived"}, + {16, nullptr, "RequestReceiveSystemUpdate"}, + {17, nullptr, "GetReceiveProgress"}, + {18, nullptr, "ApplyReceivedUpdate"}, + {19, nullptr, "GetReceivedEulaDataSize"}, + {20, nullptr, "GetReceivedEulaData"}, + {21, nullptr, "SetupToReceiveSystemUpdate"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class NS_SU final : public ServiceFramework<NS_SU> { +public: + explicit NS_SU() : ServiceFramework{"ns:su"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetBackgroundNetworkUpdateState"}, + {1, &NS_SU::OpenSystemUpdateControl, "OpenSystemUpdateControl"}, + {2, nullptr, "NotifyExFatDriverRequired"}, + {3, nullptr, "ClearExFatDriverStatusForDebug"}, + {4, nullptr, "RequestBackgroundNetworkUpdate"}, + {5, nullptr, "NotifyBackgroundNetworkUpdate"}, + {6, nullptr, "NotifyExFatDriverDownloadedForDebug"}, + {9, nullptr, "GetSystemUpdateNotificationEventForContentDelivery"}, + {10, nullptr, "NotifySystemUpdateForContentDelivery"}, + {11, nullptr, "PrepareShutdown"}, + {16, nullptr, "DestroySystemUpdateTask"}, + {17, nullptr, "RequestSendSystemUpdate"}, + {18, nullptr, "GetSendSystemUpdateProgress"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void OpenSystemUpdateControl(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<ISystemUpdateControl>(); + + LOG_DEBUG(Service_NS, "called"); + } +}; + +class NS_VM final : public ServiceFramework<NS_VM> { +public: + explicit NS_VM() : ServiceFramework{"ns:vm"} { + // clang-format off + static const FunctionInfo functions[] = { + {1200, nullptr, "NeedsUpdateVulnerability"}, + {1201, nullptr, "UpdateSafeSystemVersionForDebug"}, + {1202, nullptr, "GetSafeSystemVersion"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); + std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); + std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); + std::make_shared<NS>("ns:rt")->InstallAsService(service_manager); + std::make_shared<NS>("ns:web")->InstallAsService(service_manager); + + std::make_shared<NS_DEV>()->InstallAsService(service_manager); + std::make_shared<NS_SU>()->InstallAsService(service_manager); + std::make_shared<NS_VM>()->InstallAsService(service_manager); + std::make_shared<PL_U>()->InstallAsService(service_manager); } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index ed69a4325..8bc49935a 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -7,8 +7,8 @@ #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "video_core/gpu.h" #include "video_core/renderer_base.h" -#include "video_core/video_core.h" namespace Service::Nvidia::Devices { @@ -30,9 +30,9 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform, crop_rect}; - Core::System::GetInstance().perf_stats.EndGameFrame(); - - VideoCore::g_renderer->SwapBuffers(framebuffer); + auto& instance = Core::System::GetInstance(); + instance.perf_stats.EndGameFrame(); + instance.Renderer().SwapBuffers(framebuffer); } } // 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 57b128b40..be2b79256 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -2,14 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cinttypes> +#include <cstring> #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" #include "core/hle/service/nvdrv/devices/nvmap.h" +#include "video_core/memory_manager.h" +#include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" -#include "video_core/video_core.h" namespace Service::Nvidia::Devices { @@ -150,15 +151,16 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); - auto& gpu = Core::System::GetInstance().GPU(); - - auto itr = buffer_mappings.find(params.offset); - + const auto itr = buffer_mappings.find(params.offset); ASSERT_MSG(itr != buffer_mappings.end(), "Tried to unmap invalid mapping"); + auto& system_instance = Core::System::GetInstance(); + // Remove this memory region from the rasterizer cache. - VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(params.offset, itr->second.size); + system_instance.Renderer().Rasterizer().FlushAndInvalidateRegion(params.offset, + itr->second.size); + auto& gpu = system_instance.GPU(); params.offset = gpu.memory_manager->UnmapBuffer(params.offset, itr->second.size); buffer_mappings.erase(itr->second.offset); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 671b092e1..5685eb2be 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstdlib> +#include <cstring> + #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 090261a60..6b496e9fe 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -5,8 +5,6 @@ #pragma once #include <array> -#include <cstdlib> -#include <cstring> #include <vector> #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" 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 44e062f50..ae421247d 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cinttypes> +#include <cstring> #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h" @@ -97,7 +97,9 @@ u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlActiveSlotMask params{}; - std::memcpy(¶ms, input.data(), input.size()); + if (input.size() > 0) { + std::memcpy(¶ms, input.data(), input.size()); + } params.slot = 0x07; params.mask = 0x01; std::memcpy(output.data(), ¶ms, output.size()); @@ -107,7 +109,9 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlZcullGetCtxSize params{}; - std::memcpy(¶ms, input.data(), input.size()); + if (input.size() > 0) { + std::memcpy(¶ms, input.data(), input.size()); + } params.size = 0x1; std::memcpy(output.data(), ¶ms, output.size()); return 0; @@ -116,7 +120,11 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) { LOG_DEBUG(Service_NVDRV, "called"); IoctlNvgpuGpuZcullGetInfoArgs params{}; - std::memcpy(¶ms, input.data(), input.size()); + + if (input.size() > 0) { + std::memcpy(¶ms, input.data(), input.size()); + } + params.width_align_pixels = 0x20; params.height_align_pixels = 0x20; params.pixel_squares_by_aliquots = 0x400; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 126782573..116dabedb 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -2,12 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <cinttypes> -#include <map> +#include <cstring> #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" +#include "core/memory.h" +#include "video_core/gpu.h" +#include "video_core/memory_manager.h" namespace Service::Nvidia::Devices { @@ -132,9 +134,12 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, params.num_entries, params.flags); - auto entries = std::vector<IoctlGpfifoEntry>(); - entries.resize(params.num_entries); - std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)], + ASSERT_MSG(input.size() == + sizeof(IoctlSubmitGpfifo) + params.num_entries * sizeof(IoctlGpfifoEntry), + "Incorrect input size"); + + std::vector<IoctlGpfifoEntry> entries(params.num_entries); + std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], params.num_entries * sizeof(IoctlGpfifoEntry)); for (auto entry : entries) { Tegra::GPUVAddr va_addr = entry.Address(); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index aa8df2e6e..650ed8fbc 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -6,6 +6,7 @@ #include <memory> #include <vector> +#include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index b51c73ee8..364619e67 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> + #include "common/assert.h" #include "common/logging/log.h" #include "core/hle/service/nvdrv/devices/nvhost_nvdec.h" diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 0192aecdd..6ad74421b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -4,11 +4,9 @@ #pragma once -#include <array> -#include <cstdlib> -#include <cstring> #include <vector> #include "common/common_types.h" +#include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" 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 724eeb139..e9305bfb3 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <algorithm> -#include <cinttypes> +#include <cstring> #include "common/assert.h" #include "common/logging/log.h" diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 959b5ba29..1c3529bb6 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -5,7 +5,6 @@ #pragma once #include <memory> -#include <string> #include "core/hle/kernel/event.h" #include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 1555ea806..e8b30921a 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -54,7 +54,7 @@ u32 Module::Open(const std::string& device_name) { return fd; } -u32 Module::Ioctl(u32 fd, u32_le command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) { auto itr = open_files.find(fd); ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); diff --git a/src/core/hle/service/nvdrv/nvmemp.cpp b/src/core/hle/service/nvdrv/nvmemp.cpp index 9ca6e5512..0e8e21bad 100644 --- a/src/core/hle/service/nvdrv/nvmemp.cpp +++ b/src/core/hle/service/nvdrv/nvmemp.cpp @@ -4,8 +4,6 @@ #include "common/assert.h" #include "common/logging/log.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvmemp.h" namespace Service::Nvidia { diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 7132b18ad..adf180509 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp @@ -4,9 +4,8 @@ #include <algorithm> -#include "common/alignment.h" -#include "common/scope_exit.h" -#include "core/core_timing.h" +#include "common/assert.h" +#include "common/logging/log.h" #include "core/hle/service/nvflinger/buffer_queue.h" namespace Service { diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 5344441e1..570aa8493 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -3,8 +3,11 @@ // Refer to the license.txt file included. #include <algorithm> +#include <boost/optional.hpp> #include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" #include "common/microprofile.h" #include "common/scope_exit.h" #include "core/core.h" @@ -31,7 +34,7 @@ NVFlinger::NVFlinger() { // Schedule the screen composition events composition_event = - CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) { + CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) { Compose(); CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event); }); @@ -43,7 +46,7 @@ NVFlinger::~NVFlinger() { CoreTiming::UnscheduleEvent(composition_event, 0); } -u64 NVFlinger::OpenDisplay(const std::string& name) { +u64 NVFlinger::OpenDisplay(std::string_view name) { LOG_WARNING(Service, "Opening display {}", name); // TODO(Subv): Currently we only support the Default display. @@ -127,9 +130,11 @@ void NVFlinger::Compose() { MicroProfileFlip(); if (buffer == boost::none) { + auto& system_instance = Core::System::GetInstance(); + // There was no queued buffer to draw, render previous frame - Core::System::GetInstance().perf_stats.EndGameFrame(); - VideoCore::g_renderer->SwapBuffers({}); + system_instance.perf_stats.EndGameFrame(); + system_instance.Renderer().SwapBuffers({}); continue; } diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 2c908297b..5374df175 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -5,7 +5,11 @@ #pragma once #include <memory> -#include <boost/optional.hpp> +#include <string> +#include <string_view> +#include <vector> + +#include "common/common_types.h" #include "core/hle/kernel/event.h" namespace CoreTiming { @@ -41,7 +45,7 @@ public: ~NVFlinger(); /// Opens the specified display and returns the id. - u64 OpenDisplay(const std::string& name); + u64 OpenDisplay(std::string_view name); /// Creates a layer on the specified display and returns the layer id. u64 CreateLayer(u64 display_id); diff --git a/src/core/hle/service/pcie/pcie.cpp b/src/core/hle/service/pcie/pcie.cpp new file mode 100644 index 000000000..39cf05eba --- /dev/null +++ b/src/core/hle/service/pcie/pcie.cpp @@ -0,0 +1,64 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/pcie/pcie.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::PCIe { + +class ISession final : public ServiceFramework<ISession> { +public: + explicit ISession() : ServiceFramework{"ISession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "QueryFunctions"}, + {1, nullptr, "AcquireFunction"}, + {2, nullptr, "ReleaseFunction"}, + {3, nullptr, "GetFunctionState"}, + {4, nullptr, "GetBarProfile"}, + {5, nullptr, "ReadConfig"}, + {6, nullptr, "WriteConfig"}, + {7, nullptr, "ReadBarRegion"}, + {8, nullptr, "WriteBarRegion"}, + {9, nullptr, "FindCapability"}, + {10, nullptr, "FindExtendedCapability"}, + {11, nullptr, "MapDma"}, + {12, nullptr, "UnmapDma"}, + {13, nullptr, "UnmapDmaBusAddress"}, + {14, nullptr, "GetDmaBusAddress"}, + {15, nullptr, "GetDmaBusAddressRange"}, + {16, nullptr, "SetDmaEnable"}, + {17, nullptr, "AcquireIrq"}, + {18, nullptr, "ReleaseIrq"}, + {19, nullptr, "SetIrqEnable"}, + {20, nullptr, "SetAspmEnable"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class PCIe final : public ServiceFramework<PCIe> { +public: + explicit PCIe() : ServiceFramework{"pcie"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "RegisterClassDriver"}, + {1, nullptr, "QueryFunctionsUnregistered"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<PCIe>()->InstallAsService(sm); +} + +} // namespace Service::PCIe diff --git a/src/core/hle/service/pcie/pcie.h b/src/core/hle/service/pcie/pcie.h new file mode 100644 index 000000000..59c22ca45 --- /dev/null +++ b/src/core/hle/service/pcie/pcie.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PCIe { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::PCIe diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp new file mode 100644 index 000000000..d6891a659 --- /dev/null +++ b/src/core/hle/service/pcv/pcv.cpp @@ -0,0 +1,84 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/pcv/pcv.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::PCV { + +class PCV final : public ServiceFramework<PCV> { +public: + explicit PCV() : ServiceFramework{"pcv"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetPowerEnabled"}, + {1, nullptr, "SetClockEnabled"}, + {2, nullptr, "SetClockRate"}, + {3, nullptr, "GetClockRate"}, + {4, nullptr, "GetState"}, + {5, nullptr, "GetPossibleClockRates"}, + {6, nullptr, "SetMinVClockRate"}, + {7, nullptr, "SetReset"}, + {8, nullptr, "SetVoltageEnabled"}, + {9, nullptr, "GetVoltageEnabled"}, + {10, nullptr, "GetVoltageRange"}, + {11, nullptr, "SetVoltageValue"}, + {12, nullptr, "GetVoltageValue"}, + {13, nullptr, "GetTemperatureThresholds"}, + {14, nullptr, "SetTemperature"}, + {15, nullptr, "Initialize"}, + {16, nullptr, "IsInitialized"}, + {17, nullptr, "Finalize"}, + {18, nullptr, "PowerOn"}, + {19, nullptr, "PowerOff"}, + {20, nullptr, "ChangeVoltage"}, + {21, nullptr, "GetPowerClockInfoEvent"}, + {22, nullptr, "GetOscillatorClock"}, + {23, nullptr, "GetDvfsTable"}, + {24, nullptr, "GetModuleStateTable"}, + {25, nullptr, "GetPowerDomainStateTable"}, + {26, nullptr, "GetFuseInfo"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class PCV_ARB final : public ServiceFramework<PCV_ARB> { +public: + explicit PCV_ARB() : ServiceFramework{"pcv:arb"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "ReleaseControl"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class PCV_IMM final : public ServiceFramework<PCV_IMM> { +public: + explicit PCV_IMM() : ServiceFramework{"pcv:imm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "SetClockRate"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<PCV>()->InstallAsService(sm); + std::make_shared<PCV_ARB>()->InstallAsService(sm); + std::make_shared<PCV_IMM>()->InstallAsService(sm); +} + +} // namespace Service::PCV diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h new file mode 100644 index 000000000..219a893c3 --- /dev/null +++ b/src/core/hle/service/pcv/pcv.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PCV { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::PCV diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp new file mode 100644 index 000000000..bbad870a2 --- /dev/null +++ b/src/core/hle/service/psc/psc.cpp @@ -0,0 +1,77 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/psc/psc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" + +namespace Service::PSC { + +class PSC_C final : public ServiceFramework<PSC_C> { +public: + explicit PSC_C() : ServiceFramework{"psc:c"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "Unknown7"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IPmModule final : public ServiceFramework<IPmModule> { +public: + explicit IPmModule() : ServiceFramework{"IPmModule"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Initialize"}, + {1, nullptr, "GetRequest"}, + {2, nullptr, "Acknowledge"}, + {3, nullptr, "Unknown1"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class PSC_M final : public ServiceFramework<PSC_M> { +public: + explicit PSC_M() : ServiceFramework{"psc:m"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &PSC_M::GetPmModule, "GetPmModule"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetPmModule(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IPmModule>(); + + LOG_DEBUG(Service_PSC, "called"); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<PSC_C>()->InstallAsService(sm); + std::make_shared<PSC_M>()->InstallAsService(sm); +} + +} // namespace Service::PSC diff --git a/src/core/hle/service/psc/psc.h b/src/core/hle/service/psc/psc.h new file mode 100644 index 000000000..5052eb02c --- /dev/null +++ b/src/core/hle/service/psc/psc.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::PSC { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::PSC diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 8b84fd349..889cdd41a 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -19,28 +19,42 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" +#include "core/hle/service/arp/arp.h" #include "core/hle/service/audio/audio.h" #include "core/hle/service/bcat/bcat.h" +#include "core/hle/service/bpc/bpc.h" +#include "core/hle/service/btdrv/btdrv.h" +#include "core/hle/service/btm/btm.h" +#include "core/hle/service/caps/caps.h" #include "core/hle/service/erpt/erpt.h" #include "core/hle/service/es/es.h" #include "core/hle/service/eupld/eupld.h" #include "core/hle/service/fatal/fatal.h" +#include "core/hle/service/fgm/fgm.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/friend/friend.h" #include "core/hle/service/grc/grc.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/lbl/lbl.h" #include "core/hle/service/ldn/ldn.h" #include "core/hle/service/ldr/ldr.h" #include "core/hle/service/lm/lm.h" +#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/ncm/ncm.h" +#include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvdrv/nvdrv.h" +#include "core/hle/service/pcie/pcie.h" #include "core/hle/service/pctl/pctl.h" +#include "core/hle/service/pcv/pcv.h" #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/service.h" #include "core/hle/service/set/settings.h" #include "core/hle/service/sm/controller.h" @@ -49,7 +63,9 @@ #include "core/hle/service/spl/module.h" #include "core/hle/service/ssl/ssl.h" #include "core/hle/service/time/time.h" +#include "core/hle/service/usb/usb.h" #include "core/hle/service/vi/vi.h" +#include "core/hle/service/wlan/wlan.h" using Kernel::ClientPort; using Kernel::ServerPort; @@ -193,34 +209,50 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm) { AM::InstallInterfaces(*sm, nv_flinger); AOC::InstallInterfaces(*sm); APM::InstallInterfaces(*sm); - BCAT::InstallInterfaces(*sm); + ARP::InstallInterfaces(*sm); Audio::InstallInterfaces(*sm); + BCAT::InstallInterfaces(*sm); + BPC::InstallInterfaces(*sm); + BtDrv::InstallInterfaces(*sm); + BTM::InstallInterfaces(*sm); + Capture::InstallInterfaces(*sm); ERPT::InstallInterfaces(*sm); ES::InstallInterfaces(*sm); EUPLD::InstallInterfaces(*sm); Fatal::InstallInterfaces(*sm); + FGM::InstallInterfaces(*sm); FileSystem::InstallInterfaces(*sm); Friend::InstallInterfaces(*sm); GRC::InstallInterfaces(*sm); HID::InstallInterfaces(*sm); + LBL::InstallInterfaces(*sm); LDN::InstallInterfaces(*sm); LDR::InstallInterfaces(*sm); LM::InstallInterfaces(*sm); + Migration::InstallInterfaces(*sm); + Mii::InstallInterfaces(*sm); MM::InstallInterfaces(*sm); + NCM::InstallInterfaces(*sm); + NFC::InstallInterfaces(*sm); NFP::InstallInterfaces(*sm); NIFM::InstallInterfaces(*sm); NIM::InstallInterfaces(*sm); NS::InstallInterfaces(*sm); Nvidia::InstallInterfaces(*sm); + PCIe::InstallInterfaces(*sm); PCTL::InstallInterfaces(*sm); + PCV::InstallInterfaces(*sm); PlayReport::InstallInterfaces(*sm); PM::InstallInterfaces(*sm); + PSC::InstallInterfaces(*sm); + Set::InstallInterfaces(*sm); Sockets::InstallInterfaces(*sm); SPL::InstallInterfaces(*sm); SSL::InstallInterfaces(*sm); Time::InstallInterfaces(*sm); + USB::InstallInterfaces(*sm); VI::InstallInterfaces(*sm, nv_flinger); - Set::InstallInterfaces(*sm); + WLAN::InstallInterfaces(*sm); LOG_DEBUG(Service, "initialized OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 180f22703..046c5e18d 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -8,10 +8,9 @@ #include <string> #include <unordered_map> #include <boost/container/flat_map.hpp> -#include "common/bit_field.h" #include "common/common_types.h" #include "core/hle/kernel/hle_ipc.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Service diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 1651f6122..a461e72ec 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -8,6 +8,7 @@ #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/service/set/set.h" +#include "core/settings.h" namespace Service::Set { @@ -31,6 +32,10 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{ LanguageCode::ZH_HANT, }}; +LanguageCode GetLanguageCodeFromIndex(size_t index) { + return available_language_codes.at(index); +} + void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) { ctx.WriteBuffer(available_language_codes); @@ -49,9 +54,17 @@ void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called"); } +void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u64>(available_language_codes[Settings::values.language_index])); + + LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); +} + SET::SET() : ServiceFramework("set") { static const FunctionInfo functions[] = { - {0, nullptr, "GetLanguageCode"}, + {0, &SET::GetLanguageCode, "GetLanguageCode"}, {1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"}, {2, nullptr, "MakeLanguageCode"}, {3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"}, diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index a2472ec4c..4232b6162 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -28,6 +28,7 @@ enum class LanguageCode : u64 { ZH_HANS = 0x00736E61482D687A, ZH_HANT = 0x00746E61482D687A, }; +LanguageCode GetLanguageCodeFromIndex(size_t idx); class SET final : public ServiceFramework<SET> { public: @@ -35,6 +36,7 @@ public: ~SET() = default; private: + void GetLanguageCode(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx); void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx); }; diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index e2a00e4f6..e8ea62f08 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -4,9 +4,11 @@ #pragma once +#include <memory> #include <string> #include <unordered_map> -#include "core/hle/kernel/kernel.h" + +#include "core/hle/kernel/object.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -19,6 +21,8 @@ class SessionRequestHandler; namespace Service::SM { +class Controller; + /// Interface to "sm:" service class SM final : public ServiceFramework<SM> { public: @@ -32,8 +36,6 @@ private: std::shared_ptr<ServiceManager> service_manager; }; -class Controller; - constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(-1); constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1); constexpr ResultCode ERR_INVALID_NAME_SIZE(-1); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 37b58bb77..2172c681b 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -80,8 +80,8 @@ public: {5, nullptr, "GetTimeZoneRuleVersion"}, {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"}, {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"}, - {200, nullptr, "ToPosixTime"}, - {201, nullptr, "ToPosixTimeWithMyRule"}, + {201, nullptr, "ToPosixTime"}, + {202, nullptr, "ToPosixTimeWithMyRule"}, }; RegisterHandlers(functions); } diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp new file mode 100644 index 000000000..e7fb5a419 --- /dev/null +++ b/src/core/hle/service/usb/usb.cpp @@ -0,0 +1,238 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/usb/usb.h" + +namespace Service::USB { + +class IDsInterface final : public ServiceFramework<IDsInterface> { +public: + explicit IDsInterface() : ServiceFramework{"IDsInterface"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "GetDsEndpoint"}, + {1, nullptr, "GetSetupEvent"}, + {2, nullptr, "Unknown"}, + {3, nullptr, "EnableInterface"}, + {4, nullptr, "DisableInterface"}, + {5, nullptr, "CtrlInPostBufferAsync"}, + {6, nullptr, "CtrlOutPostBufferAsync"}, + {7, nullptr, "GetCtrlInCompletionEvent"}, + {8, nullptr, "GetCtrlInReportData"}, + {9, nullptr, "GetCtrlOutCompletionEvent"}, + {10, nullptr, "GetCtrlOutReportData"}, + {11, nullptr, "StallCtrl"}, + {12, nullptr, "AppendConfigurationData"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class USB_DS final : public ServiceFramework<USB_DS> { +public: + explicit USB_DS() : ServiceFramework{"usb:ds"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "BindDevice"}, + {1, nullptr, "BindClientProcess"}, + {2, nullptr, "GetDsInterface"}, + {3, nullptr, "GetStateChangeEvent"}, + {4, nullptr, "GetState"}, + {5, nullptr, "ClearDeviceData"}, + {6, nullptr, "AddUsbStringDescriptor"}, + {7, nullptr, "DeleteUsbStringDescriptor"}, + {8, nullptr, "SetUsbDeviceDescriptor"}, + {9, nullptr, "SetBinaryObjectStore"}, + {10, nullptr, "Enable"}, + {11, nullptr, "Disable"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IClientEpSession final : public ServiceFramework<IClientEpSession> { +public: + explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "PostBufferAsync"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "Unknown7"}, + {8, nullptr, "Unknown8"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IClientIfSession final : public ServiceFramework<IClientIfSession> { +public: + explicit IClientIfSession() : ServiceFramework{"IClientIfSession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "CtrlXferAsync"}, + {6, nullptr, "Unknown6"}, + {7, nullptr, "GetCtrlXferReport"}, + {8, nullptr, "Unknown7"}, + {9, nullptr, "GetClientEpSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class USB_HS final : public ServiceFramework<USB_HS> { +public: + explicit USB_HS() : ServiceFramework{"usb:hs"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "BindClientProcess"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "Unknown2"}, + {3, nullptr, "Unknown3"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "Unknown5"}, + {6, nullptr, "GetInterfaceStateChangeEvent"}, + {7, nullptr, "GetClientIfSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class IPdSession final : public ServiceFramework<IPdSession> { +public: + explicit IPdSession() : ServiceFramework{"IPdSession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "BindNoticeEvent"}, + {1, nullptr, "Unknown1"}, + {2, nullptr, "GetStatus"}, + {3, nullptr, "GetNotice"}, + {4, nullptr, "Unknown2"}, + {5, nullptr, "Unknown3"}, + {6, nullptr, "ReplyPowerRequest"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class USB_PD final : public ServiceFramework<USB_PD> { +public: + explicit USB_PD() : ServiceFramework{"usb:pd"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &USB_PD::GetPdSession, "GetPdSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetPdSession(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IPdSession>(); + + LOG_DEBUG(Service_USB, "called"); + } +}; + +class IPdCradleSession final : public ServiceFramework<IPdCradleSession> { +public: + explicit IPdCradleSession() : ServiceFramework{"IPdCradleSession"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "VdmUserWrite"}, + {1, nullptr, "VdmUserRead"}, + {2, nullptr, "Vdm20Init"}, + {3, nullptr, "GetFwType"}, + {4, nullptr, "GetFwRevision"}, + {5, nullptr, "GetManufacturerId"}, + {6, nullptr, "GetDeviceId"}, + {7, nullptr, "Unknown1"}, + {8, nullptr, "Unknown2"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class USB_PD_C final : public ServiceFramework<USB_PD_C> { +public: + explicit USB_PD_C() : ServiceFramework{"usb:pd:c"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &USB_PD_C::GetPdCradleSession, "GetPdCradleSession"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void GetPdCradleSession(Kernel::HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IPdCradleSession>(); + + LOG_DEBUG(Service_USB, "called"); + } +}; + +class USB_PM final : public ServiceFramework<USB_PM> { +public: + explicit USB_PM() : ServiceFramework{"usb:pm"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<USB_DS>()->InstallAsService(sm); + std::make_shared<USB_HS>()->InstallAsService(sm); + std::make_shared<USB_PD>()->InstallAsService(sm); + std::make_shared<USB_PD_C>()->InstallAsService(sm); + std::make_shared<USB_PM>()->InstallAsService(sm); +} + +} // namespace Service::USB diff --git a/src/core/hle/service/usb/usb.h b/src/core/hle/service/usb/usb.h new file mode 100644 index 000000000..970a11fe8 --- /dev/null +++ b/src/core/hle/service/usb/usb.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::USB { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::USB diff --git a/src/core/hle/service/wlan/wlan.cpp b/src/core/hle/service/wlan/wlan.cpp new file mode 100644 index 000000000..2654594c1 --- /dev/null +++ b/src/core/hle/service/wlan/wlan.cpp @@ -0,0 +1,172 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> + +#include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/wlan/wlan.h" + +namespace Service::WLAN { + +class WLANInfra final : public ServiceFramework<WLANInfra> { +public: + explicit WLANInfra() : ServiceFramework{"wlan:inf"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "GetMacAddress"}, + {3, nullptr, "StartScan"}, + {4, nullptr, "StopScan"}, + {5, nullptr, "Connect"}, + {6, nullptr, "CancelConnect"}, + {7, nullptr, "Disconnect"}, + {8, nullptr, "Unknown3"}, + {9, nullptr, "Unknown4"}, + {10, nullptr, "GetState"}, + {11, nullptr, "GetScanResult"}, + {12, nullptr, "GetRssi"}, + {13, nullptr, "ChangeRxAntenna"}, + {14, nullptr, "Unknown5"}, + {15, nullptr, "Unknown6"}, + {16, nullptr, "RequestWakeUp"}, + {17, nullptr, "RequestIfUpDown"}, + {18, nullptr, "Unknown7"}, + {19, nullptr, "Unknown8"}, + {20, nullptr, "Unknown9"}, + {21, nullptr, "Unknown10"}, + {22, nullptr, "Unknown11"}, + {23, nullptr, "Unknown12"}, + {24, nullptr, "Unknown13"}, + {25, nullptr, "Unknown14"}, + {26, nullptr, "Unknown15"}, + {27, nullptr, "Unknown16"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class WLANLocal final : public ServiceFramework<WLANLocal> { +public: + explicit WLANLocal() : ServiceFramework{"wlan:lcl"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "GetMacAddress"}, + {7, nullptr, "CreateBss"}, + {8, nullptr, "DestroyBss"}, + {9, nullptr, "StartScan"}, + {10, nullptr, "StopScan"}, + {11, nullptr, "Connect"}, + {12, nullptr, "CancelConnect"}, + {13, nullptr, "Join"}, + {14, nullptr, "CancelJoin"}, + {15, nullptr, "Disconnect"}, + {16, nullptr, "SetBeaconLostCount"}, + {17, nullptr, "Unknown7"}, + {18, nullptr, "Unknown8"}, + {19, nullptr, "Unknown9"}, + {20, nullptr, "GetBssIndicationEvent"}, + {21, nullptr, "GetBssIndicationInfo"}, + {22, nullptr, "GetState"}, + {23, nullptr, "GetAllowedChannels"}, + {24, nullptr, "AddIe"}, + {25, nullptr, "DeleteIe"}, + {26, nullptr, "Unknown10"}, + {27, nullptr, "Unknown11"}, + {28, nullptr, "CreateRxEntry"}, + {29, nullptr, "DeleteRxEntry"}, + {30, nullptr, "Unknown12"}, + {31, nullptr, "Unknown13"}, + {32, nullptr, "AddMatchingDataToRxEntry"}, + {33, nullptr, "RemoveMatchingDataFromRxEntry"}, + {34, nullptr, "GetScanResult"}, + {35, nullptr, "Unknown14"}, + {36, nullptr, "SetActionFrameWithBeacon"}, + {37, nullptr, "CancelActionFrameWithBeacon"}, + {38, nullptr, "CreateRxEntryForActionFrame"}, + {39, nullptr, "DeleteRxEntryForActionFrame"}, + {40, nullptr, "Unknown15"}, + {41, nullptr, "Unknown16"}, + {42, nullptr, "CancelGetActionFrame"}, + {43, nullptr, "GetRssi"}, + {44, nullptr, "Unknown17"}, + {45, nullptr, "Unknown18"}, + {46, nullptr, "Unknown19"}, + {47, nullptr, "Unknown20"}, + {48, nullptr, "Unknown21"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class WLANLocalGetFrame final : public ServiceFramework<WLANLocalGetFrame> { +public: + explicit WLANLocalGetFrame() : ServiceFramework{"wlan:lg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class WLANSocketGetFrame final : public ServiceFramework<WLANSocketGetFrame> { +public: + explicit WLANSocketGetFrame() : ServiceFramework{"wlan:sg"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +class WLANSocketManager final : public ServiceFramework<WLANSocketManager> { +public: + explicit WLANSocketManager() : ServiceFramework{"wlan:soc"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "Unknown1"}, + {1, nullptr, "Unknown2"}, + {2, nullptr, "Unknown3"}, + {3, nullptr, "Unknown4"}, + {4, nullptr, "Unknown5"}, + {5, nullptr, "Unknown6"}, + {6, nullptr, "GetMacAddress"}, + {7, nullptr, "SwitchTsfTimerFunction"}, + {8, nullptr, "Unknown7"}, + {9, nullptr, "Unknown8"}, + {10, nullptr, "Unknown9"}, + {11, nullptr, "Unknown10"}, + }; + // clang-format on + + RegisterHandlers(functions); + } +}; + +void InstallInterfaces(SM::ServiceManager& sm) { + std::make_shared<WLANInfra>()->InstallAsService(sm); + std::make_shared<WLANLocal>()->InstallAsService(sm); + std::make_shared<WLANLocalGetFrame>()->InstallAsService(sm); + std::make_shared<WLANSocketGetFrame>()->InstallAsService(sm); + std::make_shared<WLANSocketManager>()->InstallAsService(sm); +} + +} // namespace Service::WLAN diff --git a/src/core/hle/service/wlan/wlan.h b/src/core/hle/service/wlan/wlan.h new file mode 100644 index 000000000..054ea928a --- /dev/null +++ b/src/core/hle/service/wlan/wlan.h @@ -0,0 +1,15 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Service::SM { +class ServiceManager; +} + +namespace Service::WLAN { + +void InstallInterfaces(SM::ServiceManager& sm); + +} // namespace Service::WLAN diff --git a/src/core/hw/aes/ccm.cpp b/src/core/hw/aes/ccm.cpp deleted file mode 100644 index 1ee37aaa4..000000000 --- a/src/core/hw/aes/ccm.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include "common/alignment.h" -#include "common/assert.h" -#include "common/logging/log.h" -#include "core/hw/aes/ccm.h" -#include "core/hw/aes/key.h" - -namespace HW { -namespace AES { - -std::vector<u8> EncryptSignCCM(const std::vector<u8>& pdata, const CCMNonce& nonce, - size_t slot_id) { - UNIMPLEMENTED(); - return {}; -} - -std::vector<u8> DecryptVerifyCCM(const std::vector<u8>& cipher, const CCMNonce& nonce, - size_t slot_id) { - UNIMPLEMENTED(); - return {}; -} - -} // namespace AES -} // namespace HW diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp deleted file mode 100644 index 2f48068c1..000000000 --- a/src/core/hw/hw.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hw/hw.h" -#include "core/hw/lcd.h" - -namespace HW { - -template <typename T> -inline void Read(T& var, const u32 addr) { - switch (addr & 0xFFFFF000) { - case VADDR_GPU: - case VADDR_GPU + 0x1000: - case VADDR_GPU + 0x2000: - case VADDR_GPU + 0x3000: - case VADDR_GPU + 0x4000: - case VADDR_GPU + 0x5000: - case VADDR_GPU + 0x6000: - case VADDR_GPU + 0x7000: - case VADDR_GPU + 0x8000: - case VADDR_GPU + 0x9000: - case VADDR_GPU + 0xA000: - case VADDR_GPU + 0xB000: - case VADDR_GPU + 0xC000: - case VADDR_GPU + 0xD000: - case VADDR_GPU + 0xE000: - case VADDR_GPU + 0xF000: - break; - case VADDR_LCD: - LCD::Read(var, addr); - break; - default: - LOG_ERROR(HW_Memory, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); - break; - } -} - -template <typename T> -inline void Write(u32 addr, const T data) { - switch (addr & 0xFFFFF000) { - case VADDR_GPU: - case VADDR_GPU + 0x1000: - case VADDR_GPU + 0x2000: - case VADDR_GPU + 0x3000: - case VADDR_GPU + 0x4000: - case VADDR_GPU + 0x5000: - case VADDR_GPU + 0x6000: - case VADDR_GPU + 0x7000: - case VADDR_GPU + 0x8000: - case VADDR_GPU + 0x9000: - case VADDR_GPU + 0xA000: - case VADDR_GPU + 0xB000: - case VADDR_GPU + 0xC000: - case VADDR_GPU + 0xD000: - case VADDR_GPU + 0xE000: - case VADDR_GPU + 0xF000: - break; - case VADDR_LCD: - LCD::Write(addr, data); - break; - default: - LOG_ERROR(HW_Memory, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); - break; - } -} - -// Explicitly instantiate template functions because we aren't defining this in the header: - -template void Read<u64>(u64& var, const u32 addr); -template void Read<u32>(u32& var, const u32 addr); -template void Read<u16>(u16& var, const u32 addr); -template void Read<u8>(u8& var, const u32 addr); - -template void Write<u64>(u32 addr, const u64 data); -template void Write<u32>(u32 addr, const u32 data); -template void Write<u16>(u32 addr, const u16 data); -template void Write<u8>(u32 addr, const u8 data); - -/// Update hardware -void Update() {} - -/// Initialize hardware -void Init() { - LCD::Init(); - LOG_DEBUG(HW, "Initialized OK"); -} - -/// Shutdown hardware -void Shutdown() { - LCD::Shutdown(); - LOG_DEBUG(HW, "Shutdown OK"); -} -} // namespace HW diff --git a/src/core/hw/hw.h b/src/core/hw/hw.h deleted file mode 100644 index 5890d2b5c..000000000 --- a/src/core/hw/hw.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -namespace HW { - -/// Beginnings of IO register regions, in the user VA space. -enum : u32 { - VADDR_HASH = 0x1EC01000, - VADDR_CSND = 0x1EC03000, - VADDR_DSP = 0x1EC40000, - VADDR_PDN = 0x1EC41000, - VADDR_CODEC = 0x1EC41000, - VADDR_SPI = 0x1EC42000, - VADDR_SPI_2 = 0x1EC43000, // Only used under TWL_FIRM? - VADDR_I2C = 0x1EC44000, - VADDR_CODEC_2 = 0x1EC45000, - VADDR_HID = 0x1EC46000, - VADDR_GPIO = 0x1EC47000, - VADDR_I2C_2 = 0x1EC48000, - VADDR_SPI_3 = 0x1EC60000, - VADDR_I2C_3 = 0x1EC61000, - VADDR_MIC = 0x1EC62000, - VADDR_PXI = 0x1EC63000, - VADDR_LCD = 0x1ED02000, - VADDR_DSP_2 = 0x1ED03000, - VADDR_HASH_2 = 0x1EE01000, - VADDR_GPU = 0x1EF00000, -}; - -template <typename T> -void Read(T& var, const u32 addr); - -template <typename T> -void Write(u32 addr, const T data); - -/// Update hardware -void Update(); - -/// Initialize hardware -void Init(); - -/// Shutdown hardware -void Shutdown(); - -} // namespace HW diff --git a/src/core/hw/lcd.cpp b/src/core/hw/lcd.cpp deleted file mode 100644 index 0b62174d5..000000000 --- a/src/core/hw/lcd.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cstring> -#include "common/common_types.h" -#include "common/logging/log.h" -#include "core/hw/hw.h" -#include "core/hw/lcd.h" -#include "core/tracer/recorder.h" - -namespace LCD { - -Regs g_regs; - -template <typename T> -inline void Read(T& var, const u32 raw_addr) { - u32 addr = raw_addr - HW::VADDR_LCD; - u32 index = addr / 4; - - // Reads other than u32 are untested, so I'd rather have them abort than silently fail - if (index >= 0x400 || !std::is_same<T, u32>::value) { - LOG_ERROR(HW_LCD, "Unknown Read{} @ 0x{:08X}", sizeof(var) * 8, addr); - return; - } - - var = g_regs[index]; -} - -template <typename T> -inline void Write(u32 addr, const T data) { - addr -= HW::VADDR_LCD; - u32 index = addr / 4; - - // Writes other than u32 are untested, so I'd rather have them abort than silently fail - if (index >= 0x400 || !std::is_same<T, u32>::value) { - LOG_ERROR(HW_LCD, "Unknown Write{} 0x{:08X} @ 0x{:08X}", sizeof(data) * 8, data, addr); - return; - } - - g_regs[index] = static_cast<u32>(data); -} - -// Explicitly instantiate template functions because we aren't defining this in the header: - -template void Read<u64>(u64& var, const u32 addr); -template void Read<u32>(u32& var, const u32 addr); -template void Read<u16>(u16& var, const u32 addr); -template void Read<u8>(u8& var, const u32 addr); - -template void Write<u64>(u32 addr, const u64 data); -template void Write<u32>(u32 addr, const u32 data); -template void Write<u16>(u32 addr, const u16 data); -template void Write<u8>(u32 addr, const u8 data); - -/// Initialize hardware -void Init() { - memset(&g_regs, 0, sizeof(g_regs)); - LOG_DEBUG(HW_LCD, "Initialized OK"); -} - -/// Shutdown hardware -void Shutdown() { - LOG_DEBUG(HW_LCD, "Shutdown OK"); -} - -} // namespace LCD diff --git a/src/core/hw/lcd.h b/src/core/hw/lcd.h deleted file mode 100644 index d2db9700f..000000000 --- a/src/core/hw/lcd.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <cstddef> -#include <type_traits> -#include "common/bit_field.h" -#include "common/common_funcs.h" -#include "common/common_types.h" - -#define LCD_REG_INDEX(field_name) (offsetof(LCD::Regs, field_name) / sizeof(u32)) - -namespace LCD { - -struct Regs { - - union ColorFill { - u32 raw; - - BitField<0, 8, u32> color_r; - BitField<8, 8, u32> color_g; - BitField<16, 8, u32> color_b; - BitField<24, 1, u32> is_enabled; - }; - - INSERT_PADDING_WORDS(0x81); - ColorFill color_fill_top; - INSERT_PADDING_WORDS(0xE); - u32 backlight_top; - - INSERT_PADDING_WORDS(0x1F0); - - ColorFill color_fill_bottom; - INSERT_PADDING_WORDS(0xE); - u32 backlight_bottom; - INSERT_PADDING_WORDS(0x16F); - - static constexpr size_t NumIds() { - return sizeof(Regs) / sizeof(u32); - } - - const u32& operator[](int index) const { - const u32* content = reinterpret_cast<const u32*>(this); - return content[index]; - } - - u32& operator[](int index) { - u32* content = reinterpret_cast<u32*>(this); - return content[index]; - } -}; -static_assert(std::is_standard_layout<Regs>::value, "Structure does not use standard layout"); - -// TODO: MSVC does not support using offsetof() on non-static data members even though this -// is technically allowed since C++11. This macro should be enabled once MSVC adds -// support for that. -#ifndef _MSC_VER -#define ASSERT_REG_POSITION(field_name, position) \ - static_assert(offsetof(Regs, field_name) == position * 4, \ - "Field " #field_name " has invalid position") - -ASSERT_REG_POSITION(color_fill_top, 0x81); -ASSERT_REG_POSITION(backlight_top, 0x90); -ASSERT_REG_POSITION(color_fill_bottom, 0x281); -ASSERT_REG_POSITION(backlight_bottom, 0x290); - -#undef ASSERT_REG_POSITION -#endif // !defined(_MSC_VER) - -extern Regs g_regs; - -template <typename T> -void Read(T& var, const u32 addr); - -template <typename T> -void Write(u32 addr, const T data); - -/// Initialize hardware -void Init(); - -/// Shutdown hardware -void Shutdown(); - -} // namespace LCD diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index b0277a875..9a8cdd0ff 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -20,6 +20,10 @@ namespace Loader { AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} +AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory( + FileSys::VirtualDir directory) + : AppLoader(directory->GetFile("main")), dir(std::move(directory)) {} + FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { return FileType::DeconstructedRomDirectory; @@ -34,7 +38,12 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( return ResultStatus::ErrorAlreadyLoaded; } - const FileSys::VirtualDir dir = file->GetContainingDirectory(); + if (dir == nullptr) { + if (file == nullptr) + return ResultStatus::ErrorInvalidFormat; + dir = file->GetContainingDirectory(); + } + const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); if (npdm == nullptr) return ResultStatus::ErrorInvalidFormat; diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 982a037f7..7d5433563 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -7,7 +7,7 @@ #include <string> #include "common/common_types.h" #include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/loader/loader.h" namespace Loader { @@ -22,6 +22,9 @@ class AppLoader_DeconstructedRomDirectory final : public AppLoader { public: explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); + // Overload to accept exefs directory. Must contain 'main' and 'main.npdm' + explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory); + /** * Returns the type of the file * @param file std::shared_ptr<VfsFile> open file @@ -40,6 +43,7 @@ public: private: FileSys::ProgramMetadata metadata; FileSys::VirtualFile romfs; + FileSys::VirtualDir dir; }; } // namespace Loader diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 352938dcb..a7133f5a6 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -311,11 +311,11 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { 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->code; + codeset_segment = &codeset->CodeSegment(); } else if (permission_flags == (PF_R)) { - codeset_segment = &codeset->rodata; + codeset_segment = &codeset->RODataSegment(); } else if (permission_flags == (PF_R | PF_W)) { - codeset_segment = &codeset->data; + codeset_segment = &codeset->DataSegment(); } else { LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, p->p_flags); diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index cbc4177c6..57e6c0365 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -13,6 +13,7 @@ #include "core/loader/nca.h" #include "core/loader/nro.h" #include "core/loader/nso.h" +#include "core/loader/xci.h" namespace Loader { @@ -35,6 +36,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) { CHECK_TYPE(NSO) CHECK_TYPE(NRO) CHECK_TYPE(NCA) + CHECK_TYPE(XCI) #undef CHECK_TYPE @@ -60,6 +62,8 @@ FileType GuessFromFilename(const std::string& name) { return FileType::NSO; if (extension == "nca") return FileType::NCA; + if (extension == "xci") + return FileType::XCI; return FileType::Unknown; } @@ -74,6 +78,8 @@ const char* GetFileTypeString(FileType type) { return "NSO"; case FileType::NCA: return "NCA"; + case FileType::XCI: + return "XCI"; case FileType::DeconstructedRomDirectory: return "Directory"; case FileType::Error: @@ -111,6 +117,9 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT case FileType::NCA: return std::make_unique<AppLoader_NCA>(std::move(file)); + case FileType::XCI: + return std::make_unique<AppLoader_XCI>(std::move(file)); + // NX deconstructed ROM directory. case FileType::DeconstructedRomDirectory: return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index fbf11e5d0..e69ab85ef 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -14,7 +14,7 @@ #include "common/common_types.h" #include "common/file_util.h" #include "core/file_sys/vfs.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" namespace Kernel { struct AddressMapping; @@ -31,6 +31,7 @@ enum class FileType { NSO, NRO, NCA, + XCI, DeconstructedRomDirectory, }; @@ -72,7 +73,8 @@ enum class ResultStatus { ErrorNotUsed, ErrorAlreadyLoaded, ErrorMemoryAllocationFailed, - ErrorEncrypted, + ErrorMissingKeys, + ErrorDecrypting, ErrorUnsupportedArch, }; diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index c80df23be..dbc67c0b5 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -22,15 +22,14 @@ namespace Loader { -AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} +AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_) + : AppLoader(std::move(file_)), nca(std::make_unique<FileSys::NCA>(file)) {} FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { - // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. - FileSys::NCAHeader header{}; - if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header)) - return FileType::Error; + FileSys::NCA nca(file); - if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program) + if (nca.GetStatus() == ResultStatus::Success && + nca.GetType() == FileSys::NCAContentType::Program) return FileType::NCA; return FileType::Error; @@ -41,8 +40,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { return ResultStatus::ErrorAlreadyLoaded; } - nca = std::make_unique<FileSys::NCA>(file); - ResultStatus result = nca->GetStatus(); + const auto result = nca->GetStatus(); if (result != ResultStatus::Success) { return result; } @@ -50,44 +48,16 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { if (nca->GetType() != FileSys::NCAContentType::Program) return ResultStatus::ErrorInvalidFormat; - auto exefs = nca->GetExeFS(); + const auto exefs = nca->GetExeFS(); if (exefs == nullptr) return ResultStatus::ErrorInvalidFormat; - result = metadata.Load(exefs->GetFile("main.npdm")); - if (result != ResultStatus::Success) { - return result; - } - metadata.Print(); - - const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; - if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) { - return ResultStatus::ErrorUnsupportedArch; - } + directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs); - VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; - for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", - "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { - const VAddr load_addr = next_load_addr; - - next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr); - if (next_load_addr) { - LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); - // Register module with GDBStub - GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); - } else { - next_load_addr = load_addr; - } - } - - process->program_id = metadata.GetTitleID(); - process->svc_access_mask.set(); - process->address_mappings = default_address_mappings; - process->resource_limit = - Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), - metadata.GetMainThreadStackSize()); + const auto load_result = directory_loader->Load(process); + if (load_result != ResultStatus::Success) + return load_result; if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); @@ -98,12 +68,21 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { } ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { - if (nca == nullptr || nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) + if (nca == nullptr) + return ResultStatus::ErrorNotLoaded; + if (nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) return ResultStatus::ErrorNotUsed; dir = nca->GetRomFS(); return ResultStatus::Success; } +ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { + if (nca == nullptr) + return ResultStatus::ErrorNotLoaded; + out_program_id = nca->GetTitleId(); + return ResultStatus::Success; +} + AppLoader_NCA::~AppLoader_NCA() = default; } // namespace Loader diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 52c95953a..0fd2d0417 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -8,8 +8,9 @@ #include "common/common_types.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/loader/loader.h" +#include "deconstructed_rom_directory.h" namespace Loader { @@ -33,12 +34,15 @@ public: ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + ResultStatus ReadProgramId(u64& out_program_id) override; + ~AppLoader_NCA(); private: FileSys::ProgramMetadata metadata; std::unique_ptr<FileSys::NCA> nca; + std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; }; } // namespace Loader diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 7d3ec2a76..dc053cdad 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -159,7 +159,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { // Resize program image to include .bss section and page align each section bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); } - codeset->data.size += bss_size; + codeset->DataSegment().size += bss_size; program_image.resize(static_cast<u32>(program_image.size()) + bss_size); // Load codeset for current process diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 04a0f497e..bb01c9e25 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -6,7 +6,7 @@ #include <string> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/loader/linker.h" #include "core/loader/loader.h" diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 06b1b33f4..fee7d58c6 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -127,7 +127,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { // Resize program image to include .bss section and page align each section bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); } - codeset->data.size += bss_size; + codeset->DataSegment().size += bss_size; const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; program_image.resize(image_size); diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 3f7567500..aaeb1f2a9 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -6,7 +6,7 @@ #include <string> #include "common/common_types.h" -#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/object.h" #include "core/loader/linker.h" #include "core/loader/loader.h" diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp new file mode 100644 index 000000000..eb4dee2c2 --- /dev/null +++ b/src/core/loader/xci.cpp @@ -0,0 +1,74 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <vector> + +#include "common/file_util.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "common/swap.h" +#include "core/core.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/program_metadata.h" +#include "core/file_sys/romfs.h" +#include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/nso.h" +#include "core/loader/xci.h" +#include "core/memory.h" + +namespace Loader { + +AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) + : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), + nca_loader(std::make_unique<AppLoader_NCA>( + xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {} + +AppLoader_XCI::~AppLoader_XCI() = default; + +FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) { + FileSys::XCI xci(file); + + if (xci.GetStatus() == ResultStatus::Success && + xci.GetNCAByType(FileSys::NCAContentType::Program) != nullptr && + AppLoader_NCA::IdentifyType(xci.GetNCAFileByType(FileSys::NCAContentType::Program)) == + FileType::NCA) { + return FileType::XCI; + } + + return FileType::Error; +} + +ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) { + if (is_loaded) { + return ResultStatus::ErrorAlreadyLoaded; + } + + if (xci->GetNCAFileByType(FileSys::NCAContentType::Program) == nullptr) { + if (!Core::Crypto::KeyManager::KeyFileExists(false)) + return ResultStatus::ErrorMissingKeys; + return ResultStatus::ErrorDecrypting; + } + + auto result = nca_loader->Load(process); + if (result != ResultStatus::Success) + return result; + + is_loaded = true; + + return ResultStatus::Success; +} + +ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& dir) { + return nca_loader->ReadRomFS(dir); +} + +ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { + return nca_loader->ReadProgramId(out_program_id); +} + +} // namespace Loader diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h new file mode 100644 index 000000000..0dbcfbdf8 --- /dev/null +++ b/src/core/loader/xci.h @@ -0,0 +1,44 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "common/common_types.h" +#include "core/file_sys/card_image.h" +#include "core/loader/loader.h" +#include "core/loader/nca.h" + +namespace Loader { + +/// Loads an XCI file +class AppLoader_XCI final : public AppLoader { +public: + explicit AppLoader_XCI(FileSys::VirtualFile file); + ~AppLoader_XCI(); + + /** + * Returns the type of the file + * @param file std::shared_ptr<VfsFile> open file + * @return FileType found, or FileType::Error if this loader doesn't know it + */ + static FileType IdentifyType(const FileSys::VirtualFile& file); + + FileType GetFileType() override { + return IdentifyType(file); + } + + ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + + ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + ResultStatus ReadProgramId(u64& out_program_id) override; + +private: + FileSys::ProgramMetadata metadata; + + std::unique_ptr<FileSys::XCI> xci; + std::unique_ptr<AppLoader_NCA> nca_loader; +}; + +} // namespace Loader diff --git a/src/core/memory.cpp b/src/core/memory.cpp index e753e3436..1133bcbaf 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -14,7 +14,6 @@ #include "common/swap.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/lock.h" #include "core/memory.h" @@ -24,8 +23,6 @@ namespace Memory { -static std::array<u8, Memory::VRAM_SIZE> vram; - static PageTable* current_page_table = nullptr; void SetCurrentPageTable(PageTable* page_table) { @@ -102,22 +99,6 @@ void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPoin } /** - * This function should only be called for virtual addreses with attribute `PageType::Special`. - */ -static std::set<MemoryHookPointer> GetSpecialHandlers(const PageTable& page_table, VAddr vaddr, - u64 size) { - std::set<MemoryHookPointer> result; - auto interval = boost::icl::discrete_interval<VAddr>::closed(vaddr, vaddr + size - 1); - auto interval_list = page_table.special_regions.equal_range(interval); - for (auto it = interval_list.first; it != interval_list.second; ++it) { - for (const auto& region : it->second) { - result.insert(region.handler); - } - } - return result; -} - -/** * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) * using a VMA from the current process */ @@ -242,10 +223,6 @@ bool IsKernelVirtualAddress(const VAddr vaddr) { return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END; } -bool IsValidPhysicalAddress(const PAddr paddr) { - return GetPhysicalPointer(paddr) != nullptr; -} - u8* GetPointer(const VAddr vaddr) { u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { @@ -274,61 +251,6 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) { return string; } -u8* GetPhysicalPointer(PAddr address) { - struct MemoryArea { - PAddr paddr_base; - u32 size; - }; - - static constexpr MemoryArea memory_areas[] = { - {VRAM_PADDR, VRAM_SIZE}, - {IO_AREA_PADDR, IO_AREA_SIZE}, - {DSP_RAM_PADDR, DSP_RAM_SIZE}, - {FCRAM_PADDR, FCRAM_N3DS_SIZE}, - }; - - const auto area = - std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) { - return address >= area.paddr_base && address < area.paddr_base + area.size; - }); - - if (area == std::end(memory_areas)) { - LOG_ERROR(HW_Memory, "Unknown GetPhysicalPointer @ 0x{:016X}", address); - return nullptr; - } - - if (area->paddr_base == IO_AREA_PADDR) { - LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr={:016X}", address); - return nullptr; - } - - u64 offset_into_region = address - area->paddr_base; - - u8* target_pointer = nullptr; - switch (area->paddr_base) { - case VRAM_PADDR: - target_pointer = vram.data() + offset_into_region; - break; - case DSP_RAM_PADDR: - break; - case FCRAM_PADDR: - for (const auto& region : Kernel::memory_regions) { - if (offset_into_region >= region.base && - offset_into_region < region.base + region.size) { - target_pointer = - region.linear_heap_memory->data() + offset_into_region - region.base; - break; - } - } - ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address"); - break; - default: - UNREACHABLE(); - } - - return target_pointer; -} - void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached) { if (gpu_addr == 0) { return; @@ -404,43 +326,45 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached) } void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { + auto& system_instance = Core::System::GetInstance(); + // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be // null here - if (VideoCore::g_renderer == nullptr) { + if (!system_instance.IsPoweredOn()) { return; } VAddr end = start + size; - auto CheckRegion = [&](VAddr region_start, VAddr region_end) { + const auto CheckRegion = [&](VAddr region_start, VAddr region_end) { if (start >= region_end || end <= region_start) { // No overlap with region return; } - VAddr overlap_start = std::max(start, region_start); - VAddr overlap_end = std::min(end, region_end); + const VAddr overlap_start = std::max(start, region_start); + const VAddr overlap_end = std::min(end, region_end); - std::vector<Tegra::GPUVAddr> gpu_addresses = - Core::System::GetInstance().GPU().memory_manager->CpuToGpuAddress(overlap_start); + const std::vector<Tegra::GPUVAddr> gpu_addresses = + system_instance.GPU().memory_manager->CpuToGpuAddress(overlap_start); if (gpu_addresses.empty()) { return; } - u64 overlap_size = overlap_end - overlap_start; + const u64 overlap_size = overlap_end - overlap_start; for (const auto& gpu_address : gpu_addresses) { - auto* rasterizer = VideoCore::g_renderer->Rasterizer(); + auto& rasterizer = system_instance.Renderer().Rasterizer(); switch (mode) { case FlushMode::Flush: - rasterizer->FlushRegion(gpu_address, overlap_size); + rasterizer.FlushRegion(gpu_address, overlap_size); break; case FlushMode::Invalidate: - rasterizer->InvalidateRegion(gpu_address, overlap_size); + rasterizer.InvalidateRegion(gpu_address, overlap_size); break; case FlushMode::FlushAndInvalidate: - rasterizer->FlushAndInvalidateRegion(gpu_address, overlap_size); + rasterizer.FlushAndInvalidateRegion(gpu_address, overlap_size); break; } } @@ -666,48 +590,4 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size) { CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); } -boost::optional<PAddr> TryVirtualToPhysicalAddress(const VAddr addr) { - if (addr == 0) { - return 0; - } else if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { - return addr - VRAM_VADDR + VRAM_PADDR; - } else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { - return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR; - } else if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { - return addr - NEW_LINEAR_HEAP_VADDR + FCRAM_PADDR; - } else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) { - return addr - DSP_RAM_VADDR + DSP_RAM_PADDR; - } else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) { - return addr - IO_AREA_VADDR + IO_AREA_PADDR; - } - - return boost::none; -} - -PAddr VirtualToPhysicalAddress(const VAddr addr) { - auto paddr = TryVirtualToPhysicalAddress(addr); - if (!paddr) { - LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x{:016X}", addr); - // To help with debugging, set bit on address so that it's obviously invalid. - return addr | 0x80000000; - } - return *paddr; -} - -boost::optional<VAddr> PhysicalToVirtualAddress(const PAddr addr) { - if (addr == 0) { - return 0; - } else if (addr >= VRAM_PADDR && addr < VRAM_PADDR_END) { - return addr - VRAM_PADDR + VRAM_VADDR; - } else if (addr >= FCRAM_PADDR && addr < FCRAM_PADDR_END) { - return addr - FCRAM_PADDR + Core::CurrentProcess()->GetLinearHeapAreaAddress(); - } else if (addr >= DSP_RAM_PADDR && addr < DSP_RAM_PADDR_END) { - return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; - } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { - return addr - IO_AREA_PADDR + IO_AREA_VADDR; - } - - return boost::none; -} - } // namespace Memory diff --git a/src/core/memory.h b/src/core/memory.h index 8d5d017a4..b7fb3b9ed 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -6,12 +6,9 @@ #include <array> #include <cstddef> -#include <map> #include <string> #include <tuple> -#include <vector> #include <boost/icl/interval_map.hpp> -#include <boost/optional.hpp> #include "common/common_types.h" #include "core/memory_hook.h" #include "video_core/memory_manager.h" @@ -85,40 +82,6 @@ struct PageTable { std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; }; -/// Physical memory regions as seen from the ARM11 -enum : PAddr { - /// IO register area - IO_AREA_PADDR = 0x10100000, - IO_AREA_SIZE = 0x01000000, ///< IO area size (16MB) - IO_AREA_PADDR_END = IO_AREA_PADDR + IO_AREA_SIZE, - - /// MPCore internal memory region - MPCORE_RAM_PADDR = 0x17E00000, - MPCORE_RAM_SIZE = 0x00002000, ///< MPCore internal memory size (8KB) - MPCORE_RAM_PADDR_END = MPCORE_RAM_PADDR + MPCORE_RAM_SIZE, - - /// Video memory - VRAM_PADDR = 0x18000000, - VRAM_SIZE = 0x00600000, ///< VRAM size (6MB) - VRAM_PADDR_END = VRAM_PADDR + VRAM_SIZE, - - /// DSP memory - DSP_RAM_PADDR = 0x1FF00000, - DSP_RAM_SIZE = 0x00080000, ///< DSP memory size (512KB) - DSP_RAM_PADDR_END = DSP_RAM_PADDR + DSP_RAM_SIZE, - - /// AXI WRAM - AXI_WRAM_PADDR = 0x1FF80000, - AXI_WRAM_SIZE = 0x00080000, ///< AXI WRAM size (512KB) - AXI_WRAM_PADDR_END = AXI_WRAM_PADDR + AXI_WRAM_SIZE, - - /// Main FCRAM - FCRAM_PADDR = 0x20000000, - FCRAM_SIZE = 0x08000000, ///< FCRAM size on the Old 3DS (128MB) - FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB) - FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE, -}; - /// Virtual user-space memory regions enum : VAddr { /// Where the application text, data and bss reside. @@ -126,24 +89,6 @@ enum : VAddr { PROCESS_IMAGE_MAX_SIZE = 0x08000000, PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, - /// Maps 1:1 to an offset in FCRAM. Used for HW allocations that need to be linear in physical - /// memory. - LINEAR_HEAP_VADDR = 0x14000000, - LINEAR_HEAP_SIZE = 0x08000000, - LINEAR_HEAP_VADDR_END = LINEAR_HEAP_VADDR + LINEAR_HEAP_SIZE, - - /// Maps 1:1 to the IO register area. - IO_AREA_VADDR = 0x1EC00000, - IO_AREA_VADDR_END = IO_AREA_VADDR + IO_AREA_SIZE, - - /// Maps 1:1 to VRAM. - VRAM_VADDR = 0x1F000000, - VRAM_VADDR_END = VRAM_VADDR + VRAM_SIZE, - - /// Maps 1:1 to DSP memory. - DSP_RAM_VADDR = 0x1FF00000, - DSP_RAM_VADDR_END = DSP_RAM_VADDR + DSP_RAM_SIZE, - /// Read-only page containing kernel and system configuration values. CONFIG_MEMORY_VADDR = 0x1FF80000, CONFIG_MEMORY_SIZE = 0x00001000, @@ -154,13 +99,8 @@ enum : VAddr { SHARED_PAGE_SIZE = 0x00001000, SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE, - /// Equivalent to LINEAR_HEAP_VADDR, but expanded to cover the extra memory in the New 3DS. - NEW_LINEAR_HEAP_VADDR = 0x30000000, - NEW_LINEAR_HEAP_SIZE = 0x10000000, - NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, - /// Area where TLS (Thread-Local Storage) buffers are allocated. - TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END, + TLS_AREA_VADDR = 0x40000000, TLS_ENTRY_SIZE = 0x200, TLS_AREA_SIZE = 0x10000000, TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, @@ -200,12 +140,10 @@ void SetCurrentPageTable(PageTable* page_table); PageTable* GetCurrentPageTable(); /// Determines if the given VAddr is valid for the specified process. -bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); -bool IsValidVirtualAddress(const VAddr addr); +bool IsValidVirtualAddress(const Kernel::Process& process, VAddr vaddr); +bool IsValidVirtualAddress(VAddr vaddr); /// Determines if the given VAddr is a kernel address -bool IsKernelVirtualAddress(const VAddr addr); - -bool IsValidPhysicalAddress(const PAddr addr); +bool IsKernelVirtualAddress(VAddr vaddr); u8 Read8(VAddr addr); u16 Read16(VAddr addr); @@ -217,42 +155,17 @@ void Write16(VAddr addr, u16 data); void Write32(VAddr addr, u32 data); void Write64(VAddr addr, u64 data); -void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, - size_t size); -void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); -void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, +void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size); +void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size); +void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, size_t size); -void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); -void ZeroBlock(const VAddr dest_addr, const size_t size); +void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size); +void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size); void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); -u8* GetPointer(VAddr virtual_address); - -std::string ReadCString(VAddr virtual_address, std::size_t max_length); - -/** - * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical - * address. This should be used by services to translate addresses for use by the hardware. - */ -boost::optional<PAddr> TryVirtualToPhysicalAddress(VAddr addr); - -/** - * Converts a virtual address inside a region with 1:1 mapping to physical memory to a physical - * address. This should be used by services to translate addresses for use by the hardware. - * - * @deprecated Use TryVirtualToPhysicalAddress(), which reports failure. - */ -PAddr VirtualToPhysicalAddress(VAddr addr); +u8* GetPointer(VAddr vaddr); -/** - * Undoes a mapping performed by VirtualToPhysicalAddress(). - */ -boost::optional<VAddr> PhysicalToVirtualAddress(PAddr addr); - -/** - * Gets a pointer to the memory region beginning at the specified physical address. - */ -u8* GetPhysicalPointer(PAddr address); +std::string ReadCString(VAddr vaddr, std::size_t max_length); enum class FlushMode { /// Write back modified surfaces to RAM @@ -266,7 +179,7 @@ enum class FlushMode { /** * Mark each page touching the region as cached. */ -void RasterizerMarkRegionCached(Tegra::GPUVAddr start, u64 size, bool cached); +void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached); /** * Flushes and invalidates any externally cached rasterizer resources touching the given virtual diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 5f53b16d3..8e09b9b63 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -40,22 +40,21 @@ void PerfStats::EndGameFrame() { game_frames += 1; } -PerfStats::Results PerfStats::GetAndResetStats(u64 current_system_time_us) { +PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) { std::lock_guard<std::mutex> lock(object_mutex); - auto now = Clock::now(); + const auto now = Clock::now(); // Walltime elapsed since stats were reset - auto interval = duration_cast<DoubleSecs>(now - reset_point).count(); + const auto interval = duration_cast<DoubleSecs>(now - reset_point).count(); - auto system_us_per_second = - static_cast<double>(current_system_time_us - reset_point_system_us) / interval; + const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; Results results{}; results.system_fps = static_cast<double>(system_frames) / interval; results.game_fps = static_cast<double>(game_frames) / interval; results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / static_cast<double>(system_frames); - results.emulation_speed = system_us_per_second / 1'000'000.0; + results.emulation_speed = system_us_per_second.count() / 1'000'000.0; // Reset counters reset_point = now; @@ -74,10 +73,10 @@ double PerfStats::GetLastFrameTimeScale() { return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; } -void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { +void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher // values increase the time needed to recover and limit framerate again after spikes. - constexpr microseconds MAX_LAG_TIME_US = 25ms; + constexpr microseconds MAX_LAG_TIME_US = 25us; if (!Settings::values.toggle_framelimit) { return; @@ -85,7 +84,7 @@ void FrameLimiter::DoFrameLimiting(u64 current_system_time_us) { auto now = Clock::now(); - frame_limiting_delta_err += microseconds(current_system_time_us - previous_system_time_us); + frame_limiting_delta_err += current_system_time_us - previous_system_time_us; frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); frame_limiting_delta_err = std::clamp(frame_limiting_delta_err, -MAX_LAG_TIME_US, MAX_LAG_TIME_US); diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index 362b205c8..6e4619701 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -33,7 +33,7 @@ public: void EndSystemFrame(); void EndGameFrame(); - Results GetAndResetStats(u64 current_system_time_us); + Results GetAndResetStats(std::chrono::microseconds current_system_time_us); /** * Gets the ratio between walltime and the emulated time of the previous system frame. This is @@ -47,7 +47,7 @@ private: /// Point when the cumulative counters were reset Clock::time_point reset_point = Clock::now(); /// System time when the cumulative counters were reset - u64 reset_point_system_us = 0; + std::chrono::microseconds reset_point_system_us{0}; /// Cumulative duration (excluding v-sync/frame-limiting) of frames since last reset Clock::duration accumulated_frametime = Clock::duration::zero(); @@ -68,11 +68,11 @@ class FrameLimiter { public: using Clock = std::chrono::high_resolution_clock; - void DoFrameLimiting(u64 current_system_time_us); + void DoFrameLimiting(std::chrono::microseconds current_system_time_us); private: /// Emulated system time (in microseconds) at the last limiter invocation - u64 previous_system_time_us = 0; + std::chrono::microseconds previous_system_time_us{0}; /// Walltime at the last limiter invocation Clock::time_point previous_walltime = Clock::now(); diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 444bcc387..a4623223d 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -2,13 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/core.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/service/hid/hid.h" #include "core/settings.h" +#include "video_core/renderer_base.h" #include "video_core/video_core.h" -#include "core/frontend/emu_window.h" - namespace Settings { Values values = {}; @@ -20,9 +20,9 @@ void Apply() { VideoCore::g_toggle_framelimit_enabled = values.toggle_framelimit; - if (VideoCore::g_emu_window) { - auto layout = VideoCore::g_emu_window->GetFramebufferLayout(); - VideoCore::g_emu_window->UpdateCurrentFramebufferLayout(layout.width, layout.height); + auto& system_instance = Core::System::GetInstance(); + if (system_instance.IsPoweredOn()) { + system_instance.Renderer().UpdateCurrentFramebufferLayout(); } Service::HID::ReloadInputDevices(); diff --git a/src/core/settings.h b/src/core/settings.h index 7150d9755..73dc3061f 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -112,6 +112,8 @@ static const std::array<const char*, NumAnalogs> mapping = {{ struct Values { // System bool use_docked_mode; + std::string username; + int language_index; // Controls std::array<std::string, NativeButton::NumButtons> buttons; @@ -137,6 +139,13 @@ struct Values { std::string log_filter; + bool use_dev_keys; + + // Audio + std::string sink_id; + std::string audio_device_id; + float volume; + // Debugging bool use_gdbstub; u16 gdbstub_port; |