diff options
Diffstat (limited to 'src/core')
46 files changed, 1138 insertions, 295 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f6e082c36..f67f1ce92 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -497,10 +497,6 @@ add_library(core STATIC hle/service/hid/irsensor/processor_base.h hle/service/hid/irsensor/tera_plugin_processor.cpp hle/service/hid/irsensor/tera_plugin_processor.h - hle/service/jit/jit_context.cpp - hle/service/jit/jit_context.h - hle/service/jit/jit.cpp - hle/service/jit/jit.h hle/service/lbl/lbl.cpp hle/service/lbl/lbl.h hle/service/ldn/lan_discovery.cpp @@ -805,14 +801,18 @@ if (ENABLE_WEB_SERVICE) target_link_libraries(core PRIVATE web_service) endif() -if (ARCHITECTURE_x86_64) +if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) target_sources(core PRIVATE - arm/dynarmic/arm_dynarmic_32.cpp - arm/dynarmic/arm_dynarmic_32.h arm/dynarmic/arm_dynarmic_64.cpp arm/dynarmic/arm_dynarmic_64.h + arm/dynarmic/arm_dynarmic_32.cpp + arm/dynarmic/arm_dynarmic_32.h arm/dynarmic/arm_dynarmic_cp15.cpp arm/dynarmic/arm_dynarmic_cp15.h + hle/service/jit/jit_context.cpp + hle/service/jit/jit_context.h + hle/service/jit/jit.cpp + hle/service/jit/jit.h ) target_link_libraries(core PRIVATE dynarmic) endif() diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 287ba102e..227e06ea1 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -301,6 +301,11 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* } } +#ifdef ARCHITECTURE_arm64 + // TODO: remove when fixed in dynarmic + config.optimizations &= ~Dynarmic::OptimizationFlag::BlockLinking; +#endif + return std::make_unique<Dynarmic::A32::Jit>(config); } diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index 200efe4db..5a4eba3eb 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp @@ -52,12 +52,16 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 case 4: // CP15_DATA_SYNC_BARRIER return Callback{ - [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { -#ifdef _MSC_VER + [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64) _mm_mfence(); _mm_lfence(); -#else +#elif defined(ARCHITECTURE_x86_64) asm volatile("mfence\n\tlfence\n\t" : : : "memory"); +#elif defined(ARCHITECTURE_arm64) + asm volatile("dsb sy\n\t" : : : "memory"); +#else +#error Unsupported architecture #endif return 0; }, @@ -66,11 +70,15 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1 case 5: // CP15_DATA_MEMORY_BARRIER return Callback{ - [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t { -#ifdef _MSC_VER + [](void*, std::uint32_t, std::uint32_t) -> std::uint64_t { +#if defined(_MSC_VER) && defined(ARCHITECTURE_x86_64) _mm_mfence(); -#else +#elif defined(ARCHITECTURE_x86_64) asm volatile("mfence\n\t" : : : "memory"); +#elif defined(ARCHITECTURE_arm64) + asm volatile("dmb sy\n\t" : : : "memory"); +#else +#error Unsupported architecture #endif return 0; }, @@ -115,7 +123,7 @@ CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) { if (!two && opc == 0 && CRm == CoprocReg::C14) { // CNTPCT - const auto callback = [](Dynarmic::A32::Jit*, void* arg, u32, u32) -> u64 { + const auto callback = [](void* arg, u32, u32) -> u64 { const auto& parent_arg = *static_cast<ARM_Dynarmic_32*>(arg); return parent_arg.system.CoreTiming().GetClockTicks(); }; diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index 2db0b035d..20550faeb 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) #include "core/arm/dynarmic/arm_exclusive_monitor.h" #endif #include "core/arm/exclusive_monitor.h" @@ -13,7 +13,7 @@ ExclusiveMonitor::~ExclusiveMonitor() = default; std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::Memory& memory, std::size_t num_cores) { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores); #else // TODO(merry): Passthrough exclusive monitor diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 339f971e6..1a8e02e6a 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -27,12 +27,21 @@ static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; c(received_data); + AsyncReceiveInto(r, buffer, c); } - - AsyncReceiveInto(r, buffer, c); }); } +template <typename Callback> +static void AsyncAccept(boost::asio::ip::tcp::acceptor& acceptor, Callback&& c) { + acceptor.async_accept([&, c](const boost::system::error_code& error, auto&& peer_socket) { + if (!error.failed()) { + c(peer_socket); + AsyncAccept(acceptor, c); + } + }); +} + template <typename Readable, typename Buffer> static std::span<const u8> ReceiveInto(Readable& r, Buffer& buffer) { static_assert(std::is_trivial_v<Buffer>); @@ -59,9 +68,7 @@ namespace Core { class DebuggerImpl : public DebuggerBackend { public: - explicit DebuggerImpl(Core::System& system_, u16 port) - : system{system_}, signal_pipe{io_context}, client_socket{io_context} { - frontend = std::make_unique<GDBStub>(*this, system); + explicit DebuggerImpl(Core::System& system_, u16 port) : system{system_} { InitializeServer(port); } @@ -70,39 +77,42 @@ public: } bool SignalDebugger(SignalInfo signal_info) { - { - std::scoped_lock lk{connection_lock}; + std::scoped_lock lk{connection_lock}; - if (stopped) { - // Do not notify the debugger about another event. - // It should be ignored. - return false; - } - - // Set up the state. - stopped = true; - info = signal_info; + if (stopped || !state) { + // Do not notify the debugger about another event. + // It should be ignored. + return false; } + // Set up the state. + stopped = true; + state->info = signal_info; + // Write a single byte into the pipe to wake up the debug interface. - boost::asio::write(signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); + boost::asio::write(state->signal_pipe, boost::asio::buffer(&stopped, sizeof(stopped))); + return true; } + // These functions are callbacks from the frontend, and the lock will be held. + // There is no need to relock it. + std::span<const u8> ReadFromClient() override { - return ReceiveInto(client_socket, client_data); + return ReceiveInto(state->client_socket, state->client_data); } void WriteToClient(std::span<const u8> data) override { - boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); + boost::asio::write(state->client_socket, + boost::asio::buffer(data.data(), data.size_bytes())); } void SetActiveThread(Kernel::KThread* thread) override { - active_thread = thread; + state->active_thread = thread; } Kernel::KThread* GetActiveThread() override { - return active_thread; + return state->active_thread; } private: @@ -113,65 +123,78 @@ private: // Run the connection thread. connection_thread = std::jthread([&, port](std::stop_token stop_token) { + Common::SetCurrentThreadName("Debugger"); + try { // Initialize the listening socket and accept a new client. tcp::endpoint endpoint{boost::asio::ip::address_v4::any(), port}; tcp::acceptor acceptor{io_context, endpoint}; - acceptor.async_accept(client_socket, [](const auto&) {}); - io_context.run_one(); - io_context.restart(); + AsyncAccept(acceptor, [&](auto&& peer) { AcceptConnection(std::move(peer)); }); - if (stop_token.stop_requested()) { - return; + while (!stop_token.stop_requested() && io_context.run()) { } - - ThreadLoop(stop_token); } catch (const std::exception& ex) { LOG_CRITICAL(Debug_GDBStub, "Stopping server: {}", ex.what()); } }); } - void ShutdownServer() { - connection_thread.request_stop(); - io_context.stop(); - connection_thread.join(); - } + void AcceptConnection(boost::asio::ip::tcp::socket&& peer) { + LOG_INFO(Debug_GDBStub, "Accepting new peer connection"); - void ThreadLoop(std::stop_token stop_token) { - Common::SetCurrentThreadName("Debugger"); + std::scoped_lock lk{connection_lock}; + + // Ensure everything is stopped. + PauseEmulation(); + + // Set up the new frontend. + frontend = std::make_unique<GDBStub>(*this, system); + + // Set the new state. This will tear down any existing state. + state = ConnectionState{ + .client_socket{std::move(peer)}, + .signal_pipe{io_context}, + .info{}, + .active_thread{}, + .client_data{}, + .pipe_data{}, + }; // Set up the client signals for new data. - AsyncReceiveInto(signal_pipe, pipe_data, [&](auto d) { PipeData(d); }); - AsyncReceiveInto(client_socket, client_data, [&](auto d) { ClientData(d); }); + AsyncReceiveInto(state->signal_pipe, state->pipe_data, [&](auto d) { PipeData(d); }); + AsyncReceiveInto(state->client_socket, state->client_data, [&](auto d) { ClientData(d); }); // Set the active thread. UpdateActiveThread(); // Set up the frontend. frontend->Connected(); + } - // Main event loop. - while (!stop_token.stop_requested() && io_context.run()) { - } + void ShutdownServer() { + connection_thread.request_stop(); + io_context.stop(); + connection_thread.join(); } void PipeData(std::span<const u8> data) { - switch (info.type) { + std::scoped_lock lk{connection_lock}; + + switch (state->info.type) { case SignalType::Stopped: case SignalType::Watchpoint: // Stop emulation. PauseEmulation(); // Notify the client. - active_thread = info.thread; + state->active_thread = state->info.thread; UpdateActiveThread(); - if (info.type == SignalType::Watchpoint) { - frontend->Watchpoint(active_thread, *info.watchpoint); + if (state->info.type == SignalType::Watchpoint) { + frontend->Watchpoint(state->active_thread, *state->info.watchpoint); } else { - frontend->Stopped(active_thread); + frontend->Stopped(state->active_thread); } break; @@ -179,8 +202,8 @@ private: frontend->ShuttingDown(); // Wait for emulation to shut down gracefully now. - signal_pipe.close(); - client_socket.shutdown(boost::asio::socket_base::shutdown_both); + state->signal_pipe.close(); + state->client_socket.shutdown(boost::asio::socket_base::shutdown_both); LOG_INFO(Debug_GDBStub, "Shut down server"); break; @@ -188,17 +211,16 @@ private: } void ClientData(std::span<const u8> data) { + std::scoped_lock lk{connection_lock}; + const auto actions{frontend->ClientData(data)}; for (const auto action : actions) { switch (action) { case DebuggerAction::Interrupt: { - { - std::scoped_lock lk{connection_lock}; - stopped = true; - } + stopped = true; PauseEmulation(); UpdateActiveThread(); - frontend->Stopped(active_thread); + frontend->Stopped(state->active_thread); break; } case DebuggerAction::Continue: @@ -206,15 +228,15 @@ private: break; case DebuggerAction::StepThreadUnlocked: MarkResumed([&] { - active_thread->SetStepState(Kernel::StepState::StepPending); - active_thread->Resume(Kernel::SuspendType::Debug); - ResumeEmulation(active_thread); + state->active_thread->SetStepState(Kernel::StepState::StepPending); + state->active_thread->Resume(Kernel::SuspendType::Debug); + ResumeEmulation(state->active_thread); }); break; case DebuggerAction::StepThreadLocked: { MarkResumed([&] { - active_thread->SetStepState(Kernel::StepState::StepPending); - active_thread->Resume(Kernel::SuspendType::Debug); + state->active_thread->SetStepState(Kernel::StepState::StepPending); + state->active_thread->Resume(Kernel::SuspendType::Debug); }); break; } @@ -254,15 +276,14 @@ private: template <typename Callback> void MarkResumed(Callback&& cb) { Kernel::KScopedSchedulerLock sl{system.Kernel()}; - std::scoped_lock cl{connection_lock}; stopped = false; cb(); } void UpdateActiveThread() { const auto& threads{ThreadList()}; - if (std::find(threads.begin(), threads.end(), active_thread) == threads.end()) { - active_thread = threads[0]; + if (std::find(threads.begin(), threads.end(), state->active_thread) == threads.end()) { + state->active_thread = threads[0]; } } @@ -274,18 +295,22 @@ private: System& system; std::unique_ptr<DebuggerFrontend> frontend; + boost::asio::io_context io_context; std::jthread connection_thread; std::mutex connection_lock; - boost::asio::io_context io_context; - boost::process::async_pipe signal_pipe; - boost::asio::ip::tcp::socket client_socket; - SignalInfo info; - Kernel::KThread* active_thread; - bool pipe_data; - bool stopped; + struct ConnectionState { + boost::asio::ip::tcp::socket client_socket; + boost::process::async_pipe signal_pipe; + + SignalInfo info; + Kernel::KThread* active_thread; + std::array<u8, 4096> client_data; + bool pipe_data; + }; - std::array<u8, 4096> client_data; + std::optional<ConnectionState> state{}; + bool stopped{}; }; Debugger::Debugger(Core::System& system, u16 port) { diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 884229c77..a64a9ac64 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -606,6 +606,8 @@ void GDBStub::HandleQuery(std::string_view command) { } else if (command.starts_with("StartNoAckMode")) { no_ack = true; SendReply(GDB_STUB_REPLY_OK); + } else if (command.starts_with("Rcmd,")) { + HandleRcmd(Common::HexStringToVector(command.substr(5), false)); } else { SendReply(GDB_STUB_REPLY_EMPTY); } @@ -645,6 +647,155 @@ void GDBStub::HandleVCont(std::string_view command, std::vector<DebuggerAction>& } } +constexpr std::array<std::pair<const char*, Kernel::Svc::MemoryState>, 22> MemoryStateNames{{ + {"----- Free -----", Kernel::Svc::MemoryState::Free}, + {"Io ", Kernel::Svc::MemoryState::Io}, + {"Static ", Kernel::Svc::MemoryState::Static}, + {"Code ", Kernel::Svc::MemoryState::Code}, + {"CodeData ", Kernel::Svc::MemoryState::CodeData}, + {"Normal ", Kernel::Svc::MemoryState::Normal}, + {"Shared ", Kernel::Svc::MemoryState::Shared}, + {"AliasCode ", Kernel::Svc::MemoryState::AliasCode}, + {"AliasCodeData ", Kernel::Svc::MemoryState::AliasCodeData}, + {"Ipc ", Kernel::Svc::MemoryState::Ipc}, + {"Stack ", Kernel::Svc::MemoryState::Stack}, + {"ThreadLocal ", Kernel::Svc::MemoryState::ThreadLocal}, + {"Transfered ", Kernel::Svc::MemoryState::Transfered}, + {"SharedTransfered", Kernel::Svc::MemoryState::SharedTransfered}, + {"SharedCode ", Kernel::Svc::MemoryState::SharedCode}, + {"Inaccessible ", Kernel::Svc::MemoryState::Inaccessible}, + {"NonSecureIpc ", Kernel::Svc::MemoryState::NonSecureIpc}, + {"NonDeviceIpc ", Kernel::Svc::MemoryState::NonDeviceIpc}, + {"Kernel ", Kernel::Svc::MemoryState::Kernel}, + {"GeneratedCode ", Kernel::Svc::MemoryState::GeneratedCode}, + {"CodeOut ", Kernel::Svc::MemoryState::CodeOut}, + {"Coverage ", Kernel::Svc::MemoryState::Coverage}, +}}; + +static constexpr const char* GetMemoryStateName(Kernel::Svc::MemoryState state) { + for (size_t i = 0; i < MemoryStateNames.size(); i++) { + if (std::get<1>(MemoryStateNames[i]) == state) { + return std::get<0>(MemoryStateNames[i]); + } + } + return "Unknown "; +} + +static constexpr const char* GetMemoryPermissionString(const Kernel::Svc::MemoryInfo& info) { + if (info.state == Kernel::Svc::MemoryState::Free) { + return " "; + } + + switch (info.permission) { + case Kernel::Svc::MemoryPermission::ReadExecute: + return "r-x"; + case Kernel::Svc::MemoryPermission::Read: + return "r--"; + case Kernel::Svc::MemoryPermission::ReadWrite: + return "rw-"; + default: + return "---"; + } +} + +static VAddr GetModuleEnd(Kernel::KPageTable& page_table, VAddr base) { + Kernel::Svc::MemoryInfo mem_info; + VAddr cur_addr{base}; + + // Expect: r-x Code (.text) + mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + cur_addr = mem_info.base_address + mem_info.size; + if (mem_info.state != Kernel::Svc::MemoryState::Code || + mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) { + return cur_addr - 1; + } + + // Expect: r-- Code (.rodata) + mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + cur_addr = mem_info.base_address + mem_info.size; + if (mem_info.state != Kernel::Svc::MemoryState::Code || + mem_info.permission != Kernel::Svc::MemoryPermission::Read) { + return cur_addr - 1; + } + + // Expect: rw- CodeData (.data) + mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + cur_addr = mem_info.base_address + mem_info.size; + return cur_addr - 1; +} + +void GDBStub::HandleRcmd(const std::vector<u8>& command) { + std::string_view command_str{reinterpret_cast<const char*>(&command[0]), command.size()}; + std::string reply; + + auto* process = system.CurrentProcess(); + auto& page_table = process->PageTable(); + + if (command_str == "get info") { + Loader::AppLoader::Modules modules; + system.GetAppLoader().ReadNSOModules(modules); + + reply = fmt::format("Process: {:#x} ({})\n" + "Program Id: {:#018x}\n", + process->GetProcessID(), process->GetName(), process->GetProgramID()); + reply += + fmt::format("Layout:\n" + " Alias: {:#012x} - {:#012x}\n" + " Heap: {:#012x} - {:#012x}\n" + " Aslr: {:#012x} - {:#012x}\n" + " Stack: {:#012x} - {:#012x}\n" + "Modules:\n", + page_table.GetAliasRegionStart(), page_table.GetAliasRegionEnd(), + page_table.GetHeapRegionStart(), page_table.GetHeapRegionEnd(), + page_table.GetAliasCodeRegionStart(), page_table.GetAliasCodeRegionEnd(), + page_table.GetStackRegionStart(), page_table.GetStackRegionEnd()); + + for (const auto& [vaddr, name] : modules) { + reply += fmt::format(" {:#012x} - {:#012x} {}\n", vaddr, + GetModuleEnd(page_table, vaddr), name); + } + } else if (command_str == "get mappings") { + reply = "Mappings:\n"; + VAddr cur_addr = 0; + + while (true) { + using MemoryAttribute = Kernel::Svc::MemoryAttribute; + + auto mem_info = page_table.QueryInfo(cur_addr).GetSvcMemoryInfo(); + + if (mem_info.state != Kernel::Svc::MemoryState::Inaccessible || + mem_info.base_address + mem_info.size - 1 != std::numeric_limits<u64>::max()) { + const char* state = GetMemoryStateName(mem_info.state); + const char* perm = GetMemoryPermissionString(mem_info); + + const char l = True(mem_info.attribute & MemoryAttribute::Locked) ? 'L' : '-'; + const char i = True(mem_info.attribute & MemoryAttribute::IpcLocked) ? 'I' : '-'; + const char d = True(mem_info.attribute & MemoryAttribute::DeviceShared) ? 'D' : '-'; + const char u = True(mem_info.attribute & MemoryAttribute::Uncached) ? 'U' : '-'; + + reply += + fmt::format(" {:#012x} - {:#012x} {} {} {}{}{}{} [{}, {}]\n", + mem_info.base_address, mem_info.base_address + mem_info.size - 1, + perm, state, l, i, d, u, mem_info.ipc_count, mem_info.device_count); + } + + const uintptr_t next_address = mem_info.base_address + mem_info.size; + if (next_address <= cur_addr) { + break; + } + + cur_addr = next_address; + } + } else if (command_str == "help") { + reply = "Commands:\n get info\n get mappings\n"; + } else { + reply = "Unknown command.\nCommands:\n get info\n get mappings\n"; + } + + std::span<const u8> reply_span{reinterpret_cast<u8*>(&reply.front()), reply.size()}; + SendReply(Common::HexToString(reply_span, false)); +} + Kernel::KThread* GDBStub::GetThreadByID(u64 thread_id) { const auto& threads{system.GlobalSchedulerContext().GetThreadList()}; for (auto* thread : threads) { diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index 0b0f56e4b..368197920 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -32,6 +32,7 @@ private: void ExecuteCommand(std::string_view packet, std::vector<DebuggerAction>& actions); void HandleVCont(std::string_view command, std::vector<DebuggerAction>& actions); void HandleQuery(std::string_view command); + void HandleRcmd(const std::vector<u8>& command); void HandleBreakpointInsert(std::string_view command); void HandleBreakpointRemove(std::string_view command); std::vector<char>::const_iterator CommandEnd() const; diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 3bb111748..a86bec252 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -149,7 +149,7 @@ public: context->AddDomainObject(std::move(iface)); } else { kernel.CurrentProcess()->GetResourceLimit()->Reserve( - Kernel::LimitableResource::Sessions, 1); + Kernel::LimitableResource::SessionCountMax, 1); auto* session = Kernel::KSession::Create(kernel); session->Initialize(nullptr, iface->GetServiceName()); diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp index aa2dddcc6..7b363eb1e 100644 --- a/src/core/hle/kernel/init/init_slab_setup.cpp +++ b/src/core/hle/kernel/init/init_slab_setup.cpp @@ -243,6 +243,7 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) { // If we somehow get an invalid type, abort. default: ASSERT_MSG(false, "Unknown slab type: {}", slab_types[i]); + break; } // If we've hit the end of a gap, free it. @@ -265,7 +266,8 @@ void KPageBufferSlabHeap::Initialize(Core::System& system) { const size_t slab_size = num_pages * PageSize; // Reserve memory from the system resource limit. - ASSERT(kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemory, slab_size)); + ASSERT( + kernel.GetSystemResourceLimit()->Reserve(LimitableResource::PhysicalMemoryMax, slab_size)); // Allocate memory for the slab. constexpr auto AllocateOption = KMemoryManager::EncodeOption( diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp index eaa2e094c..2ec623a58 100644 --- a/src/core/hle/kernel/k_client_port.cpp +++ b/src/core/hle/kernel/k_client_port.cpp @@ -61,7 +61,7 @@ bool KClientPort::IsSignaled() const { Result KClientPort::CreateSession(KClientSession** out) { // Reserve a new session from the resource limit. KScopedResourceReservation session_reservation(kernel.CurrentProcess()->GetResourceLimit(), - LimitableResource::Sessions); + LimitableResource::SessionCountMax); R_UNLESS(session_reservation.Succeeded(), ResultLimitReached); // Update the session counts. diff --git a/src/core/hle/kernel/k_event.cpp b/src/core/hle/kernel/k_event.cpp index 78ca59463..d973853ab 100644 --- a/src/core/hle/kernel/k_event.cpp +++ b/src/core/hle/kernel/k_event.cpp @@ -20,8 +20,12 @@ void KEvent::Initialize(KProcess* owner) { m_readable_event.Initialize(this); // Set our owner process. - m_owner = owner; - m_owner->Open(); + // HACK: this should never be nullptr, but service threads don't have a + // proper parent process yet. + if (owner != nullptr) { + m_owner = owner; + m_owner->Open(); + } // Mark initialized. m_initialized = true; @@ -50,8 +54,11 @@ Result KEvent::Clear() { void KEvent::PostDestroy(uintptr_t arg) { // Release the event count resource the owner process holds. KProcess* owner = reinterpret_cast<KProcess*>(arg); - owner->GetResourceLimit()->Release(LimitableResource::Events, 1); - owner->Close(); + + if (owner != nullptr) { + owner->GetResourceLimit()->Release(LimitableResource::EventCountMax, 1); + owner->Close(); + } } } // namespace Kernel diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h index 6f845d675..3b6e7baff 100644 --- a/src/core/hle/kernel/k_memory_block.h +++ b/src/core/hle/kernel/k_memory_block.h @@ -216,13 +216,15 @@ struct KMemoryInfo { constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { return { - .addr = m_address, + .base_address = m_address, .size = m_size, .state = static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask), - .attr = static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask), - .perm = static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask), - .ipc_refcount = m_ipc_lock_count, - .device_refcount = m_device_use_count, + .attribute = + static_cast<Svc::MemoryAttribute>(m_attribute & KMemoryAttribute::UserMask), + .permission = + static_cast<Svc::MemoryPermission>(m_permission & KMemoryPermission::UserMask), + .ipc_count = m_ipc_lock_count, + .device_count = m_device_use_count, .padding = {}, }; } diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index fab55a057..612fc76fa 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -920,8 +920,8 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add // Reserve space for any partial pages we allocate. const size_t unmapped_size = aligned_src_size - mapping_src_size; - KScopedResourceReservation memory_reservation(m_resource_limit, - LimitableResource::PhysicalMemory, unmapped_size); + KScopedResourceReservation memory_reservation( + m_resource_limit, LimitableResource::PhysicalMemoryMax, unmapped_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Ensure that we manage page references correctly. @@ -1227,7 +1227,7 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState const VAddr mapping_start = Common::AlignUp((address), PageSize); const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; - m_resource_limit->Release(LimitableResource::PhysicalMemory, aligned_size - mapping_size); + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size); R_SUCCEED(); } @@ -1568,7 +1568,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { { // Reserve the memory from the process resource limit. KScopedResourceReservation memory_reservation( - m_resource_limit, LimitableResource::PhysicalMemory, size - mapped_size); + m_resource_limit, LimitableResource::PhysicalMemoryMax, size - mapped_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the new memory. @@ -1908,7 +1908,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { // Release the memory resource. m_mapped_physical_memory_size -= mapped_size; - m_resource_limit->Release(LimitableResource::PhysicalMemory, mapped_size); + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, mapped_size); // Update memory blocks. m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize, @@ -2301,6 +2301,7 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size, break; default: ASSERT(false); + break; } } @@ -2492,7 +2493,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { OperationType::Unmap)); // Release the memory from the resource limit. - m_resource_limit->Release(LimitableResource::PhysicalMemory, num_pages * PageSize); + m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, num_pages * PageSize); // Apply the memory block update. m_memory_block_manager.Update(std::addressof(allocator), m_heap_region_start + size, @@ -2522,7 +2523,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { // Reserve memory for the heap extension. KScopedResourceReservation memory_reservation( - m_resource_limit, LimitableResource::PhysicalMemory, allocation_size); + m_resource_limit, LimitableResource::PhysicalMemoryMax, allocation_size); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Allocate pages for the heap extension. @@ -2803,6 +2804,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_ break; default: ASSERT(false); + break; } addr += size; @@ -2838,6 +2840,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, break; default: ASSERT(false); + break; } R_SUCCEED(); } diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 950850291..f1ca785d7 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -320,6 +320,9 @@ public: constexpr VAddr GetAliasCodeRegionStart() const { return m_alias_code_region_start; } + constexpr VAddr GetAliasCodeRegionEnd() const { + return m_alias_code_region_end; + } constexpr VAddr GetAliasCodeRegionSize() const { return m_alias_code_region_end - m_alias_code_region_start; } diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 4ddeea73b..d1dc62401 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -38,7 +38,7 @@ namespace { */ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, VAddr stack_top) { const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); - ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::Threads, 1)); + ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); KThread* thread = KThread::Create(system.Kernel()); SCOPE_EXIT({ thread->Close(); }); @@ -124,7 +124,7 @@ void KProcess::DecrementRunningThreadCount() { } u64 KProcess::GetTotalPhysicalMemoryAvailable() { - const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + + const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemoryMax) + page_table.GetNormalMemorySize() + GetSystemResourceSize() + image_size + main_thread_stack_size}; if (const auto pool_size = kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application); @@ -349,8 +349,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: // We currently do not support process-specific system resource UNIMPLEMENTED_IF(system_resource_size != 0); - KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, - code_size + system_resource_size); + KScopedResourceReservation memory_reservation( + resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size); if (!memory_reservation.Succeeded()) { LOG_ERROR(Kernel, "Could not reserve process memory requirements of size {:X} bytes", code_size + system_resource_size); @@ -395,6 +395,7 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: default: ASSERT(false); + break; } // Create TLS region @@ -406,8 +407,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std: void KProcess::Run(s32 main_thread_priority, u64 stack_size) { AllocateMainThreadStack(stack_size); - resource_limit->Reserve(LimitableResource::Threads, 1); - resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); + resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); + resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size); const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)}; ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); @@ -442,7 +443,7 @@ void KProcess::PrepareForTermination() { plr_address = 0; if (resource_limit) { - resource_limit->Release(LimitableResource::PhysicalMemory, + resource_limit->Release(LimitableResource::PhysicalMemoryMax, main_thread_stack_size + image_size); } diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp index 010dcf99e..b9d22b414 100644 --- a/src/core/hle/kernel/k_resource_limit.cpp +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -159,12 +159,13 @@ KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical // TODO(bunnei): These values are the system defaults, the limits for service processes are // lower. These should use the correct limit values. - ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, physical_memory_size) + ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size) .IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200).IsSuccess()); - ASSERT(resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess()); + ASSERT( + resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess()); + ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess()); return resource_limit; } diff --git a/src/core/hle/kernel/k_resource_limit.h b/src/core/hle/kernel/k_resource_limit.h index 65c98c979..2573d1b7c 100644 --- a/src/core/hle/kernel/k_resource_limit.h +++ b/src/core/hle/kernel/k_resource_limit.h @@ -16,15 +16,8 @@ class CoreTiming; namespace Kernel { class KernelCore; -enum class LimitableResource : u32 { - PhysicalMemory = 0, - Threads = 1, - Events = 2, - TransferMemory = 3, - Sessions = 4, - - Count, -}; + +using LimitableResource = Svc::LimitableResource; constexpr bool IsValidResourceType(LimitableResource type) { return type < LimitableResource::Count; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index b1cabbca0..d6676904b 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -384,7 +384,8 @@ void KScheduler::SwitchThread(KThread* next_thread) { void KScheduler::ScheduleImpl() { // First, clear the needs scheduling bool. - m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + m_state.needs_scheduling.store(false, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); // Load the appropriate thread pointers for scheduling. KThread* const cur_thread{GetCurrentThreadPointer(kernel)}; @@ -400,7 +401,8 @@ void KScheduler::ScheduleImpl() { // If there aren't, we want to check if the highest priority thread is the same as the current // thread. if (highest_priority_thread == cur_thread) { - // If they're the same, then we can just return. + // If they're the same, then we can just issue a memory barrier and return. + std::atomic_thread_fence(std::memory_order_seq_cst); return; } @@ -476,7 +478,8 @@ void KScheduler::ScheduleImplFiber() { // We failed to successfully do the context switch, and need to retry. // Clear needs_scheduling. - m_state.needs_scheduling.store(false, std::memory_order_seq_cst); + m_state.needs_scheduling.store(false, std::memory_order_relaxed); + std::atomic_thread_fence(std::memory_order_seq_cst); // Refresh the highest priority thread. highest_priority_thread = m_state.highest_priority_thread; diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 73314b45e..129d60472 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -60,6 +60,9 @@ public: // Release an instance of the lock. if ((--lock_count) == 0) { + // Perform a memory barrier here. + std::atomic_thread_fence(std::memory_order_seq_cst); + // We're no longer going to hold the lock. Take note of what cores need scheduling. const u64 cores_needing_scheduling = SchedulerType::UpdateHighestPriorityThreads(kernel); diff --git a/src/core/hle/kernel/k_session.cpp b/src/core/hle/kernel/k_session.cpp index 7a6534ac3..b6f6fe9d9 100644 --- a/src/core/hle/kernel/k_session.cpp +++ b/src/core/hle/kernel/k_session.cpp @@ -76,7 +76,7 @@ void KSession::OnClientClosed() { void KSession::PostDestroy(uintptr_t arg) { // Release the session count resource the owner process holds. KProcess* owner = reinterpret_cast<KProcess*>(arg); - owner->GetResourceLimit()->Release(LimitableResource::Sessions, 1); + owner->GetResourceLimit()->Release(LimitableResource::SessionCountMax, 1); owner->Close(); } diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index a039cc591..10cd4c43d 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -14,7 +14,7 @@ namespace Kernel { KSharedMemory::KSharedMemory(KernelCore& kernel_) : KAutoObjectWithSlabHeapAndContainer{kernel_} {} KSharedMemory::~KSharedMemory() { - kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemory, size); + kernel.GetSystemResourceLimit()->Release(LimitableResource::PhysicalMemoryMax, size); } Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* owner_process_, @@ -35,7 +35,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o KResourceLimit* reslimit = kernel.GetSystemResourceLimit(); // Reserve memory for ourselves. - KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemory, + KScopedResourceReservation memory_reservation(reslimit, LimitableResource::PhysicalMemoryMax, size_); R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); @@ -57,7 +57,7 @@ Result KSharedMemory::Initialize(Core::DeviceMemory& device_memory_, KProcess* o void KSharedMemory::Finalize() { // Release the memory reservation. - resource_limit->Release(LimitableResource::PhysicalMemory, size); + resource_limit->Release(LimitableResource::PhysicalMemoryMax, size); resource_limit->Close(); // Perform inherited finalization. diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index cc88d08f0..21207fe99 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -263,9 +263,9 @@ Result KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_ R_SUCCEED(); } -Result KThread::InitializeDummyThread(KThread* thread) { +Result KThread::InitializeDummyThread(KThread* thread, KProcess* owner) { // Initialize the thread. - R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy)); + R_TRY(thread->Initialize({}, {}, {}, DummyThreadPriority, 3, owner, ThreadType::Dummy)); // Initialize emulation parameters. thread->stack_parameters.disable_count = 0; @@ -303,7 +303,7 @@ void KThread::PostDestroy(uintptr_t arg) { const bool resource_limit_release_hint = (arg & 1); const s64 hint_value = (resource_limit_release_hint ? 0 : 1); if (owner != nullptr) { - owner->GetResourceLimit()->Release(LimitableResource::Threads, 1, hint_value); + owner->GetResourceLimit()->Release(LimitableResource::ThreadCountMax, 1, hint_value); owner->Close(); } } @@ -1054,7 +1054,7 @@ void KThread::Exit() { // Release the thread resource hint, running thread count from parent. if (parent != nullptr) { - parent->GetResourceLimit()->Release(Kernel::LimitableResource::Threads, 0, 1); + parent->GetResourceLimit()->Release(Kernel::LimitableResource::ThreadCountMax, 0, 1); resource_limit_release_hint = true; parent->DecrementRunningThreadCount(); } diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 30aa10c9a..f38c92bff 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -415,7 +415,7 @@ public: static void PostDestroy(uintptr_t arg); - [[nodiscard]] static Result InitializeDummyThread(KThread* thread); + [[nodiscard]] static Result InitializeDummyThread(KThread* thread, KProcess* owner); [[nodiscard]] static Result InitializeMainThread(Core::System& system, KThread* thread, s32 virt_core); diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index b0320eb73..9f34c2d46 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp @@ -37,7 +37,7 @@ void KTransferMemory::Finalize() { void KTransferMemory::PostDestroy(uintptr_t arg) { KProcess* owner = reinterpret_cast<KProcess*>(arg); - owner->GetResourceLimit()->Release(LimitableResource::TransferMemory, 1); + owner->GetResourceLimit()->Release(LimitableResource::TransferMemoryCountMax, 1); owner->Close(); } diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index abff14079..b77723503 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -91,7 +91,7 @@ struct KernelCore::Impl { pt_heap_region.GetSize()); } - RegisterHostThread(); + RegisterHostThread(nullptr); default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread"); } @@ -229,18 +229,22 @@ struct KernelCore::Impl { const auto kernel_size{sizes.second}; // If setting the default system values fails, then something seriously wrong has occurred. - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size) + ASSERT( + system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, total_size) + .IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900) .IsSuccess()); - ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); - system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200) + .IsSuccess()); + ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133) + .IsSuccess()); + system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, kernel_size); // Reserve secure applet memory, introduced in firmware 5.0.0 constexpr u64 secure_applet_memory_size{4_MiB}; - ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, + ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, secure_applet_memory_size)); } @@ -373,15 +377,18 @@ struct KernelCore::Impl { } // Gets the dummy KThread for the caller, allocating a new one if this is the first time - KThread* GetHostDummyThread() { + KThread* GetHostDummyThread(KThread* existing_thread) { auto initialize = [this](KThread* thread) { - ASSERT(KThread::InitializeDummyThread(thread).IsSuccess()); + ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); thread->SetName(fmt::format("DummyThread:{}", GetHostThreadId())); return thread; }; - thread_local auto raw_thread = KThread(system.Kernel()); - thread_local auto thread = initialize(&raw_thread); + thread_local KThread raw_thread{system.Kernel()}; + thread_local KThread* thread = nullptr; + if (thread == nullptr) { + thread = (existing_thread == nullptr) ? initialize(&raw_thread) : existing_thread; + } return thread; } @@ -396,9 +403,9 @@ struct KernelCore::Impl { } /// Registers a new host thread by allocating a host thread ID for it - void RegisterHostThread() { + void RegisterHostThread(KThread* existing_thread) { [[maybe_unused]] const auto this_id = GetHostThreadId(); - [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(); + [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(existing_thread); } [[nodiscard]] u32 GetCurrentHostThreadID() { @@ -429,7 +436,7 @@ struct KernelCore::Impl { KThread* GetCurrentEmuThread() { const auto thread_id = GetCurrentHostThreadID(); if (thread_id >= Core::Hardware::NUM_CPU_CORES) { - return GetHostDummyThread(); + return GetHostDummyThread(nullptr); } return current_thread; @@ -1120,8 +1127,12 @@ void KernelCore::RegisterCoreThread(std::size_t core_id) { impl->RegisterCoreThread(core_id); } -void KernelCore::RegisterHostThread() { - impl->RegisterHostThread(); +void KernelCore::RegisterHostThread(KThread* existing_thread) { + impl->RegisterHostThread(existing_thread); + + if (existing_thread != nullptr) { + ASSERT(GetCurrentEmuThread() == existing_thread); + } } u32 KernelCore::GetCurrentHostThreadID() const { @@ -1196,16 +1207,28 @@ void KernelCore::Suspend(bool suspended) { const bool should_suspend{exception_exited || suspended}; const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; - for (auto* process : GetProcessList()) { - process->SetActivity(activity); + std::vector<KScopedAutoObject<KThread>> process_threads; + { + KScopedSchedulerLock sl{*this}; + + if (auto* process = CurrentProcess(); process != nullptr) { + process->SetActivity(activity); + + if (!should_suspend) { + // Runnable now; no need to wait. + return; + } - if (should_suspend) { - // Wait for execution to stop for (auto* thread : process->GetThreadList()) { - thread->WaitUntilSuspended(); + process_threads.emplace_back(thread); } } } + + // Wait for execution to stop. + for (auto& thread : process_threads) { + thread->WaitUntilSuspended(); + } } void KernelCore::ShutdownCores() { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 29617d736..2e22fe0f6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -240,7 +240,7 @@ public: void RegisterCoreThread(std::size_t core_id); /// Register the current thread as a non CPU core thread. - void RegisterHostThread(); + void RegisterHostThread(KThread* existing_thread = nullptr); /// Gets the virtual memory manager for the kernel. KMemoryManager& MemoryManager(); diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index d4375962f..3044922ac 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -12,7 +12,7 @@ namespace Kernel { PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_) : core_index{core_index_}, system{system_}, scheduler{scheduler_} { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) // TODO(bunnei): Initialization relies on a core being available. We may later replace this with // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. auto& kernel = system.Kernel(); @@ -26,7 +26,7 @@ PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KSche PhysicalCore::~PhysicalCore() = default; void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) { -#ifdef ARCHITECTURE_x86_64 +#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) auto& kernel = system.Kernel(); if (!is_64_bit) { // We already initialized a 64-bit core, replace with a 32-bit one. diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index c8fe42537..e6e41ac34 100644 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -36,11 +36,11 @@ public: private: KernelCore& kernel; - std::jthread m_thread; + std::jthread m_host_thread; std::mutex m_session_mutex; std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions; KEvent* m_wakeup_event; - KProcess* m_process; + KThread* m_thread; std::atomic<bool> m_shutdown_requested; const std::string m_service_name; }; @@ -132,7 +132,7 @@ void ServiceThread::Impl::SessionClosed(KServerSession* server_session, void ServiceThread::Impl::LoopProcess() { Common::SetCurrentThreadName(m_service_name.c_str()); - kernel.RegisterHostThread(); + kernel.RegisterHostThread(m_thread); while (!m_shutdown_requested.load()) { WaitAndProcessImpl(); @@ -160,7 +160,7 @@ ServiceThread::Impl::~Impl() { // Shut down the processing thread. m_shutdown_requested.store(true); m_wakeup_event->Signal(); - m_thread.join(); + m_host_thread.join(); // Lock mutex. m_session_mutex.lock(); @@ -177,33 +177,22 @@ ServiceThread::Impl::~Impl() { m_wakeup_event->GetReadableEvent().Close(); m_wakeup_event->Close(); - // Close process. - m_process->Close(); + // Close thread. + m_thread->Close(); } ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name) : kernel{kernel_}, m_service_name{service_name} { - // Initialize process. - m_process = KProcess::Create(kernel); - KProcess::Initialize(m_process, kernel.System(), service_name, - KProcess::ProcessType::KernelInternal, kernel.GetSystemResourceLimit()); - - // Reserve a new event from the process resource limit - KScopedResourceReservation event_reservation(m_process, LimitableResource::Events); - ASSERT(event_reservation.Succeeded()); - // Initialize event. m_wakeup_event = KEvent::Create(kernel); - m_wakeup_event->Initialize(m_process); - - // Commit the event reservation. - event_reservation.Commit(); + m_wakeup_event->Initialize(nullptr); - // Register the event. - KEvent::Register(kernel, m_wakeup_event); + // Initialize thread. + m_thread = KThread::Create(kernel); + ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess()); // Start thread. - m_thread = std::jthread([this] { LoopProcess(); }); + m_host_thread = std::jthread([this] { LoopProcess(); }); } ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name) diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index ecac97a52..e520cab47 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -267,7 +267,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien // Reserve a new session from the process resource limit. // FIXME: LimitableResource_SessionCountMax - KScopedResourceReservation session_reservation(&process, LimitableResource::Sessions); + KScopedResourceReservation session_reservation(&process, LimitableResource::SessionCountMax); if (session_reservation.Succeeded()) { session = T::Create(system.Kernel()); } else { @@ -298,7 +298,7 @@ Result CreateSession(Core::System& system, Handle* out_server, Handle* out_clien // We successfully allocated a session, so add the object we allocated to the resource // limit. - // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::Sessions, 1); + // system.Kernel().GetSystemResourceLimit().Reserve(LimitableResource::SessionCountMax, 1); } // Check that we successfully created a session. @@ -656,27 +656,12 @@ static Result ArbitrateUnlock32(Core::System& system, u32 address) { return ArbitrateUnlock(system, address); } -enum class BreakType : u32 { - Panic = 0, - AssertionFailed = 1, - PreNROLoad = 3, - PostNROLoad = 4, - PreNROUnload = 5, - PostNROUnload = 6, - CppException = 7, -}; - -struct BreakReason { - union { - u32 raw; - BitField<0, 30, BreakType> break_type; - BitField<31, 1, u32> signal_debugger; - }; -}; - /// Break program execution static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { - BreakReason break_reason{reason}; + BreakReason break_reason = + static_cast<BreakReason>(reason & ~static_cast<u32>(BreakReason::NotificationOnlyFlag)); + bool notification_only = (reason & static_cast<u32>(BreakReason::NotificationOnlyFlag)) != 0; + bool has_dumped_buffer{}; std::vector<u8> debug_buffer; @@ -705,57 +690,56 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { } has_dumped_buffer = true; }; - switch (break_reason.break_type) { - case BreakType::Panic: - LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}", - info1, info2); + switch (break_reason) { + case BreakReason::Panic: + LOG_CRITICAL(Debug_Emulated, "Userspace PANIC! info1=0x{:016X}, info2=0x{:016X}", info1, + info2); handle_debug_buffer(info1, info2); break; - case BreakType::AssertionFailed: - LOG_CRITICAL(Debug_Emulated, - "Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}", + case BreakReason::Assert: + LOG_CRITICAL(Debug_Emulated, "Userspace Assertion failed! info1=0x{:016X}, info2=0x{:016X}", info1, info2); handle_debug_buffer(info1, info2); break; - case BreakType::PreNROLoad: - LOG_WARNING( - Debug_Emulated, - "Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", - info1, info2); + case BreakReason::User: + LOG_WARNING(Debug_Emulated, "Userspace Break! 0x{:016X} with size 0x{:016X}", info1, info2); + handle_debug_buffer(info1, info2); break; - case BreakType::PostNROLoad: - LOG_WARNING(Debug_Emulated, - "Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, - info2); + case BreakReason::PreLoadDll: + LOG_INFO(Debug_Emulated, + "Userspace Attempting to load an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::PreNROUnload: - LOG_WARNING( - Debug_Emulated, - "Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", - info1, info2); + case BreakReason::PostLoadDll: + LOG_INFO(Debug_Emulated, "Userspace Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::PostNROUnload: - LOG_WARNING(Debug_Emulated, - "Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1, - info2); + case BreakReason::PreUnloadDll: + LOG_INFO(Debug_Emulated, + "Userspace Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}", info1, + info2); break; - case BreakType::CppException: + case BreakReason::PostUnloadDll: + LOG_INFO(Debug_Emulated, "Userspace Unloaded an NRO at 0x{:016X} with size 0x{:016X}", + info1, info2); + break; + case BreakReason::CppException: LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered."); break; default: LOG_WARNING( Debug_Emulated, - "Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}", - static_cast<u32>(break_reason.break_type.Value()), info1, info2); + "Signalling debugger, Unknown break reason {:#X}, info1=0x{:016X}, info2=0x{:016X}", + reason, info1, info2); handle_debug_buffer(info1, info2); break; } - system.GetReporter().SaveSvcBreakReport( - static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger.As<bool>(), - info1, info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt); + system.GetReporter().SaveSvcBreakReport(reason, notification_only, info1, info2, + has_dumped_buffer ? std::make_optional(debug_buffer) + : std::nullopt); - if (!break_reason.signal_debugger) { + if (!notification_only) { LOG_CRITICAL( Debug_Emulated, "Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}", @@ -1716,13 +1700,13 @@ static Result QueryProcessMemory(Core::System& system, VAddr memory_info_address auto& memory{system.Memory()}; const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; - memory.Write64(memory_info_address + 0x00, memory_info.addr); + memory.Write64(memory_info_address + 0x00, memory_info.base_address); memory.Write64(memory_info_address + 0x08, memory_info.size); memory.Write32(memory_info_address + 0x10, static_cast<u32>(memory_info.state) & 0xff); - memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attr)); - memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.perm)); - memory.Write32(memory_info_address + 0x1c, memory_info.ipc_refcount); - memory.Write32(memory_info_address + 0x20, memory_info.device_refcount); + memory.Write32(memory_info_address + 0x14, static_cast<u32>(memory_info.attribute)); + memory.Write32(memory_info_address + 0x18, static_cast<u32>(memory_info.permission)); + memory.Write32(memory_info_address + 0x1c, memory_info.ipc_count); + memory.Write32(memory_info_address + 0x20, memory_info.device_count); memory.Write32(memory_info_address + 0x24, 0); // Page info appears to be currently unused by the kernel and is always set to zero. @@ -1943,7 +1927,7 @@ static Result CreateThread(Core::System& system, Handle* out_handle, VAddr entry // Reserve a new thread from the process resource limit (waiting up to 100ms). KScopedResourceReservation thread_reservation( - kernel.CurrentProcess(), LimitableResource::Threads, 1, + kernel.CurrentProcess(), LimitableResource::ThreadCountMax, 1, system.CoreTiming().GetGlobalTimeNs().count() + 100000000); if (!thread_reservation.Succeeded()) { LOG_ERROR(Kernel_SVC, "Could not reserve a new thread"); @@ -2344,7 +2328,7 @@ static Result CreateTransferMemory(Core::System& system, Handle* out, VAddr addr // Reserve a new transfer memory from the process resource limit. KScopedResourceReservation trmem_reservation(kernel.CurrentProcess(), - LimitableResource::TransferMemory); + LimitableResource::TransferMemoryCountMax); R_UNLESS(trmem_reservation.Succeeded(), ResultLimitReached); // Create the transfer memory. @@ -2496,7 +2480,7 @@ static Result CreateEvent(Core::System& system, Handle* out_write, Handle* out_r // Reserve a new event from the process resource limit KScopedResourceReservation event_reservation(kernel.CurrentProcess(), - LimitableResource::Events); + LimitableResource::EventCountMax); R_UNLESS(event_reservation.Succeeded(), ResultLimitReached); // Create a new event. @@ -2539,11 +2523,6 @@ static Result CreateEvent32(Core::System& system, Handle* out_write, Handle* out static Result GetProcessInfo(Core::System& system, u64* out, Handle process_handle, u32 type) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type); - // This function currently only allows retrieving a process' status. - enum class InfoType { - Status, - }; - const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); KScopedAutoObject process = handle_table.GetObject<KProcess>(process_handle); if (process.IsNull()) { @@ -2552,9 +2531,9 @@ static Result GetProcessInfo(Core::System& system, u64* out, Handle process_hand return ResultInvalidHandle; } - const auto info_type = static_cast<InfoType>(type); - if (info_type != InfoType::Status) { - LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type); + const auto info_type = static_cast<ProcessInfoType>(type); + if (info_type != ProcessInfoType::ProcessState) { + LOG_ERROR(Kernel_SVC, "Expected info_type to be ProcessState but got {} instead", type); return ResultInvalidEnumValue; } @@ -2722,14 +2701,24 @@ static Result GetThreadList(Core::System& system, u32* out_num_threads, VAddr ou return ResultSuccess; } -static Result FlushProcessDataCache32([[maybe_unused]] Core::System& system, - [[maybe_unused]] Handle handle, [[maybe_unused]] u32 address, - [[maybe_unused]] u32 size) { - // Note(Blinkhawk): For emulation purposes of the data cache this is mostly a no-op, - // as all emulation is done in the same cache level in host architecture, thus data cache - // does not need flushing. - LOG_DEBUG(Kernel_SVC, "called"); - return ResultSuccess; +static Result FlushProcessDataCache32(Core::System& system, Handle process_handle, u64 address, + u64 size) { + // Validate address/size. + R_UNLESS(size > 0, ResultInvalidSize); + R_UNLESS(address == static_cast<uintptr_t>(address), ResultInvalidCurrentMemory); + R_UNLESS(size == static_cast<size_t>(size), ResultInvalidCurrentMemory); + + // Get the process from its handle. + KScopedAutoObject process = + system.Kernel().CurrentProcess()->GetHandleTable().GetObject<KProcess>(process_handle); + R_UNLESS(process.IsNotNull(), ResultInvalidHandle); + + // Verify the region is within range. + auto& page_table = process->PageTable(); + R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); + + // Perform the operation. + R_RETURN(system.Memory().FlushDataCache(*process, address, size)); } namespace { diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 9b0305552..33eebcef6 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -8,6 +8,8 @@ namespace Kernel::Svc { +using Handle = u32; + enum class MemoryState : u32 { Free = 0x00, Io = 0x01, @@ -55,17 +57,6 @@ enum class MemoryPermission : u32 { }; DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); -struct MemoryInfo { - u64 addr{}; - u64 size{}; - MemoryState state{}; - MemoryAttribute attr{}; - MemoryPermission perm{}; - u32 ipc_refcount{}; - u32 device_refcount{}; - u32 padding{}; -}; - enum class SignalType : u32 { Signal = 0, SignalAndIncrementIfEqual = 1, @@ -124,7 +115,57 @@ enum class ProcessExitReason : u32 { constexpr inline size_t ThreadLocalRegionSize = 0x200; -// Debug types. +struct PageInfo { + u32 flags; +}; + +// Info Types. +enum class InfoType : u32 { + CoreMask = 0, + PriorityMask = 1, + AliasRegionAddress = 2, + AliasRegionSize = 3, + HeapRegionAddress = 4, + HeapRegionSize = 5, + TotalMemorySize = 6, + UsedMemorySize = 7, + DebuggerAttached = 8, + ResourceLimit = 9, + IdleTickCount = 10, + RandomEntropy = 11, + AslrRegionAddress = 12, + AslrRegionSize = 13, + StackRegionAddress = 14, + StackRegionSize = 15, + SystemResourceSizeTotal = 16, + SystemResourceSizeUsed = 17, + ProgramId = 18, + InitialProcessIdRange = 19, + UserExceptionContextAddress = 20, + TotalNonSystemMemorySize = 21, + UsedNonSystemMemorySize = 22, + IsApplication = 23, + FreeThreadCount = 24, + ThreadTickCount = 25, + IsSvcPermitted = 26, + + MesosphereMeta = 65000, + MesosphereCurrentProcess = 65001, +}; + +enum class BreakReason : u32 { + Panic = 0, + Assert = 1, + User = 2, + PreLoadDll = 3, + PostLoadDll = 4, + PreUnloadDll = 5, + PostUnloadDll = 6, + CppException = 7, + + NotificationOnlyFlag = 0x80000000, +}; + enum class DebugEvent : u32 { CreateProcess = 0, CreateThread = 1, @@ -133,6 +174,14 @@ enum class DebugEvent : u32 { Exception = 4, }; +enum class DebugThreadParam : u32 { + Priority = 0, + State = 1, + IdealCore = 2, + CurrentCore = 3, + AffinityMask = 4, +}; + enum class DebugException : u32 { UndefinedInstruction = 0, InstructionAbort = 1, @@ -146,4 +195,401 @@ enum class DebugException : u32 { MemorySystemError = 9, }; +enum class DebugEventFlag : u32 { + Stopped = (1u << 0), +}; + +enum class BreakPointType : u32 { + HardwareInstruction = 0, + HardwareData = 1, +}; + +enum class HardwareBreakPointRegisterName : u32 { + I0 = 0, + I1 = 1, + I2 = 2, + I3 = 3, + I4 = 4, + I5 = 5, + I6 = 6, + I7 = 7, + I8 = 8, + I9 = 9, + I10 = 10, + I11 = 11, + I12 = 12, + I13 = 13, + I14 = 14, + I15 = 15, + D0 = 16, + D1 = 17, + D2 = 18, + D3 = 19, + D4 = 20, + D5 = 21, + D6 = 22, + D7 = 23, + D8 = 24, + D9 = 25, + D10 = 26, + D11 = 27, + D12 = 28, + D13 = 29, + D14 = 30, + D15 = 31, +}; + +namespace lp64 { +struct LastThreadContext { + u64 fp; + u64 sp; + u64 lr; + u64 pc; +}; + +struct PhysicalMemoryInfo { + PAddr physical_address; + u64 virtual_address; + u64 size; +}; + +struct DebugInfoCreateProcess { + u64 program_id; + u64 process_id; + std::array<char, 0xC> name; + u32 flags; + u64 user_exception_context_address; // 5.0.0+ +}; + +struct DebugInfoCreateThread { + u64 thread_id; + u64 tls_address; + // Removed in 11.0.0 u64 entrypoint; +}; + +struct DebugInfoExitProcess { + ProcessExitReason reason; +}; + +struct DebugInfoExitThread { + ThreadExitReason reason; +}; + +struct DebugInfoUndefinedInstructionException { + u32 insn; +}; + +struct DebugInfoDataAbortException { + u64 address; +}; + +struct DebugInfoAlignmentFaultException { + u64 address; +}; + +struct DebugInfoBreakPointException { + BreakPointType type; + u64 address; +}; + +struct DebugInfoUserBreakException { + BreakReason break_reason; + u64 address; + u64 size; +}; + +struct DebugInfoDebuggerBreakException { + std::array<u64, 4> active_thread_ids; +}; + +struct DebugInfoUndefinedSystemCallException { + u32 id; +}; + +union DebugInfoSpecificException { + DebugInfoUndefinedInstructionException undefined_instruction; + DebugInfoDataAbortException data_abort; + DebugInfoAlignmentFaultException alignment_fault; + DebugInfoBreakPointException break_point; + DebugInfoUserBreakException user_break; + DebugInfoDebuggerBreakException debugger_break; + DebugInfoUndefinedSystemCallException undefined_system_call; + u64 raw; +}; + +struct DebugInfoException { + DebugException type; + u64 address; + DebugInfoSpecificException specific; +}; + +union DebugInfo { + DebugInfoCreateProcess create_process; + DebugInfoCreateThread create_thread; + DebugInfoExitProcess exit_process; + DebugInfoExitThread exit_thread; + DebugInfoException exception; +}; + +struct DebugEventInfo { + DebugEvent type; + u32 flags; + u64 thread_id; + DebugInfo info; +}; +static_assert(sizeof(DebugEventInfo) >= 0x40); + +struct SecureMonitorArguments { + std::array<u64, 8> r; +}; +static_assert(sizeof(SecureMonitorArguments) == 0x40); +} // namespace lp64 + +namespace ilp32 { +struct LastThreadContext { + u32 fp; + u32 sp; + u32 lr; + u32 pc; +}; + +struct PhysicalMemoryInfo { + PAddr physical_address; + u32 virtual_address; + u32 size; +}; + +struct DebugInfoCreateProcess { + u64 program_id; + u64 process_id; + std::array<char, 0xC> name; + u32 flags; + u32 user_exception_context_address; // 5.0.0+ +}; + +struct DebugInfoCreateThread { + u64 thread_id; + u32 tls_address; + // Removed in 11.0.0 u32 entrypoint; +}; + +struct DebugInfoExitProcess { + ProcessExitReason reason; +}; + +struct DebugInfoExitThread { + ThreadExitReason reason; +}; + +struct DebugInfoUndefinedInstructionException { + u32 insn; +}; + +struct DebugInfoDataAbortException { + u32 address; +}; + +struct DebugInfoAlignmentFaultException { + u32 address; +}; + +struct DebugInfoBreakPointException { + BreakPointType type; + u32 address; +}; + +struct DebugInfoUserBreakException { + BreakReason break_reason; + u32 address; + u32 size; +}; + +struct DebugInfoDebuggerBreakException { + std::array<u64, 4> active_thread_ids; +}; + +struct DebugInfoUndefinedSystemCallException { + u32 id; +}; + +union DebugInfoSpecificException { + DebugInfoUndefinedInstructionException undefined_instruction; + DebugInfoDataAbortException data_abort; + DebugInfoAlignmentFaultException alignment_fault; + DebugInfoBreakPointException break_point; + DebugInfoUserBreakException user_break; + DebugInfoDebuggerBreakException debugger_break; + DebugInfoUndefinedSystemCallException undefined_system_call; + u64 raw; +}; + +struct DebugInfoException { + DebugException type; + u32 address; + DebugInfoSpecificException specific; +}; + +union DebugInfo { + DebugInfoCreateProcess create_process; + DebugInfoCreateThread create_thread; + DebugInfoExitProcess exit_process; + DebugInfoExitThread exit_thread; + DebugInfoException exception; +}; + +struct DebugEventInfo { + DebugEvent type; + u32 flags; + u64 thread_id; + DebugInfo info; +}; + +struct SecureMonitorArguments { + std::array<u32, 8> r; +}; +static_assert(sizeof(SecureMonitorArguments) == 0x20); +} // namespace ilp32 + +struct ThreadContext { + std::array<u64, 29> r; + u64 fp; + u64 lr; + u64 sp; + u64 pc; + u32 pstate; + u32 padding; + std::array<u128, 32> v; + u32 fpcr; + u32 fpsr; + u64 tpidr; +}; +static_assert(sizeof(ThreadContext) == 0x320); + +struct MemoryInfo { + u64 base_address; + u64 size; + MemoryState state; + MemoryAttribute attribute; + MemoryPermission permission; + u32 ipc_count; + u32 device_count; + u32 padding; +}; + +enum class LimitableResource : u32 { + PhysicalMemoryMax = 0, + ThreadCountMax = 1, + EventCountMax = 2, + TransferMemoryCountMax = 3, + SessionCountMax = 4, + Count, +}; + +enum class IoPoolType : u32 { + // Not supported. + Count = 0, +}; + +enum class MemoryMapping : u32 { + IoRegister = 0, + Uncached = 1, + Memory = 2, +}; + +enum class KernelDebugType : u32 { + Thread = 0, + ThreadCallStack = 1, + KernelObject = 2, + Handle_ = 3, + Memory = 4, + PageTable = 5, + CpuUtilization = 6, + Process = 7, + SuspendProcess = 8, + ResumeProcess = 9, + Port = 10, +}; + +enum class KernelTraceState : u32 { + Disabled = 0, + Enabled = 1, +}; + +enum class CodeMemoryOperation : u32 { + Map = 0, + MapToOwner = 1, + Unmap = 2, + UnmapFromOwner = 3, +}; + +enum class InterruptType : u32 { + Edge = 0, + Level = 1, +}; + +enum class DeviceName { + Afi = 0, + Avpc = 1, + Dc = 2, + Dcb = 3, + Hc = 4, + Hda = 5, + Isp2 = 6, + MsencNvenc = 7, + Nv = 8, + Nv2 = 9, + Ppcs = 10, + Sata = 11, + Vi = 12, + Vic = 13, + XusbHost = 14, + XusbDev = 15, + Tsec = 16, + Ppcs1 = 17, + Dc1 = 18, + Sdmmc1a = 19, + Sdmmc2a = 20, + Sdmmc3a = 21, + Sdmmc4a = 22, + Isp2b = 23, + Gpu = 24, + Gpub = 25, + Ppcs2 = 26, + Nvdec = 27, + Ape = 28, + Se = 29, + Nvjpg = 30, + Hc1 = 31, + Se1 = 32, + Axiap = 33, + Etr = 34, + Tsecb = 35, + Tsec1 = 36, + Tsecb1 = 37, + Nvdec1 = 38, + Count, +}; + +enum class SystemInfoType : u32 { + TotalPhysicalMemorySize = 0, + UsedPhysicalMemorySize = 1, + InitialProcessIdRange = 2, +}; + +enum class ProcessInfoType : u32 { + ProcessState = 0, +}; + +struct CreateProcessParameter { + std::array<char, 12> name; + u32 version; + u64 program_id; + u64 code_address; + s32 code_num_pages; + u32 flags; + Handle reslimit; + s32 system_resource_num_pages; +}; +static_assert(sizeof(CreateProcessParameter) == 0x30); + } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 272c54cf7..3730937fe 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -722,4 +722,12 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by Invalidate/Store/FlushProcessDataCache32 +template <Result func(Core::System&, Handle, u64, u64)> +void SvcWrap32(Core::System& system) { + const u64 address = (Param(system, 3) << 32) | Param(system, 2); + const u64 size = (Param(system, 4) << 32) | Param(system, 1); + FuncReturn32(system, func(system, Param32(system, 0), address, size).raw); +} + } // namespace Kernel diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 56c990728..240f06689 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -28,30 +28,49 @@ enum class ErrorModule : u32 { Loader = 9, CMIF = 10, HIPC = 11, + TMA = 12, + DMNT = 13, + GDS = 14, PM = 15, NS = 16, + BSDSockets = 17, HTC = 18, + TSC = 19, NCMContent = 20, SM = 21, RO = 22, + GC = 23, SDMMC = 24, OVLN = 25, SPL = 26, + Socket = 27, + HTCLOW = 29, + DDSF = 30, + HTCFS = 31, + Async = 32, + Util = 33, + TIPC = 35, + ANIF = 37, ETHC = 100, I2C = 101, GPIO = 102, UART = 103, + CPAD = 104, Settings = 105, + FTM = 106, WLAN = 107, XCD = 108, + TMP451 = 109, NIFM = 110, Hwopus = 111, + LSM6DS3 = 112, Bluetooth = 113, VI = 114, NFP = 115, Time = 116, FGM = 117, OE = 118, + BH1730FVC = 119, PCIe = 120, Friends = 121, BCAT = 122, @@ -65,7 +84,7 @@ enum class ErrorModule : u32 { AHID = 130, Qlaunch = 132, PCV = 133, - OMM = 134, + USBPD = 134, BPC = 135, PSM = 136, NIM = 137, @@ -75,18 +94,22 @@ enum class ErrorModule : u32 { NSD = 141, PCTL = 142, BTM = 143, + LA = 144, ETicket = 145, NGC = 146, ERPT = 147, APM = 148, + CEC = 149, Profiler = 150, ErrorUpload = 151, + LIDBE = 152, Audio = 153, NPNS = 154, NPNSHTTPSTREAM = 155, ARP = 157, SWKBD = 158, BOOT = 159, + NetDiag = 160, NFCMifare = 161, UserlandAssert = 162, Fatal = 163, @@ -94,17 +117,68 @@ enum class ErrorModule : u32 { SPSM = 165, BGTC = 167, UserlandCrash = 168, + SASBUS = 169, + PI = 170, + AudioCtrl = 172, + LBL = 173, + JIT = 175, + HDCP = 176, + OMM = 177, + PDM = 178, + OLSC = 179, SREPO = 180, Dauth = 181, + STDFU = 182, + DBG = 183, + DHCPS = 186, + SPI = 187, + AVM = 188, + PWM = 189, + RTC = 191, + Regulator = 192, + LED = 193, + SIO = 195, + PCM = 196, + CLKRST = 197, + POWCTL = 198, + AudioOld = 201, HID = 202, LDN = 203, + CS = 204, Irsensor = 205, Capture = 206, Manu = 208, ATK = 209, + WEB = 210, + LCS = 211, GRC = 212, + Repair = 213, + Album = 214, + RID = 215, Migration = 216, MigrationLdcServ = 217, + HIDBUS = 218, + ENS = 219, + WebSocket = 223, + DCDMTP = 227, + PGL = 228, + Notification = 229, + INS = 230, + LP2P = 231, + RCD = 232, + LCM40607 = 233, + PRC = 235, + TMAHTC = 237, + ECTX = 238, + MNPP = 239, + HSHL = 240, + CAPMTP = 242, + DP2HDMI = 244, + Cradle = 245, + SProfile = 246, + NDRM = 250, + TSPM = 499, + DevMenu = 500, GeneralWebApplet = 800, WifiWebAuthApplet = 809, WhitelistedApplet = 810, diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp index fcf34bf7e..bae0d99a6 100644 --- a/src/core/hle/service/am/applets/applet_error.cpp +++ b/src/core/hle/service/am/applets/applet_error.cpp @@ -144,6 +144,7 @@ void Error::Initialize() { break; default: UNIMPLEMENTED_MSG("Unimplemented LibAppletError mode={:02X}!", mode); + break; } } diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp index c34ef08b3..e50acdaf6 100644 --- a/src/core/hle/service/am/applets/applet_general_backend.cpp +++ b/src/core/hle/service/am/applets/applet_general_backend.cpp @@ -129,6 +129,7 @@ void Auth::Execute() { } default: unimplemented_log(); + break; } } @@ -192,6 +193,7 @@ void PhotoViewer::Execute() { break; default: UNIMPLEMENTED_MSG("Unimplemented PhotoViewer applet mode={:02X}!", mode); + break; } } diff --git a/src/core/hle/service/kernel_helpers.cpp b/src/core/hle/service/kernel_helpers.cpp index af133af93..42991928e 100644 --- a/src/core/hle/service/kernel_helpers.cpp +++ b/src/core/hle/service/kernel_helpers.cpp @@ -31,7 +31,7 @@ ServiceContext::~ServiceContext() { Kernel::KEvent* ServiceContext::CreateEvent(std::string&& name) { // Reserve a new event from the process resource limit Kernel::KScopedResourceReservation event_reservation(process, - Kernel::LimitableResource::Events); + Kernel::LimitableResource::EventCountMax); if (!event_reservation.Succeeded()) { LOG_CRITICAL(Service, "Resource limit reached!"); return {}; 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 ced57dfe6..b97813fbc 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -300,11 +300,10 @@ Kernel::KEvent* nvhost_ctrl_gpu::QueryEvent(u32 event_id) { return error_notifier_event; case 2: return unknown_event; - default: { + default: LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); + return nullptr; } - } - return nullptr; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 45a759fa8..e123564c6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -364,11 +364,10 @@ Kernel::KEvent* nvhost_gpu::QueryEvent(u32 event_id) { return sm_exception_breakpoint_pause_report_event; case 3: return error_notifier_event; - default: { + default: LOG_CRITICAL(Service_NVDRV, "Unknown Ctrl GPU Event {}", event_id); + return nullptr; } - } - return nullptr; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp index ea4a14ea4..3d1338e66 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -23,15 +23,17 @@ void BufferQueueCore::NotifyShutdown() { } void BufferQueueCore::SignalDequeueCondition() { + dequeue_possible.store(true); dequeue_condition.notify_all(); } -bool BufferQueueCore::WaitForDequeueCondition() { +bool BufferQueueCore::WaitForDequeueCondition(std::unique_lock<std::mutex>& lk) { if (is_shutting_down) { return false; } - dequeue_condition.wait(mutex); + dequeue_condition.wait(lk, [&] { return dequeue_possible.load(); }); + dequeue_possible.store(false); return true; } diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.h b/src/core/hle/service/nvflinger/buffer_queue_core.h index ca6baefaf..85b3bc4c1 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.h +++ b/src/core/hle/service/nvflinger/buffer_queue_core.h @@ -38,7 +38,7 @@ public: private: void SignalDequeueCondition(); - bool WaitForDequeueCondition(); + bool WaitForDequeueCondition(std::unique_lock<std::mutex>& lk); s32 GetMinUndequeuedBufferCountLocked(bool async) const; s32 GetMinMaxBufferCountLocked(bool async) const; @@ -60,7 +60,8 @@ private: BufferQueueDefs::SlotsType slots{}; std::vector<BufferItem> queue; s32 override_max_buffer_count{}; - mutable std::condition_variable_any dequeue_condition; + std::condition_variable dequeue_condition; + std::atomic<bool> dequeue_possible{}; const bool use_async_buffer{}; // This is always disabled on HOS bool dequeue_buffer_cannot_block{}; PixelFormat default_buffer_format{PixelFormat::Rgba8888}; diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp index 41ba44b21..e601b5da1 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.cpp @@ -121,8 +121,8 @@ Status BufferQueueProducer::SetBufferCount(s32 buffer_count) { return Status::NoError; } -Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, - Status* return_flags) const { +Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, + std::unique_lock<std::mutex>& lk) const { bool try_again = true; while (try_again) { @@ -214,7 +214,7 @@ Status BufferQueueProducer::WaitForFreeSlotThenRelock(bool async, s32* found, return Status::WouldBlock; } - if (!core->WaitForDequeueCondition()) { + if (!core->WaitForDequeueCondition(lk)) { // We are no longer running return Status::NoError; } @@ -237,7 +237,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool Status return_flags = Status::NoError; bool attached_by_consumer = false; { - std::scoped_lock lock{core->mutex}; + std::unique_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); if (format == PixelFormat::NoFormat) { @@ -248,7 +248,7 @@ Status BufferQueueProducer::DequeueBuffer(s32* out_slot, Fence* out_fence, bool usage |= core->consumer_usage_bit; s32 found{}; - Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags); + Status status = WaitForFreeSlotThenRelock(async, &found, &return_flags, lock); if (status != Status::NoError) { return status; } @@ -400,13 +400,13 @@ Status BufferQueueProducer::AttachBuffer(s32* out_slot, return Status::BadValue; } - std::scoped_lock lock{core->mutex}; + std::unique_lock lock{core->mutex}; core->WaitWhileAllocatingLocked(); Status return_flags = Status::NoError; s32 found{}; - const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags); + const auto status = WaitForFreeSlotThenRelock(false, &found, &return_flags, lock); if (status != Status::NoError) { return status; } diff --git a/src/core/hle/service/nvflinger/buffer_queue_producer.h b/src/core/hle/service/nvflinger/buffer_queue_producer.h index 7526bf8ec..1d380480f 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_producer.h +++ b/src/core/hle/service/nvflinger/buffer_queue_producer.h @@ -70,7 +70,8 @@ public: private: BufferQueueProducer(const BufferQueueProducer&) = delete; - Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags) const; + Status WaitForFreeSlotThenRelock(bool async, s32* found, Status* return_flags, + std::unique_lock<std::mutex>& lk) const; Kernel::KEvent* buffer_wait_event{}; Service::KernelHelpers::ServiceContext& service_context; diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 5ab41c0c4..0de67f1e1 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -228,6 +228,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session, } UNIMPLEMENTED_MSG("command_type={}", ctx.GetCommandType()); + break; } // If emulation was shutdown, we are closing service threads, do not write the response back to diff --git a/src/core/hle/service/sm/sm_controller.cpp b/src/core/hle/service/sm/sm_controller.cpp index 69e0fe808..1cf9dd1c4 100644 --- a/src/core/hle/service/sm/sm_controller.cpp +++ b/src/core/hle/service/sm/sm_controller.cpp @@ -34,8 +34,8 @@ void Controller::CloneCurrentObject(Kernel::HLERequestContext& ctx) { // once this is a proper process // Reserve a new session from the process resource limit. - Kernel::KScopedResourceReservation session_reservation(&process, - Kernel::LimitableResource::Sessions); + Kernel::KScopedResourceReservation session_reservation( + &process, Kernel::LimitableResource::SessionCountMax); ASSERT(session_reservation.Succeeded()); // Create the session. diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index 2aa675df9..f9ada7c93 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -280,6 +280,7 @@ static constexpr int TransitionTime(int year, Rule rule, int offset) { } default: ASSERT(false); + break; } return value + rule.transition_time + offset; } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 3ca80c8ff..3141122f1 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "common/atomic_ops.h" +#include "common/cache_management.h" #include "common/common_types.h" #include "common/logging/log.h" #include "common/page_table.h" @@ -329,6 +330,55 @@ struct Memory::Impl { }); } + template <typename Callback> + Result PerformCacheOperation(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size, + Callback&& cb) { + class InvalidMemoryException : public std::exception {}; + + try { + WalkBlock( + process, dest_addr, size, + [&](const std::size_t block_size, const VAddr current_vaddr) { + LOG_ERROR(HW_Memory, "Unmapped cache maintenance @ {:#018X}", current_vaddr); + throw InvalidMemoryException(); + }, + [&](const std::size_t block_size, u8* const host_ptr) { cb(block_size, host_ptr); }, + [&](const VAddr current_vaddr, const std::size_t block_size, u8* const host_ptr) { + system.GPU().FlushRegion(current_vaddr, block_size); + cb(block_size, host_ptr); + }, + [](const std::size_t block_size) {}); + } catch (InvalidMemoryException&) { + return Kernel::ResultInvalidCurrentMemory; + } + + return ResultSuccess; + } + + Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { + auto perform = [&](const std::size_t block_size, u8* const host_ptr) { + // Do nothing; this operation (dc ivac) cannot be supported + // from EL0 + }; + return PerformCacheOperation(process, dest_addr, size, perform); + } + + Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { + auto perform = [&](const std::size_t block_size, u8* const host_ptr) { + // dc cvac: Store to point of coherency + Common::DataCacheLineCleanByVAToPoC(host_ptr, block_size); + }; + return PerformCacheOperation(process, dest_addr, size, perform); + } + + Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size) { + auto perform = [&](const std::size_t block_size, u8* const host_ptr) { + // dc civac: Store to point of coherency, and invalidate from cache + Common::DataCacheLineCleanAndInvalidateByVAToPoC(host_ptr, block_size); + }; + return PerformCacheOperation(process, dest_addr, size, perform); + } + void MarkRegionDebug(VAddr vaddr, u64 size, bool debug) { if (vaddr == 0) { return; @@ -786,6 +836,21 @@ void Memory::ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, const s impl->ZeroBlock(process, dest_addr, size); } +Result Memory::InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, + const std::size_t size) { + return impl->InvalidateDataCache(process, dest_addr, size); +} + +Result Memory::StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, + const std::size_t size) { + return impl->StoreDataCache(process, dest_addr, size); +} + +Result Memory::FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, + const std::size_t size) { + return impl->FlushDataCache(process, dest_addr, size); +} + void Memory::RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) { impl->RasterizerMarkRegionCached(vaddr, size, cached); } diff --git a/src/core/memory.h b/src/core/memory.h index 81eac448b..31fe699d8 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -7,6 +7,7 @@ #include <memory> #include <string> #include "common/common_types.h" +#include "core/hle/result.h" namespace Common { struct PageTable; @@ -450,6 +451,39 @@ public: void ZeroBlock(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); /** + * Invalidates a range of bytes within the current process' address space at the specified + * virtual address. + * + * @param process The process that will have data invalidated within its address space. + * @param dest_addr The destination virtual address to invalidate the data from. + * @param size The size of the range to invalidate, in bytes. + * + */ + Result InvalidateDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); + + /** + * Stores a range of bytes within the current process' address space at the specified + * virtual address. + * + * @param process The process that will have data stored within its address space. + * @param dest_addr The destination virtual address to store the data from. + * @param size The size of the range to store, in bytes. + * + */ + Result StoreDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); + + /** + * Flushes a range of bytes within the current process' address space at the specified + * virtual address. + * + * @param process The process that will have data flushed within its address space. + * @param dest_addr The destination virtual address to flush the data from. + * @param size The size of the range to flush, in bytes. + * + */ + Result FlushDataCache(const Kernel::KProcess& process, VAddr dest_addr, std::size_t size); + + /** * Marks each page within the specified address range as cached or uncached. * * @param vaddr The virtual address indicating the start of the address range. |
