diff options
Diffstat (limited to 'src/core')
97 files changed, 4681 insertions, 3425 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c15d9f52f..8546d3602 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -35,6 +35,8 @@ add_library(core STATIC      crypto/ctr_encryption_layer.h      crypto/xts_encryption_layer.cpp      crypto/xts_encryption_layer.h +    device_memory.cpp +    device_memory.h      file_sys/bis_factory.cpp      file_sys/bis_factory.h      file_sys/card_image.cpp @@ -152,6 +154,23 @@ add_library(core STATIC      hle/kernel/hle_ipc.h      hle/kernel/kernel.cpp      hle/kernel/kernel.h +    hle/kernel/memory/address_space_info.cpp +    hle/kernel/memory/address_space_info.h +    hle/kernel/memory/memory_block.h +    hle/kernel/memory/memory_block_manager.cpp +    hle/kernel/memory/memory_block_manager.h +    hle/kernel/memory/memory_layout.h +    hle/kernel/memory/memory_manager.cpp +    hle/kernel/memory/memory_manager.h +    hle/kernel/memory/memory_types.h +    hle/kernel/memory/page_linked_list.h +    hle/kernel/memory/page_heap.cpp +    hle/kernel/memory/page_heap.h +    hle/kernel/memory/page_table.cpp +    hle/kernel/memory/page_table.h +    hle/kernel/memory/slab_heap.h +    hle/kernel/memory/system_control.cpp +    hle/kernel/memory/system_control.h      hle/kernel/mutex.cpp      hle/kernel/mutex.h      hle/kernel/object.cpp @@ -178,6 +197,7 @@ add_library(core STATIC      hle/kernel/shared_memory.h      hle/kernel/svc.cpp      hle/kernel/svc.h +    hle/kernel/svc_types.h      hle/kernel/svc_wrap.h      hle/kernel/synchronization_object.cpp      hle/kernel/synchronization_object.h @@ -189,8 +209,6 @@ add_library(core STATIC      hle/kernel/time_manager.h      hle/kernel/transfer_memory.cpp      hle/kernel/transfer_memory.h -    hle/kernel/vm_manager.cpp -    hle/kernel/vm_manager.h      hle/kernel/writable_event.cpp      hle/kernel/writable_event.h      hle/lock.cpp diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index fb9e616b9..d079a1bc8 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -60,7 +60,7 @@ static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");  using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; -Symbols GetSymbols(VAddr text_offset, Memory::Memory& memory) { +Symbols GetSymbols(VAddr text_offset, Core::Memory::Memory& memory) {      const auto mod_offset = text_offset + memory.Read32(text_offset + 4);      if (mod_offset < text_offset || (mod_offset & 0b11) != 0 || diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 57eae839e..cb2e640e2 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -26,28 +26,28 @@ public:      virtual ~ARM_Interface() = default;      struct ThreadContext32 { -        std::array<u32, 16> cpu_registers; -        u32 cpsr; -        std::array<u8, 4> padding; -        std::array<u64, 32> fprs; -        u32 fpscr; -        u32 fpexc; -        u32 tpidr; +        std::array<u32, 16> cpu_registers{}; +        u32 cpsr{}; +        std::array<u8, 4> padding{}; +        std::array<u64, 32> fprs{}; +        u32 fpscr{}; +        u32 fpexc{}; +        u32 tpidr{};      };      // Internally within the kernel, it expects the AArch32 version of the      // thread context to be 344 bytes in size.      static_assert(sizeof(ThreadContext32) == 0x158);      struct ThreadContext64 { -        std::array<u64, 31> cpu_registers; -        u64 sp; -        u64 pc; -        u32 pstate; -        std::array<u8, 4> padding; -        std::array<u128, 32> vector_registers; -        u32 fpcr; -        u32 fpsr; -        u64 tpidr; +        std::array<u64, 31> cpu_registers{}; +        u64 sp{}; +        u64 pc{}; +        u32 pstate{}; +        std::array<u8, 4> padding{}; +        std::array<u128, 32> vector_registers{}; +        u32 fpcr{}; +        u32 fpsr{}; +        u64 tpidr{};      };      // Internally within the kernel, it expects the AArch64 version of the      // thread context to be 800 bytes in size. diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 187a972ac..9bc86e3b9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -67,7 +67,7 @@ public:      }      void CallSVC(u32 swi) override { -        Kernel::CallSVC(parent.system, swi); +        Kernel::Svc::Call(parent.system, swi);      }      void AddTicks(u64 ticks) override { diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 143e46e4d..8ba9cea8f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -15,7 +15,7 @@  #include "core/arm/arm_interface.h"  #include "core/arm/exclusive_monitor.h" -namespace Memory { +namespace Core::Memory {  class Memory;  } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index a53a58ba0..65cbfe5e6 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -8,6 +8,7 @@  #include <dynarmic/A64/config.h>  #include "common/logging/log.h"  #include "common/microprofile.h" +#include "common/page_table.h"  #include "core/arm/dynarmic/arm_dynarmic_64.h"  #include "core/core.h"  #include "core/core_manager.h" @@ -18,8 +19,8 @@  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/scheduler.h"  #include "core/hle/kernel/svc.h" -#include "core/hle/kernel/vm_manager.h"  #include "core/memory.h" +#include "core/settings.h"  namespace Core { @@ -103,7 +104,7 @@ public:      }      void CallSVC(u32 swi) override { -        Kernel::CallSVC(parent.system, swi); +        Kernel::Svc::Call(parent.system, swi);      }      void AddTicks(u64 ticks) override { @@ -144,6 +145,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&      config.page_table_address_space_bits = address_space_bits;      config.silently_mirror_page_table = false;      config.absolute_offset_page_table = true; +    config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; +    config.only_detect_misalignment_via_page_table_on_page_boundary = true;      // Multi-process state      config.processor_id = core_index; @@ -159,6 +162,12 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&      // Unpredictable instructions      config.define_unpredictable_behaviour = true; +    // Optimizations +    if (Settings::values.disable_cpu_opt) { +        config.enable_optimizations = false; +        config.enable_fast_dispatch = false; +    } +      return std::make_shared<Dynarmic::A64::Jit>(config);  } diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index e71240a96..647cecaf0 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -15,7 +15,7 @@  #include "core/arm/exclusive_monitor.h"  #include "core/arm/unicorn/arm_unicorn.h" -namespace Memory { +namespace Core::Memory {  class Memory;  } @@ -92,7 +92,7 @@ public:  private:      friend class ARM_Dynarmic_64;      Dynarmic::A64::ExclusiveMonitor monitor; -    Memory::Memory& memory; +    Core::Memory::Memory& memory;  };  } // namespace Core diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index 4ef418b90..ccd73b80f 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h @@ -8,7 +8,7 @@  #include "common/common_types.h" -namespace Memory { +namespace Core::Memory {  class Memory;  } diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 8a9800a96..b96583123 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp @@ -11,6 +11,7 @@  #include "core/core_timing.h"  #include "core/hle/kernel/scheduler.h"  #include "core/hle/kernel/svc.h" +#include "core/memory.h"  namespace Core { @@ -171,7 +172,17 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));  void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {      MICROPROFILE_SCOPE(ARM_Jit_Unicorn); + +    // Temporarily map the code page for Unicorn +    u64 map_addr{GetPC() & ~Memory::PAGE_MASK}; +    std::vector<u8> page_buffer(Memory::PAGE_SIZE); +    system.Memory().ReadBlock(map_addr, page_buffer.data(), page_buffer.size()); + +    CHECKED(uc_mem_map_ptr(uc, map_addr, page_buffer.size(), +                           UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data()));      CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); +    CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); +      system.CoreTiming().AddTicks(num_instructions);      if (GDBStub::IsServerEnabled()) {          if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { @@ -266,7 +277,7 @@ void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {      switch (ec) {      case 0x15: // SVC -        Kernel::CallSVC(arm_instance->system, iss); +        Kernel::Svc::Call(arm_instance->system, iss);          break;      }  } diff --git a/src/core/core.cpp b/src/core/core.cpp index 3bd90d79f..f9f8a3000 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -14,6 +14,7 @@  #include "core/core_manager.h"  #include "core/core_timing.h"  #include "core/cpu_manager.h" +#include "core/device_memory.h"  #include "core/file_sys/bis_factory.h"  #include "core/file_sys/card_image.h"  #include "core/file_sys/mode.h" @@ -140,6 +141,8 @@ struct System::Impl {      ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {          LOG_DEBUG(HW_Memory, "initialized OK"); +        device_memory = std::make_unique<Core::DeviceMemory>(system); +          core_timing.Initialize();          kernel.Initialize();          cpu_manager.Initialize(); @@ -276,6 +279,7 @@ struct System::Impl {          telemetry_session.reset();          perf_stats.reset();          gpu_core.reset(); +        device_memory.reset();          // Close all CPU/threading state          cpu_manager.Shutdown(); @@ -346,7 +350,8 @@ struct System::Impl {      std::unique_ptr<Loader::AppLoader> app_loader;      std::unique_ptr<Tegra::GPU> gpu_core;      std::unique_ptr<Hardware::InterruptManager> interrupt_manager; -    Memory::Memory memory; +    std::unique_ptr<Core::DeviceMemory> device_memory; +    Core::Memory::Memory memory;      CpuManager cpu_manager;      bool is_powered_on = false;      bool exit_lock = false; @@ -472,6 +477,14 @@ Kernel::Process* System::CurrentProcess() {      return impl->kernel.CurrentProcess();  } +Core::DeviceMemory& System::DeviceMemory() { +    return *impl->device_memory; +} + +const Core::DeviceMemory& System::DeviceMemory() const { +    return *impl->device_memory; +} +  const Kernel::Process* System::CurrentProcess() const {      return impl->kernel.CurrentProcess();  } @@ -505,7 +518,7 @@ Memory::Memory& System::Memory() {      return impl->memory;  } -const Memory::Memory& System::Memory() const { +const Core::Memory::Memory& System::Memory() const {      return impl->memory;  } diff --git a/src/core/core.h b/src/core/core.h index 8d862a8e6..acc53d6a1 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -36,9 +36,10 @@ class AppLoader;  enum class ResultStatus : u16;  } // namespace Loader -namespace Memory { +namespace Core::Memory {  struct CheatEntry; -} // namespace Memory +class Memory; +} // namespace Core::Memory  namespace Service { @@ -86,14 +87,11 @@ namespace Core::Hardware {  class InterruptManager;  } -namespace Memory { -class Memory; -} -  namespace Core {  class ARM_Interface;  class CoreManager; +class DeviceMemory;  class ExclusiveMonitor;  class FrameLimiter;  class PerfStats; @@ -230,10 +228,10 @@ public:      const ExclusiveMonitor& Monitor() const;      /// Gets a mutable reference to the system memory instance. -    Memory::Memory& Memory(); +    Core::Memory::Memory& Memory();      /// Gets a constant reference to the system memory instance. -    const Memory::Memory& Memory() const; +    const Core::Memory::Memory& Memory() const;      /// Gets a mutable reference to the GPU interface      Tegra::GPU& GPU(); @@ -259,6 +257,12 @@ public:      /// Gets the global scheduler      const Kernel::GlobalScheduler& GlobalScheduler() const; +    /// Gets the manager for the guest device memory +    Core::DeviceMemory& DeviceMemory(); + +    /// Gets the manager for the guest device memory +    const Core::DeviceMemory& DeviceMemory() const; +      /// Provides a pointer to the current process      Kernel::Process* CurrentProcess(); diff --git a/src/core/core_manager.h b/src/core/core_manager.h index b14e723d7..d525de00a 100644 --- a/src/core/core_manager.h +++ b/src/core/core_manager.h @@ -22,7 +22,7 @@ namespace Core::Timing {  class CoreTiming;  } -namespace Memory { +namespace Core::Memory {  class Memory;  } diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 87e6a1fd3..8997c7082 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -1202,7 +1202,8 @@ const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager:       {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),        static_cast<u64>(KeyAreaKeyType::System)}},      {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}}, -    {"keyblob_mac_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)}}, +    {"keyblob_mac_key_source", +     {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},      {"tsec_key", {S128KeyType::TSEC, 0, 0}},      {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},      {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp new file mode 100644 index 000000000..51097ced3 --- /dev/null +++ b/src/core/device_memory.cpp @@ -0,0 +1,15 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/core.h" +#include "core/device_memory.h" +#include "core/memory.h" + +namespace Core { + +DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {} + +DeviceMemory::~DeviceMemory() = default; + +} // namespace Core diff --git a/src/core/device_memory.h b/src/core/device_memory.h new file mode 100644 index 000000000..9efa088d0 --- /dev/null +++ b/src/core/device_memory.h @@ -0,0 +1,51 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/virtual_buffer.h" + +namespace Core { + +class System; + +namespace DramMemoryMap { +enum : u64 { +    Base = 0x80000000ULL, +    Size = 0x100000000ULL, +    End = Base + Size, +    KernelReserveBase = Base + 0x60000, +    SlabHeapBase = KernelReserveBase + 0x85000, +    SlapHeapSize = 0xa21000, +    SlabHeapEnd = SlabHeapBase + SlapHeapSize, +}; +}; // namespace DramMemoryMap + +class DeviceMemory : NonCopyable { +public: +    explicit DeviceMemory(Core::System& system); +    ~DeviceMemory(); + +    template <typename T> +    PAddr GetPhysicalAddr(const T* ptr) const { +        return (reinterpret_cast<uintptr_t>(ptr) - reinterpret_cast<uintptr_t>(buffer.data())) + +               DramMemoryMap::Base; +    } + +    u8* GetPointer(PAddr addr) { +        return buffer.data() + (addr - DramMemoryMap::Base); +    } + +    const u8* GetPointer(PAddr addr) const { +        return buffer.data() + (addr - DramMemoryMap::Base); +    } + +private: +    Common::VirtualBuffer<u8> buffer; +    Core::System& system; +}; + +} // namespace Core diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 81ec06cd4..b93aa6935 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -249,7 +249,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {  }  namespace { -std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder( +std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(      const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,      const VirtualDir& base_path, bool upper) {      const auto build_id_raw = Common::HexToString(build_id_, upper); @@ -269,14 +269,14 @@ std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder(          return std::nullopt;      } -    Memory::TextCheatParser parser; +    Core::Memory::TextCheatParser parser;      return parser.Parse(          system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));  }  } // Anonymous namespace -std::vector<Memory::CheatEntry> PatchManager::CreateCheatList( +std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(      const Core::System& system, const std::array<u8, 32>& build_id_) const {      const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);      if (load_dir == nullptr) { @@ -289,7 +289,7 @@ std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(      std::sort(patch_dirs.begin(), patch_dirs.end(),                [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); -    std::vector<Memory::CheatEntry> out; +    std::vector<Core::Memory::CheatEntry> out;      for (const auto& subdir : patch_dirs) {          if (std::find(disabled.cbegin(), disabled.cend(), subdir->GetName()) != disabled.cend()) {              continue; diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index e857e6e82..ec6db524d 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -51,8 +51,8 @@ public:      bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;      // Creates a CheatList object with all -    std::vector<Memory::CheatEntry> CreateCheatList(const Core::System& system, -                                                    const std::array<u8, 0x20>& build_id) const; +    std::vector<Core::Memory::CheatEntry> CreateCheatList( +        const Core::System& system, const std::array<u8, 0x20>& build_id) const;      // Currently tracked RomFS patches:      // - Game Updates diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 1d6c30962..43169bf9f 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -51,6 +51,17 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {      return Loader::ResultStatus::Success;  } +/*static*/ ProgramMetadata ProgramMetadata::GetDefault() { +    ProgramMetadata result; + +    result.LoadManual( +        true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/, +        0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x00100000 /*main_thread_stack_size*/, +        {}, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, {} /*capabilities*/); + +    return result; +} +  void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,                                   s32 main_thread_prio, u32 main_thread_core,                                   u32 main_thread_stack_size, u64 title_id, diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index f8759a396..35069972b 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -44,9 +44,13 @@ public:      ProgramMetadata();      ~ProgramMetadata(); +    /// Gets a default ProgramMetadata configuration, should only be used for homebrew formats where +    /// we do not have an NPDM file +    static ProgramMetadata GetDefault(); +      Loader::ResultStatus Load(VirtualFile file); -    // Load from parameters instead of NPDM file, used for KIP +    /// Load from parameters instead of NPDM file, used for KIP      void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio,                      u32 main_thread_core, u32 main_thread_stack_size, u64 title_id,                      u64 filesystem_permissions, KernelCapabilityDescriptors capabilities); diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 6f51895b9..70c0f8b80 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -37,9 +37,9 @@  #include "core/core.h"  #include "core/core_manager.h"  #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/scheduler.h" -#include "core/hle/kernel/vm_manager.h"  #include "core/loader/loader.h"  #include "core/memory.h" @@ -643,7 +643,7 @@ static void HandleQuery() {          SendReply(target_xml);      } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {          const VAddr base_address = -            Core::System::GetInstance().CurrentProcess()->VMManager().GetCodeRegionBaseAddress(); +            Core::System::GetInstance().CurrentProcess()->PageTable().GetCodeRegionStart();          std::string buffer = fmt::format("TextSeg={:0x}", base_address);          SendReply(buffer.c_str());      } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 6d66276bc..5ab204b9b 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -47,7 +47,8 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern      return MakeResult(std::move(client_session));  } -ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) { +ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, +                                          Core::Memory::Memory& memory) {      // Keep ServerSession alive until we're done working with it.      if (!parent->Server()) {          return ERR_SESSION_CLOSED_BY_REMOTE; diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index d15b09554..c5f760d7d 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -12,7 +12,7 @@  union ResultCode; -namespace Memory { +namespace Core::Memory {  class Memory;  } @@ -42,7 +42,7 @@ public:          return HANDLE_TYPE;      } -    ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory); +    ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);      bool ShouldWait(const Thread* thread) const override; diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 8097b3863..29bfa3621 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -14,6 +14,7 @@ constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};  constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};  constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};  constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; +constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103};  constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};  constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};  constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index d65dae3ae..91d94025c 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -282,19 +282,19 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {      return RESULT_SUCCESS;  } -std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { +std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {      std::vector<u8> buffer; -    const bool is_buffer_a{BufferDescriptorA().size() > std::size_t(buffer_index) && +    const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&                             BufferDescriptorA()[buffer_index].Size()};      auto& memory = Core::System::GetInstance().Memory();      if (is_buffer_a) { -        ASSERT_MSG(BufferDescriptorA().size() > std::size_t(buffer_index), +        ASSERT_MSG(BufferDescriptorA().size() > buffer_index,                     "BufferDescriptorA invalid buffer_index {}", buffer_index);          buffer.resize(BufferDescriptorA()[buffer_index].Size());          memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());      } else { -        ASSERT_MSG(BufferDescriptorX().size() > std::size_t(buffer_index), +        ASSERT_MSG(BufferDescriptorX().size() > buffer_index,                     "BufferDescriptorX invalid buffer_index {}", buffer_index);          buffer.resize(BufferDescriptorX()[buffer_index].Size());          memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); @@ -304,13 +304,13 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {  }  std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, -                                           int buffer_index) const { +                                           std::size_t buffer_index) const {      if (size == 0) {          LOG_WARNING(Core, "skip empty buffer write");          return 0;      } -    const bool is_buffer_b{BufferDescriptorB().size() > std::size_t(buffer_index) && +    const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&                             BufferDescriptorB()[buffer_index].Size()};      const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};      if (size > buffer_size) { @@ -321,13 +321,13 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,      auto& memory = Core::System::GetInstance().Memory();      if (is_buffer_b) { -        ASSERT_MSG(BufferDescriptorB().size() > std::size_t(buffer_index), +        ASSERT_MSG(BufferDescriptorB().size() > buffer_index,                     "BufferDescriptorB invalid buffer_index {}", buffer_index);          ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size,                     "BufferDescriptorB buffer_index {} is not large enough", buffer_index);          memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);      } else { -        ASSERT_MSG(BufferDescriptorC().size() > std::size_t(buffer_index), +        ASSERT_MSG(BufferDescriptorC().size() > buffer_index,                     "BufferDescriptorC invalid buffer_index {}", buffer_index);          ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size,                     "BufferDescriptorC buffer_index {} is not large enough", buffer_index); @@ -337,17 +337,17 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,      return size;  } -std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { -    const bool is_buffer_a{BufferDescriptorA().size() > std::size_t(buffer_index) && +std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const { +    const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&                             BufferDescriptorA()[buffer_index].Size()};      if (is_buffer_a) { -        ASSERT_MSG(BufferDescriptorA().size() > std::size_t(buffer_index), +        ASSERT_MSG(BufferDescriptorA().size() > buffer_index,                     "BufferDescriptorA invalid buffer_index {}", buffer_index);          ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0,                     "BufferDescriptorA buffer_index {} is empty", buffer_index);          return BufferDescriptorA()[buffer_index].Size();      } else { -        ASSERT_MSG(BufferDescriptorX().size() > std::size_t(buffer_index), +        ASSERT_MSG(BufferDescriptorX().size() > buffer_index,                     "BufferDescriptorX invalid buffer_index {}", buffer_index);          ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0,                     "BufferDescriptorX buffer_index {} is empty", buffer_index); @@ -355,15 +355,15 @@ std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {      }  } -std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { -    const bool is_buffer_b{BufferDescriptorB().size() > std::size_t(buffer_index) && +std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) const { +    const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&                             BufferDescriptorB()[buffer_index].Size()};      if (is_buffer_b) { -        ASSERT_MSG(BufferDescriptorB().size() > std::size_t(buffer_index), +        ASSERT_MSG(BufferDescriptorB().size() > buffer_index,                     "BufferDescriptorB invalid buffer_index {}", buffer_index);          return BufferDescriptorB()[buffer_index].Size();      } else { -        ASSERT_MSG(BufferDescriptorC().size() > std::size_t(buffer_index), +        ASSERT_MSG(BufferDescriptorC().size() > buffer_index,                     "BufferDescriptorC invalid buffer_index {}", buffer_index);          return BufferDescriptorC()[buffer_index].Size();      } diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 050ad8fd7..af3330297 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -179,10 +179,11 @@ public:      }      /// Helper function to read a buffer using the appropriate buffer descriptor -    std::vector<u8> ReadBuffer(int buffer_index = 0) const; +    std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;      /// Helper function to write a buffer using the appropriate buffer descriptor -    std::size_t WriteBuffer(const void* buffer, std::size_t size, int buffer_index = 0) const; +    std::size_t WriteBuffer(const void* buffer, std::size_t size, +                            std::size_t buffer_index = 0) const;      /* Helper function to write a buffer using the appropriate buffer descriptor       * @@ -194,7 +195,8 @@ public:       */      template <typename ContiguousContainer,                typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> -    std::size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const { +    std::size_t WriteBuffer(const ContiguousContainer& container, +                            std::size_t buffer_index = 0) const {          using ContiguousType = typename ContiguousContainer::value_type;          static_assert(std::is_trivially_copyable_v<ContiguousType>, @@ -205,10 +207,10 @@ public:      }      /// Helper function to get the size of the input buffer -    std::size_t GetReadBufferSize(int buffer_index = 0) const; +    std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;      /// Helper function to get the size of the output buffer -    std::size_t GetWriteBufferSize(int buffer_index = 0) const; +    std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;      template <typename T>      std::shared_ptr<T> GetCopyObject(std::size_t index) { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 014d647cf..7655382fa 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -18,15 +18,20 @@  #include "core/core.h"  #include "core/core_timing.h"  #include "core/core_timing_util.h" +#include "core/device_memory.h"  #include "core/hardware_properties.h"  #include "core/hle/kernel/client_port.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/memory_layout.h" +#include "core/hle/kernel/memory/memory_manager.h" +#include "core/hle/kernel/memory/slab_heap.h"  #include "core/hle/kernel/physical_core.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/resource_limit.h"  #include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/shared_memory.h"  #include "core/hle/kernel/synchronization.h"  #include "core/hle/kernel/thread.h"  #include "core/hle/kernel/time_manager.h" @@ -110,6 +115,7 @@ struct KernelCore::Impl {          InitializePhysicalCores();          InitializeSystemResourceLimit(kernel); +        InitializeMemoryLayout();          InitializeThreads();          InitializePreemption();      } @@ -154,12 +160,17 @@ struct KernelCore::Impl {          system_resource_limit = ResourceLimit::Create(kernel);          // If setting the default system values fails, then something seriously wrong has occurred. -        ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000) +        ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x100000000)                     .IsSuccess());          ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());          ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());          ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());          ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); + +        if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) || +            !system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) { +            UNREACHABLE(); +        }      }      void InitializeThreads() { @@ -237,6 +248,57 @@ struct KernelCore::Impl {          return result;      } +    void InitializeMemoryLayout() { +        // Initialize memory layout +        constexpr Memory::MemoryLayout layout{Memory::MemoryLayout::GetDefaultLayout()}; +        constexpr std::size_t hid_size{0x40000}; +        constexpr std::size_t font_size{0x1100000}; +        constexpr std::size_t irs_size{0x8000}; +        constexpr std::size_t time_size{0x1000}; +        constexpr PAddr hid_addr{layout.System().StartAddress()}; +        constexpr PAddr font_pa{layout.System().StartAddress() + hid_size}; +        constexpr PAddr irs_addr{layout.System().StartAddress() + hid_size + font_size}; +        constexpr PAddr time_addr{layout.System().StartAddress() + hid_size + font_size + irs_size}; + +        // Initialize memory manager +        memory_manager = std::make_unique<Memory::MemoryManager>(); +        memory_manager->InitializeManager(Memory::MemoryManager::Pool::Application, +                                          layout.Application().StartAddress(), +                                          layout.Application().EndAddress()); +        memory_manager->InitializeManager(Memory::MemoryManager::Pool::Applet, +                                          layout.Applet().StartAddress(), +                                          layout.Applet().EndAddress()); +        memory_manager->InitializeManager(Memory::MemoryManager::Pool::System, +                                          layout.System().StartAddress(), +                                          layout.System().EndAddress()); + +        hid_shared_mem = Kernel::SharedMemory::Create( +            system.Kernel(), system.DeviceMemory(), nullptr, +            {hid_addr, hid_size / Memory::PageSize}, Memory::MemoryPermission::None, +            Memory::MemoryPermission::Read, hid_addr, hid_size, "HID:SharedMemory"); +        font_shared_mem = Kernel::SharedMemory::Create( +            system.Kernel(), system.DeviceMemory(), nullptr, +            {font_pa, font_size / Memory::PageSize}, Memory::MemoryPermission::None, +            Memory::MemoryPermission::Read, font_pa, font_size, "Font:SharedMemory"); +        irs_shared_mem = Kernel::SharedMemory::Create( +            system.Kernel(), system.DeviceMemory(), nullptr, +            {irs_addr, irs_size / Memory::PageSize}, Memory::MemoryPermission::None, +            Memory::MemoryPermission::Read, irs_addr, irs_size, "IRS:SharedMemory"); +        time_shared_mem = Kernel::SharedMemory::Create( +            system.Kernel(), system.DeviceMemory(), nullptr, +            {time_addr, time_size / Memory::PageSize}, Memory::MemoryPermission::None, +            Memory::MemoryPermission::Read, time_addr, time_size, "Time:SharedMemory"); + +        // Allocate slab heaps +        user_slab_heap_pages = std::make_unique<Memory::SlabHeap<Memory::Page>>(); + +        // Initialize slab heaps +        constexpr u64 user_slab_heap_size{0x3de000}; +        user_slab_heap_pages->Initialize( +            system.DeviceMemory().GetPointer(Core::DramMemoryMap::SlabHeapBase), +            user_slab_heap_size); +    } +      std::atomic<u32> next_object_id{0};      std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};      std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; @@ -271,6 +333,16 @@ struct KernelCore::Impl {      std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;      std::mutex register_thread_mutex; +    // Kernel memory management +    std::unique_ptr<Memory::MemoryManager> memory_manager; +    std::unique_ptr<Memory::SlabHeap<Memory::Page>> user_slab_heap_pages; + +    // Shared memory for services +    std::shared_ptr<Kernel::SharedMemory> hid_shared_mem; +    std::shared_ptr<Kernel::SharedMemory> font_shared_mem; +    std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; +    std::shared_ptr<Kernel::SharedMemory> time_shared_mem; +      // System context      Core::System& system;  }; @@ -437,4 +509,52 @@ Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const {      return impl->GetCurrentEmuThreadID();  } +Memory::MemoryManager& KernelCore::MemoryManager() { +    return *impl->memory_manager; +} + +const Memory::MemoryManager& KernelCore::MemoryManager() const { +    return *impl->memory_manager; +} + +Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() { +    return *impl->user_slab_heap_pages; +} + +const Memory::SlabHeap<Memory::Page>& KernelCore::GetUserSlabHeapPages() const { +    return *impl->user_slab_heap_pages; +} + +Kernel::SharedMemory& KernelCore::GetHidSharedMem() { +    return *impl->hid_shared_mem; +} + +const Kernel::SharedMemory& KernelCore::GetHidSharedMem() const { +    return *impl->hid_shared_mem; +} + +Kernel::SharedMemory& KernelCore::GetFontSharedMem() { +    return *impl->font_shared_mem; +} + +const Kernel::SharedMemory& KernelCore::GetFontSharedMem() const { +    return *impl->font_shared_mem; +} + +Kernel::SharedMemory& KernelCore::GetIrsSharedMem() { +    return *impl->irs_shared_mem; +} + +const Kernel::SharedMemory& KernelCore::GetIrsSharedMem() const { +    return *impl->irs_shared_mem; +} + +Kernel::SharedMemory& KernelCore::GetTimeSharedMem() { +    return *impl->time_shared_mem; +} + +const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const { +    return *impl->time_shared_mem; +} +  } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index c4f78ab71..83de1f542 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -8,6 +8,7 @@  #include <string>  #include <unordered_map>  #include <vector> +#include "core/hle/kernel/memory/memory_types.h"  #include "core/hle/kernel/object.h"  namespace Core { @@ -23,6 +24,12 @@ struct EventType;  namespace Kernel { +namespace Memory { +class MemoryManager; +template <typename T> +class SlabHeap; +} // namespace Memory +  class AddressArbiter;  class ClientPort;  class GlobalScheduler; @@ -31,6 +38,7 @@ class PhysicalCore;  class Process;  class ResourceLimit;  class Scheduler; +class SharedMemory;  class Synchronization;  class Thread;  class TimeManager; @@ -147,6 +155,42 @@ public:      /// Register the current thread as a non CPU core thread.      void RegisterHostThread(); +    /// Gets the virtual memory manager for the kernel. +    Memory::MemoryManager& MemoryManager(); + +    /// Gets the virtual memory manager for the kernel. +    const Memory::MemoryManager& MemoryManager() const; + +    /// Gets the slab heap allocated for user space pages. +    Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages(); + +    /// Gets the slab heap allocated for user space pages. +    const Memory::SlabHeap<Memory::Page>& GetUserSlabHeapPages() const; + +    /// Gets the shared memory object for HID services. +    Kernel::SharedMemory& GetHidSharedMem(); + +    /// Gets the shared memory object for HID services. +    const Kernel::SharedMemory& GetHidSharedMem() const; + +    /// Gets the shared memory object for font services. +    Kernel::SharedMemory& GetFontSharedMem(); + +    /// Gets the shared memory object for font services. +    const Kernel::SharedMemory& GetFontSharedMem() const; + +    /// Gets the shared memory object for IRS services. +    Kernel::SharedMemory& GetIrsSharedMem(); + +    /// Gets the shared memory object for IRS services. +    const Kernel::SharedMemory& GetIrsSharedMem() const; + +    /// Gets the shared memory object for Time services. +    Kernel::SharedMemory& GetTimeSharedMem(); + +    /// Gets the shared memory object for Time services. +    const Kernel::SharedMemory& GetTimeSharedMem() const; +  private:      friend class Object;      friend class Process; diff --git a/src/core/hle/kernel/memory/address_space_info.cpp b/src/core/hle/kernel/memory/address_space_info.cpp new file mode 100644 index 000000000..27fae05e7 --- /dev/null +++ b/src/core/hle/kernel/memory/address_space_info.cpp @@ -0,0 +1,118 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// This file references various implementation details from Atmosphère, an open-source firmware for +// the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX. + +#include <array> + +#include "common/assert.h" +#include "core/hle/kernel/memory/address_space_info.h" + +namespace Kernel::Memory { + +namespace { + +enum : u64 { +    Size_1_MB = 0x100000, +    Size_2_MB = 2 * Size_1_MB, +    Size_128_MB = 128 * Size_1_MB, +    Size_1_GB = 0x40000000, +    Size_2_GB = 2 * Size_1_GB, +    Size_4_GB = 4 * Size_1_GB, +    Size_6_GB = 6 * Size_1_GB, +    Size_64_GB = 64 * Size_1_GB, +    Size_512_GB = 512 * Size_1_GB, +    Invalid = std::numeric_limits<u64>::max(), +}; + +// clang-format off +constexpr std::array<AddressSpaceInfo, 13> AddressSpaceInfos{{ +   { 32 /*bit_width*/, Size_2_MB   /*addr*/, Size_1_GB   - Size_2_MB   /*size*/, AddressSpaceInfo::Type::Is32Bit,    }, +   { 32 /*bit_width*/, Size_1_GB   /*addr*/, Size_4_GB   - Size_1_GB   /*size*/, AddressSpaceInfo::Type::Small64Bit, }, +   { 32 /*bit_width*/, Invalid     /*addr*/, Size_1_GB                 /*size*/, AddressSpaceInfo::Type::Heap,       }, +   { 32 /*bit_width*/, Invalid     /*addr*/, Size_1_GB                 /*size*/, AddressSpaceInfo::Type::Alias,      }, +   { 36 /*bit_width*/, Size_128_MB /*addr*/, Size_2_GB   - Size_128_MB /*size*/, AddressSpaceInfo::Type::Is32Bit,    }, +   { 36 /*bit_width*/, Size_2_GB   /*addr*/, Size_64_GB  - Size_2_GB   /*size*/, AddressSpaceInfo::Type::Small64Bit, }, +   { 36 /*bit_width*/, Invalid     /*addr*/, Size_6_GB                 /*size*/, AddressSpaceInfo::Type::Heap,       }, +   { 36 /*bit_width*/, Invalid     /*addr*/, Size_6_GB                 /*size*/, AddressSpaceInfo::Type::Alias,      }, +   { 39 /*bit_width*/, Size_128_MB /*addr*/, Size_512_GB - Size_128_MB /*size*/, AddressSpaceInfo::Type::Large64Bit, }, +   { 39 /*bit_width*/, Invalid     /*addr*/, Size_64_GB                /*size*/, AddressSpaceInfo::Type::Is32Bit     }, +   { 39 /*bit_width*/, Invalid     /*addr*/, Size_6_GB                 /*size*/, AddressSpaceInfo::Type::Heap,       }, +   { 39 /*bit_width*/, Invalid     /*addr*/, Size_64_GB                /*size*/, AddressSpaceInfo::Type::Alias,      }, +   { 39 /*bit_width*/, Invalid     /*addr*/, Size_2_GB                 /*size*/, AddressSpaceInfo::Type::Stack,      }, +}}; +// clang-format on + +constexpr bool IsAllowedIndexForAddress(std::size_t index) { +    return index < std::size(AddressSpaceInfos) && AddressSpaceInfos[index].GetAddress() != Invalid; +} + +constexpr std::size_t +    AddressSpaceIndices32Bit[static_cast<std::size_t>(AddressSpaceInfo::Type::Count)]{ +        0, 1, 0, 2, 0, 3, +    }; + +constexpr std::size_t +    AddressSpaceIndices36Bit[static_cast<std::size_t>(AddressSpaceInfo::Type::Count)]{ +        4, 5, 4, 6, 4, 7, +    }; + +constexpr std::size_t +    AddressSpaceIndices39Bit[static_cast<std::size_t>(AddressSpaceInfo::Type::Count)]{ +        9, 8, 8, 10, 12, 11, +    }; + +constexpr bool IsAllowed32BitType(AddressSpaceInfo::Type type) { +    return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit && +           type != AddressSpaceInfo::Type::Stack; +} + +constexpr bool IsAllowed36BitType(AddressSpaceInfo::Type type) { +    return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Large64Bit && +           type != AddressSpaceInfo::Type::Stack; +} + +constexpr bool IsAllowed39BitType(AddressSpaceInfo::Type type) { +    return type < AddressSpaceInfo::Type::Count && type != AddressSpaceInfo::Type::Small64Bit; +} + +} // namespace + +u64 AddressSpaceInfo::GetAddressSpaceStart(std::size_t width, AddressSpaceInfo::Type type) { +    const std::size_t index{static_cast<std::size_t>(type)}; +    switch (width) { +    case 32: +        ASSERT(IsAllowed32BitType(type)); +        ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices32Bit[index])); +        return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetAddress(); +    case 36: +        ASSERT(IsAllowed36BitType(type)); +        ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices36Bit[index])); +        return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetAddress(); +    case 39: +        ASSERT(IsAllowed39BitType(type)); +        ASSERT(IsAllowedIndexForAddress(AddressSpaceIndices39Bit[index])); +        return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetAddress(); +    } +    UNREACHABLE(); +} + +std::size_t AddressSpaceInfo::GetAddressSpaceSize(std::size_t width, AddressSpaceInfo::Type type) { +    const std::size_t index{static_cast<std::size_t>(type)}; +    switch (width) { +    case 32: +        ASSERT(IsAllowed32BitType(type)); +        return AddressSpaceInfos[AddressSpaceIndices32Bit[index]].GetSize(); +    case 36: +        ASSERT(IsAllowed36BitType(type)); +        return AddressSpaceInfos[AddressSpaceIndices36Bit[index]].GetSize(); +    case 39: +        ASSERT(IsAllowed39BitType(type)); +        return AddressSpaceInfos[AddressSpaceIndices39Bit[index]].GetSize(); +    } +    UNREACHABLE(); +} + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/address_space_info.h b/src/core/hle/kernel/memory/address_space_info.h new file mode 100644 index 000000000..cc9a6421e --- /dev/null +++ b/src/core/hle/kernel/memory/address_space_info.h @@ -0,0 +1,54 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// This file references various implementation details from Atmosphère, an open-source firmware for +// the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX. + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Kernel::Memory { + +class AddressSpaceInfo final : NonCopyable { +public: +    enum class Type : u32 { +        Is32Bit = 0, +        Small64Bit = 1, +        Large64Bit = 2, +        Heap = 3, +        Stack = 4, +        Alias = 5, +        Count, +    }; + +private: +    std::size_t bit_width{}; +    std::size_t addr{}; +    std::size_t size{}; +    Type type{}; + +public: +    static u64 GetAddressSpaceStart(std::size_t width, Type type); +    static std::size_t GetAddressSpaceSize(std::size_t width, Type type); + +    constexpr AddressSpaceInfo(std::size_t bit_width, std::size_t addr, std::size_t size, Type type) +        : bit_width{bit_width}, addr{addr}, size{size}, type{type} {} + +    constexpr std::size_t GetWidth() const { +        return bit_width; +    } +    constexpr std::size_t GetAddress() const { +        return addr; +    } +    constexpr std::size_t GetSize() const { +        return size; +    } +    constexpr Type GetType() const { +        return type; +    } +}; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/memory_block.h b/src/core/hle/kernel/memory/memory_block.h new file mode 100644 index 000000000..e11043b60 --- /dev/null +++ b/src/core/hle/kernel/memory/memory_block.h @@ -0,0 +1,318 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// This file references various implementation details from Atmosphère, an open-source firmware for +// the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX. + +#pragma once + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/memory/memory_types.h" +#include "core/hle/kernel/svc_types.h" + +namespace Kernel::Memory { + +enum class MemoryState : u32 { +    None = 0, +    Mask = 0xFFFFFFFF, // TODO(bunnei): This should probable be 0xFF +    All = ~None, + +    FlagCanReprotect = (1 << 8), +    FlagCanDebug = (1 << 9), +    FlagCanUseIpc = (1 << 10), +    FlagCanUseNonDeviceIpc = (1 << 11), +    FlagCanUseNonSecureIpc = (1 << 12), +    FlagMapped = (1 << 13), +    FlagCode = (1 << 14), +    FlagCanAlias = (1 << 15), +    FlagCanCodeAlias = (1 << 16), +    FlagCanTransfer = (1 << 17), +    FlagCanQueryPhysical = (1 << 18), +    FlagCanDeviceMap = (1 << 19), +    FlagCanAlignedDeviceMap = (1 << 20), +    FlagCanIpcUserBuffer = (1 << 21), +    FlagReferenceCounted = (1 << 22), +    FlagCanMapProcess = (1 << 23), +    FlagCanChangeAttribute = (1 << 24), +    FlagCanCodeMemory = (1 << 25), + +    FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | +                FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical | +                FlagCanDeviceMap | FlagCanAlignedDeviceMap | FlagCanIpcUserBuffer | +                FlagReferenceCounted | FlagCanChangeAttribute, + +    FlagsCode = FlagCanDebug | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc | +                FlagMapped | FlagCode | FlagCanQueryPhysical | FlagCanDeviceMap | +                FlagCanAlignedDeviceMap | FlagReferenceCounted, + +    FlagsMisc = FlagMapped | FlagReferenceCounted | FlagCanQueryPhysical | FlagCanDeviceMap, + +    Free = static_cast<u32>(Svc::MemoryState::Free), +    Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped, +    Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical, +    Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess, +    CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess | +               FlagCanCodeMemory, +    Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted, +    Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory, + +    AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess | +                FlagCanCodeAlias, +    AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData | +                    FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory, + +    Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap | +          FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + +    Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap | +            FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + +    ThreadLocal = +        static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagReferenceCounted, + +    Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc | +                 FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc | +                 FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + +    SharedTransfered = static_cast<u32>(Svc::MemoryState::SharedTransfered) | FlagsMisc | +                       FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + +    SharedCode = static_cast<u32>(Svc::MemoryState::SharedCode) | FlagMapped | +                 FlagReferenceCounted | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + +    Inaccessible = static_cast<u32>(Svc::MemoryState::Inaccessible), + +    NonSecureIpc = static_cast<u32>(Svc::MemoryState::NonSecureIpc) | FlagsMisc | +                   FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc, + +    NonDeviceIpc = +        static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc, + +    Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped, + +    GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped | +                    FlagReferenceCounted | FlagCanDebug, +    CodeOut = static_cast<u32>(Svc::MemoryState::CodeOut) | FlagMapped | FlagReferenceCounted, +}; +DECLARE_ENUM_FLAG_OPERATORS(MemoryState); + +static_assert(static_cast<u32>(MemoryState::Free) == 0x00000000); +static_assert(static_cast<u32>(MemoryState::Io) == 0x00002001); +static_assert(static_cast<u32>(MemoryState::Static) == 0x00042002); +static_assert(static_cast<u32>(MemoryState::Code) == 0x00DC7E03); +static_assert(static_cast<u32>(MemoryState::CodeData) == 0x03FEBD04); +static_assert(static_cast<u32>(MemoryState::Normal) == 0x037EBD05); +static_assert(static_cast<u32>(MemoryState::Shared) == 0x00402006); +static_assert(static_cast<u32>(MemoryState::AliasCode) == 0x00DD7E08); +static_assert(static_cast<u32>(MemoryState::AliasCodeData) == 0x03FFBD09); +static_assert(static_cast<u32>(MemoryState::Ipc) == 0x005C3C0A); +static_assert(static_cast<u32>(MemoryState::Stack) == 0x005C3C0B); +static_assert(static_cast<u32>(MemoryState::ThreadLocal) == 0x0040200C); +static_assert(static_cast<u32>(MemoryState::Transfered) == 0x015C3C0D); +static_assert(static_cast<u32>(MemoryState::SharedTransfered) == 0x005C380E); +static_assert(static_cast<u32>(MemoryState::SharedCode) == 0x0040380F); +static_assert(static_cast<u32>(MemoryState::Inaccessible) == 0x00000010); +static_assert(static_cast<u32>(MemoryState::NonSecureIpc) == 0x005C3811); +static_assert(static_cast<u32>(MemoryState::NonDeviceIpc) == 0x004C2812); +static_assert(static_cast<u32>(MemoryState::Kernel) == 0x00002013); +static_assert(static_cast<u32>(MemoryState::GeneratedCode) == 0x00402214); +static_assert(static_cast<u32>(MemoryState::CodeOut) == 0x00402015); + +enum class MemoryPermission : u8 { +    None = 0, +    Mask = static_cast<u8>(~None), + +    Read = 1 << 0, +    Write = 1 << 1, +    Execute = 1 << 2, + +    ReadAndWrite = Read | Write, +    ReadAndExecute = Read | Execute, + +    UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write | +                               Svc::MemoryPermission::Execute), +}; +DECLARE_ENUM_FLAG_OPERATORS(MemoryPermission); + +enum class MemoryAttribute : u8 { +    None = 0x00, +    Mask = 0x7F, +    All = Mask, +    DontCareMask = 0x80, + +    Locked = static_cast<u8>(Svc::MemoryAttribute::Locked), +    IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked), +    DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared), +    Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached), + +    IpcAndDeviceMapped = IpcLocked | DeviceShared, +    LockedAndIpcLocked = Locked | IpcLocked, +    DeviceSharedAndUncached = DeviceShared | Uncached +}; +DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute); + +static_assert((static_cast<u8>(MemoryAttribute::Mask) & +               static_cast<u8>(MemoryAttribute::DontCareMask)) == 0); + +struct MemoryInfo { +    VAddr addr{}; +    std::size_t size{}; +    MemoryState state{}; +    MemoryPermission perm{}; +    MemoryAttribute attribute{}; +    MemoryPermission original_perm{}; +    u16 ipc_lock_count{}; +    u16 device_use_count{}; + +    constexpr Svc::MemoryInfo GetSvcMemoryInfo() const { +        return { +            addr, +            size, +            static_cast<Svc::MemoryState>(state & MemoryState::Mask), +            static_cast<Svc::MemoryAttribute>(attribute & MemoryAttribute::Mask), +            static_cast<Svc::MemoryPermission>(perm & MemoryPermission::UserMask), +            ipc_lock_count, +            device_use_count, +        }; +    } + +    constexpr VAddr GetAddress() const { +        return addr; +    } +    constexpr std::size_t GetSize() const { +        return size; +    } +    constexpr std::size_t GetNumPages() const { +        return GetSize() / PageSize; +    } +    constexpr VAddr GetEndAddress() const { +        return GetAddress() + GetSize(); +    } +    constexpr VAddr GetLastAddress() const { +        return GetEndAddress() - 1; +    } +}; + +class MemoryBlock final { +    friend class MemoryBlockManager; + +private: +    VAddr addr{}; +    std::size_t num_pages{}; +    MemoryState state{MemoryState::None}; +    u16 ipc_lock_count{}; +    u16 device_use_count{}; +    MemoryPermission perm{MemoryPermission::None}; +    MemoryPermission original_perm{MemoryPermission::None}; +    MemoryAttribute attribute{MemoryAttribute::None}; + +public: +    static constexpr int Compare(const MemoryBlock& lhs, const MemoryBlock& rhs) { +        if (lhs.GetAddress() < rhs.GetAddress()) { +            return -1; +        } else if (lhs.GetAddress() <= rhs.GetLastAddress()) { +            return 0; +        } else { +            return 1; +        } +    } + +public: +    constexpr MemoryBlock() = default; +    constexpr MemoryBlock(VAddr addr, std::size_t num_pages, MemoryState state, +                          MemoryPermission perm, MemoryAttribute attribute) +        : addr{addr}, num_pages(num_pages), state{state}, perm{perm}, attribute{attribute} {} + +    constexpr VAddr GetAddress() const { +        return addr; +    } + +    constexpr std::size_t GetNumPages() const { +        return num_pages; +    } + +    constexpr std::size_t GetSize() const { +        return GetNumPages() * PageSize; +    } + +    constexpr VAddr GetEndAddress() const { +        return GetAddress() + GetSize(); +    } + +    constexpr VAddr GetLastAddress() const { +        return GetEndAddress() - 1; +    } + +    constexpr MemoryInfo GetMemoryInfo() const { +        return { +            GetAddress(), GetSize(),     state,          perm, +            attribute,    original_perm, ipc_lock_count, device_use_count, +        }; +    } + +private: +    constexpr bool HasProperties(MemoryState s, MemoryPermission p, MemoryAttribute a) const { +        constexpr MemoryAttribute AttributeIgnoreMask{MemoryAttribute::DontCareMask | +                                                      MemoryAttribute::IpcLocked | +                                                      MemoryAttribute::DeviceShared}; +        return state == s && perm == p && +               (attribute | AttributeIgnoreMask) == (a | AttributeIgnoreMask); +    } + +    constexpr bool HasSameProperties(const MemoryBlock& rhs) const { +        return state == rhs.state && perm == rhs.perm && original_perm == rhs.original_perm && +               attribute == rhs.attribute && ipc_lock_count == rhs.ipc_lock_count && +               device_use_count == rhs.device_use_count; +    } + +    constexpr bool Contains(VAddr start) const { +        return GetAddress() <= start && start <= GetEndAddress(); +    } + +    constexpr void Add(std::size_t count) { +        ASSERT(count > 0); +        ASSERT(GetAddress() + count * PageSize - 1 < GetEndAddress() + count * PageSize - 1); + +        num_pages += count; +    } + +    constexpr void Update(MemoryState new_state, MemoryPermission new_perm, +                          MemoryAttribute new_attribute) { +        ASSERT(original_perm == MemoryPermission::None); +        ASSERT((attribute & MemoryAttribute::IpcLocked) == MemoryAttribute::None); + +        state = new_state; +        perm = new_perm; + +        // TODO(bunnei): Is this right? +        attribute = static_cast<MemoryAttribute>( +            new_attribute /*| (attribute & (MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared))*/); +    } + +    constexpr MemoryBlock Split(VAddr split_addr) { +        ASSERT(GetAddress() < split_addr); +        ASSERT(Contains(split_addr)); +        ASSERT(Common::IsAligned(split_addr, PageSize)); + +        MemoryBlock block; +        block.addr = addr; +        block.num_pages = (split_addr - GetAddress()) / PageSize; +        block.state = state; +        block.ipc_lock_count = ipc_lock_count; +        block.device_use_count = device_use_count; +        block.perm = perm; +        block.original_perm = original_perm; +        block.attribute = attribute; + +        addr = split_addr; +        num_pages -= block.num_pages; + +        return block; +    } +}; +static_assert(std::is_trivially_destructible<MemoryBlock>::value); + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/memory_block_manager.cpp b/src/core/hle/kernel/memory/memory_block_manager.cpp new file mode 100644 index 000000000..1ebc126c0 --- /dev/null +++ b/src/core/hle/kernel/memory/memory_block_manager.cpp @@ -0,0 +1,190 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/kernel/memory/memory_block_manager.h" +#include "core/hle/kernel/memory/memory_types.h" + +namespace Kernel::Memory { + +MemoryBlockManager::MemoryBlockManager(VAddr start_addr, VAddr end_addr) +    : start_addr{start_addr}, end_addr{end_addr} { +    const u64 num_pages{(end_addr - start_addr) / PageSize}; +    memory_block_tree.emplace_back(start_addr, num_pages, MemoryState::Free, MemoryPermission::None, +                                   MemoryAttribute::None); +} + +MemoryBlockManager::iterator MemoryBlockManager::FindIterator(VAddr addr) { +    auto node{memory_block_tree.begin()}; +    while (node != end()) { +        const VAddr end_addr{node->GetNumPages() * PageSize + node->GetAddress()}; +        if (node->GetAddress() <= addr && end_addr - 1 >= addr) { +            return node; +        } +        node = std::next(node); +    } +    return end(); +} + +VAddr MemoryBlockManager::FindFreeArea(VAddr region_start, std::size_t region_num_pages, +                                       std::size_t num_pages, std::size_t align, std::size_t offset, +                                       std::size_t guard_pages) { +    if (num_pages == 0) { +        return {}; +    } + +    const VAddr region_end{region_start + region_num_pages * PageSize}; +    const VAddr region_last{region_end - 1}; +    for (auto it{FindIterator(region_start)}; it != memory_block_tree.cend(); it++) { +        const auto info{it->GetMemoryInfo()}; +        if (region_last < info.GetAddress()) { +            break; +        } + +        if (info.state != MemoryState::Free) { +            continue; +        } + +        VAddr area{(info.GetAddress() <= region_start) ? region_start : info.GetAddress()}; +        area += guard_pages * PageSize; + +        const VAddr offset_area{Common::AlignDown(area, align) + offset}; +        area = (area <= offset_area) ? offset_area : offset_area + align; + +        const VAddr area_end{area + num_pages * PageSize + guard_pages * PageSize}; +        const VAddr area_last{area_end - 1}; + +        if (info.GetAddress() <= area && area < area_last && area_last <= region_last && +            area_last <= info.GetLastAddress()) { +            return area; +        } +    } + +    return {}; +} + +void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState prev_state, +                                MemoryPermission prev_perm, MemoryAttribute prev_attribute, +                                MemoryState state, MemoryPermission perm, +                                MemoryAttribute attribute) { +    const std::size_t prev_count{memory_block_tree.size()}; +    const VAddr end_addr{addr + num_pages * PageSize}; +    iterator node{memory_block_tree.begin()}; + +    prev_attribute |= MemoryAttribute::IpcAndDeviceMapped; + +    while (node != memory_block_tree.end()) { +        MemoryBlock* block{&(*node)}; +        iterator next_node{std::next(node)}; +        const VAddr cur_addr{block->GetAddress()}; +        const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; + +        if (addr < cur_end_addr && cur_addr < end_addr) { +            if (!block->HasProperties(prev_state, prev_perm, prev_attribute)) { +                node = next_node; +                continue; +            } + +            iterator new_node{node}; +            if (addr > cur_addr) { +                memory_block_tree.insert(node, block->Split(addr)); +            } + +            if (end_addr < cur_end_addr) { +                new_node = memory_block_tree.insert(node, block->Split(end_addr)); +            } + +            new_node->Update(state, perm, attribute); + +            MergeAdjacent(new_node, next_node); +        } + +        if (cur_end_addr - 1 >= end_addr - 1) { +            break; +        } + +        node = next_node; +    } +} + +void MemoryBlockManager::Update(VAddr addr, std::size_t num_pages, MemoryState state, +                                MemoryPermission perm, MemoryAttribute attribute) { +    const std::size_t prev_count{memory_block_tree.size()}; +    const VAddr end_addr{addr + num_pages * PageSize}; +    iterator node{memory_block_tree.begin()}; + +    while (node != memory_block_tree.end()) { +        MemoryBlock* block{&(*node)}; +        iterator next_node{std::next(node)}; +        const VAddr cur_addr{block->GetAddress()}; +        const VAddr cur_end_addr{block->GetNumPages() * PageSize + cur_addr}; + +        if (addr < cur_end_addr && cur_addr < end_addr) { +            iterator new_node{node}; + +            if (addr > cur_addr) { +                memory_block_tree.insert(node, block->Split(addr)); +            } + +            if (end_addr < cur_end_addr) { +                new_node = memory_block_tree.insert(node, block->Split(end_addr)); +            } + +            new_node->Update(state, perm, attribute); + +            MergeAdjacent(new_node, next_node); +        } + +        if (cur_end_addr - 1 >= end_addr - 1) { +            break; +        } + +        node = next_node; +    } +} + +void MemoryBlockManager::IterateForRange(VAddr start, VAddr end, IterateFunc&& func) { +    const_iterator it{FindIterator(start)}; +    MemoryInfo info{}; +    do { +        info = it->GetMemoryInfo(); +        func(info); +        it = std::next(it); +    } while (info.addr + info.size - 1 < end - 1 && it != cend()); +} + +void MemoryBlockManager::MergeAdjacent(iterator it, iterator& next_it) { +    MemoryBlock* block{&(*it)}; + +    auto EraseIt = [&](const iterator it_to_erase) { +        if (next_it == it_to_erase) { +            next_it = std::next(next_it); +        } +        memory_block_tree.erase(it_to_erase); +    }; + +    if (it != memory_block_tree.begin()) { +        MemoryBlock* prev{&(*std::prev(it))}; + +        if (block->HasSameProperties(*prev)) { +            const iterator prev_it{std::prev(it)}; + +            prev->Add(block->GetNumPages()); +            EraseIt(it); + +            it = prev_it; +            block = prev; +        } +    } + +    if (it != cend()) { +        const MemoryBlock* const next{&(*std::next(it))}; + +        if (block->HasSameProperties(*next)) { +            block->Add(next->GetNumPages()); +            EraseIt(std::next(it)); +        } +    } +} + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/memory_block_manager.h b/src/core/hle/kernel/memory/memory_block_manager.h new file mode 100644 index 000000000..0f2270f0f --- /dev/null +++ b/src/core/hle/kernel/memory/memory_block_manager.h @@ -0,0 +1,64 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <functional> +#include <list> +#include <memory> + +#include "common/common_types.h" +#include "core/hle/kernel/memory/memory_block.h" + +namespace Kernel::Memory { + +class MemoryBlockManager final { +public: +    using MemoryBlockTree = std::list<MemoryBlock>; +    using iterator = MemoryBlockTree::iterator; +    using const_iterator = MemoryBlockTree::const_iterator; + +public: +    MemoryBlockManager(VAddr start_addr, VAddr end_addr); + +    iterator end() { +        return memory_block_tree.end(); +    } +    const_iterator end() const { +        return memory_block_tree.end(); +    } +    const_iterator cend() const { +        return memory_block_tree.cend(); +    } + +    iterator FindIterator(VAddr addr); + +    VAddr FindFreeArea(VAddr region_start, std::size_t region_num_pages, std::size_t num_pages, +                       std::size_t align, std::size_t offset, std::size_t guard_pages); + +    void Update(VAddr addr, std::size_t num_pages, MemoryState prev_state, +                MemoryPermission prev_perm, MemoryAttribute prev_attribute, MemoryState state, +                MemoryPermission perm, MemoryAttribute attribute); + +    void Update(VAddr addr, std::size_t num_pages, MemoryState state, +                MemoryPermission perm = MemoryPermission::None, +                MemoryAttribute attribute = MemoryAttribute::None); + +    using IterateFunc = std::function<void(const MemoryInfo&)>; +    void IterateForRange(VAddr start, VAddr end, IterateFunc&& func); + +    MemoryBlock& FindBlock(VAddr addr) { +        return *FindIterator(addr); +    } + +private: +    void MergeAdjacent(iterator it, iterator& next_it); + +    const VAddr start_addr; +    const VAddr end_addr; + +    MemoryBlockTree memory_block_tree; +}; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/memory_layout.h b/src/core/hle/kernel/memory/memory_layout.h new file mode 100644 index 000000000..830c6f0d7 --- /dev/null +++ b/src/core/hle/kernel/memory/memory_layout.h @@ -0,0 +1,73 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Kernel::Memory { + +class MemoryRegion final { +    friend class MemoryLayout; + +public: +    constexpr PAddr StartAddress() const { +        return start_address; +    } + +    constexpr PAddr EndAddress() const { +        return end_address; +    } + +private: +    constexpr MemoryRegion() = default; +    constexpr MemoryRegion(PAddr start_address, PAddr end_address) +        : start_address{start_address}, end_address{end_address} {} + +    const PAddr start_address{}; +    const PAddr end_address{}; +}; + +class MemoryLayout final { +public: +    constexpr const MemoryRegion& Application() const { +        return application; +    } + +    constexpr const MemoryRegion& Applet() const { +        return applet; +    } + +    constexpr const MemoryRegion& System() const { +        return system; +    } + +    static constexpr MemoryLayout GetDefaultLayout() { +        constexpr std::size_t application_size{0xcd500000}; +        constexpr std::size_t applet_size{0x1fb00000}; +        constexpr PAddr application_start_address{Core::DramMemoryMap::End - application_size}; +        constexpr PAddr application_end_address{Core::DramMemoryMap::End}; +        constexpr PAddr applet_start_address{application_start_address - applet_size}; +        constexpr PAddr applet_end_address{applet_start_address + applet_size}; +        constexpr PAddr system_start_address{Core::DramMemoryMap::SlabHeapEnd}; +        constexpr PAddr system_end_address{applet_start_address}; +        return {application_start_address, application_end_address, applet_start_address, +                applet_end_address,        system_start_address,    system_end_address}; +    } + +private: +    constexpr MemoryLayout(PAddr application_start_address, std::size_t application_size, +                           PAddr applet_start_address, std::size_t applet_size, +                           PAddr system_start_address, std::size_t system_size) +        : application{application_start_address, application_size}, +          applet{applet_start_address, applet_size}, system{system_start_address, system_size} {} + +    const MemoryRegion application; +    const MemoryRegion applet; +    const MemoryRegion system; + +    const PAddr start_address{}; +}; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/memory_manager.cpp b/src/core/hle/kernel/memory/memory_manager.cpp new file mode 100644 index 000000000..3cd4f9e85 --- /dev/null +++ b/src/core/hle/kernel/memory/memory_manager.cpp @@ -0,0 +1,176 @@ +// Copyright 2020 yuzu 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/common_types.h" +#include "common/scope_exit.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/memory/memory_manager.h" +#include "core/hle/kernel/memory/page_linked_list.h" + +namespace Kernel::Memory { + +std::size_t MemoryManager::Impl::Initialize(Pool new_pool, u64 start_address, u64 end_address) { +    const auto size{end_address - start_address}; + +    // Calculate metadata sizes +    const auto ref_count_size{(size / PageSize) * sizeof(u16)}; +    const auto optimize_map_size{(Common::AlignUp((size / PageSize), 64) / 64) * sizeof(u64)}; +    const auto manager_size{Common::AlignUp(optimize_map_size + ref_count_size, PageSize)}; +    const auto page_heap_size{PageHeap::CalculateMetadataOverheadSize(size)}; +    const auto total_metadata_size{manager_size + page_heap_size}; +    ASSERT(manager_size <= total_metadata_size); +    ASSERT(Common::IsAligned(total_metadata_size, PageSize)); + +    // Setup region +    pool = new_pool; + +    // Initialize the manager's KPageHeap +    heap.Initialize(start_address, size, page_heap_size); + +    // Free the memory to the heap +    heap.Free(start_address, size / PageSize); + +    // Update the heap's used size +    heap.UpdateUsedSize(); + +    return total_metadata_size; +} + +void MemoryManager::InitializeManager(Pool pool, u64 start_address, u64 end_address) { +    ASSERT(pool < Pool::Count); +    managers[static_cast<std::size_t>(pool)].Initialize(pool, start_address, end_address); +} + +VAddr MemoryManager::AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool, +                                        Direction dir) { +    // Early return if we're allocating no pages +    if (num_pages == 0) { +        return {}; +    } + +    // Lock the pool that we're allocating from +    const auto pool_index{static_cast<std::size_t>(pool)}; +    std::lock_guard lock{pool_locks[pool_index]}; + +    // Choose a heap based on our page size request +    const s32 heap_index{PageHeap::GetAlignedBlockIndex(num_pages, align_pages)}; + +    // Loop, trying to iterate from each block +    // TODO (bunnei): Support multiple managers +    Impl& chosen_manager{managers[pool_index]}; +    VAddr allocated_block{chosen_manager.AllocateBlock(heap_index)}; + +    // If we failed to allocate, quit now +    if (!allocated_block) { +        return {}; +    } + +    // If we allocated more than we need, free some +    const auto allocated_pages{PageHeap::GetBlockNumPages(heap_index)}; +    if (allocated_pages > num_pages) { +        chosen_manager.Free(allocated_block + num_pages * PageSize, allocated_pages - num_pages); +    } + +    return allocated_block; +} + +ResultCode MemoryManager::Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool, +                                   Direction dir) { +    ASSERT(page_list.GetNumPages() == 0); + +    // Early return if we're allocating no pages +    if (num_pages == 0) { +        return RESULT_SUCCESS; +    } + +    // Lock the pool that we're allocating from +    const auto pool_index{static_cast<std::size_t>(pool)}; +    std::lock_guard lock{pool_locks[pool_index]}; + +    // Choose a heap based on our page size request +    const s32 heap_index{PageHeap::GetBlockIndex(num_pages)}; +    if (heap_index < 0) { +        return ERR_OUT_OF_MEMORY; +    } + +    // TODO (bunnei): Support multiple managers +    Impl& chosen_manager{managers[pool_index]}; + +    // Ensure that we don't leave anything un-freed +    auto group_guard = detail::ScopeExit([&] { +        for (const auto& it : page_list.Nodes()) { +            const auto num_pages{std::min( +                it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; +            chosen_manager.Free(it.GetAddress(), num_pages); +        } +    }); + +    // Keep allocating until we've allocated all our pages +    for (s32 index{heap_index}; index >= 0 && num_pages > 0; index--) { +        const auto pages_per_alloc{PageHeap::GetBlockNumPages(index)}; + +        while (num_pages >= pages_per_alloc) { +            // Allocate a block +            VAddr allocated_block{chosen_manager.AllocateBlock(index)}; +            if (!allocated_block) { +                break; +            } + +            // Safely add it to our group +            { +                auto block_guard = detail::ScopeExit( +                    [&] { chosen_manager.Free(allocated_block, pages_per_alloc); }); + +                if (const ResultCode result{page_list.AddBlock(allocated_block, pages_per_alloc)}; +                    result.IsError()) { +                    return result; +                } + +                block_guard.Cancel(); +            } + +            num_pages -= pages_per_alloc; +        } +    } + +    // Only succeed if we allocated as many pages as we wanted +    ASSERT(num_pages >= 0); +    if (num_pages) { +        return ERR_OUT_OF_MEMORY; +    } + +    // We succeeded! +    group_guard.Cancel(); +    return RESULT_SUCCESS; +} + +ResultCode MemoryManager::Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool, +                               Direction dir) { +    // Early return if we're freeing no pages +    if (!num_pages) { +        return RESULT_SUCCESS; +    } + +    // Lock the pool that we're freeing from +    const auto pool_index{static_cast<std::size_t>(pool)}; +    std::lock_guard lock{pool_locks[pool_index]}; + +    // TODO (bunnei): Support multiple managers +    Impl& chosen_manager{managers[pool_index]}; + +    // Free all of the pages +    for (const auto& it : page_list.Nodes()) { +        const auto num_pages{std::min( +            it.GetNumPages(), (chosen_manager.GetEndAddress() - it.GetAddress()) / PageSize)}; +        chosen_manager.Free(it.GetAddress(), num_pages); +    } + +    return RESULT_SUCCESS; +} + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/memory_manager.h b/src/core/hle/kernel/memory/memory_manager.h new file mode 100644 index 000000000..b078d7a5e --- /dev/null +++ b/src/core/hle/kernel/memory/memory_manager.h @@ -0,0 +1,97 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <mutex> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/kernel/memory/page_heap.h" +#include "core/hle/result.h" + +namespace Kernel::Memory { + +class PageLinkedList; + +class MemoryManager final : NonCopyable { +public: +    enum class Pool : u32 { +        Application = 0, +        Applet = 1, +        System = 2, +        SystemNonSecure = 3, + +        Count, + +        Shift = 4, +        Mask = (0xF << Shift), +    }; + +    enum class Direction : u32 { +        FromFront = 0, +        FromBack = 1, + +        Shift = 0, +        Mask = (0xF << Shift), +    }; + +    MemoryManager() = default; + +    constexpr std::size_t GetSize(Pool pool) const { +        return managers[static_cast<std::size_t>(pool)].GetSize(); +    } + +    void InitializeManager(Pool pool, u64 start_address, u64 end_address); +    VAddr AllocateContinuous(std::size_t num_pages, std::size_t align_pages, Pool pool, +                             Direction dir = Direction::FromFront); +    ResultCode Allocate(PageLinkedList& page_list, std::size_t num_pages, Pool pool, +                        Direction dir = Direction::FromFront); +    ResultCode Free(PageLinkedList& page_list, std::size_t num_pages, Pool pool, +                    Direction dir = Direction::FromFront); + +    static constexpr std::size_t MaxManagerCount = 10; + +private: +    class Impl final : NonCopyable { +    private: +        using RefCount = u16; + +    private: +        PageHeap heap; +        Pool pool{}; + +    public: +        Impl() = default; + +        std::size_t Initialize(Pool new_pool, u64 start_address, u64 end_address); + +        VAddr AllocateBlock(s32 index) { +            return heap.AllocateBlock(index); +        } + +        void Free(VAddr addr, std::size_t num_pages) { +            heap.Free(addr, num_pages); +        } + +        constexpr std::size_t GetSize() const { +            return heap.GetSize(); +        } + +        constexpr VAddr GetAddress() const { +            return heap.GetAddress(); +        } + +        constexpr VAddr GetEndAddress() const { +            return heap.GetEndAddress(); +        } +    }; + +private: +    std::array<std::mutex, static_cast<std::size_t>(Pool::Count)> pool_locks; +    std::array<Impl, MaxManagerCount> managers; +}; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/memory_types.h b/src/core/hle/kernel/memory/memory_types.h new file mode 100644 index 000000000..a75bf77c0 --- /dev/null +++ b/src/core/hle/kernel/memory/memory_types.h @@ -0,0 +1,18 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> + +#include "common/common_types.h" + +namespace Kernel::Memory { + +constexpr std::size_t PageBits{12}; +constexpr std::size_t PageSize{1 << PageBits}; + +using Page = std::array<u8, PageSize>; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/page_heap.cpp b/src/core/hle/kernel/memory/page_heap.cpp new file mode 100644 index 000000000..efcbb3cad --- /dev/null +++ b/src/core/hle/kernel/memory/page_heap.cpp @@ -0,0 +1,119 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// This file references various implementation details from Atmosphère, an open-source firmware for +// the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX. + +#include "core/core.h" +#include "core/hle/kernel/memory/page_heap.h" +#include "core/memory.h" + +namespace Kernel::Memory { + +void PageHeap::Initialize(VAddr address, std::size_t size, std::size_t metadata_size) { +    // Check our assumptions +    ASSERT(Common::IsAligned((address), PageSize)); +    ASSERT(Common::IsAligned(size, PageSize)); + +    // Set our members +    heap_address = address; +    heap_size = size; + +    // Setup bitmaps +    metadata.resize(metadata_size / sizeof(u64)); +    u64* cur_bitmap_storage{metadata.data()}; +    for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { +        const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; +        const std::size_t next_block_shift{ +            (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; +        cur_bitmap_storage = blocks[i].Initialize(heap_address, heap_size, cur_block_shift, +                                                  next_block_shift, cur_bitmap_storage); +    } +} + +VAddr PageHeap::AllocateBlock(s32 index) { +    const std::size_t needed_size{blocks[index].GetSize()}; + +    for (s32 i{index}; i < static_cast<s32>(MemoryBlockPageShifts.size()); i++) { +        if (const VAddr addr{blocks[i].PopBlock()}; addr) { +            if (const std::size_t allocated_size{blocks[i].GetSize()}; +                allocated_size > needed_size) { +                Free(addr + needed_size, (allocated_size - needed_size) / PageSize); +            } +            return addr; +        } +    } + +    return 0; +} + +void PageHeap::FreeBlock(VAddr block, s32 index) { +    do { +        block = blocks[index++].PushBlock(block); +    } while (block != 0); +} + +void PageHeap::Free(VAddr addr, std::size_t num_pages) { +    // Freeing no pages is a no-op +    if (num_pages == 0) { +        return; +    } + +    // Find the largest block size that we can free, and free as many as possible +    s32 big_index{static_cast<s32>(MemoryBlockPageShifts.size()) - 1}; +    const VAddr start{addr}; +    const VAddr end{(num_pages * PageSize) + addr}; +    VAddr before_start{start}; +    VAddr before_end{start}; +    VAddr after_start{end}; +    VAddr after_end{end}; +    while (big_index >= 0) { +        const std::size_t block_size{blocks[big_index].GetSize()}; +        const VAddr big_start{Common::AlignUp((start), block_size)}; +        const VAddr big_end{Common::AlignDown((end), block_size)}; +        if (big_start < big_end) { +            // Free as many big blocks as we can +            for (auto block{big_start}; block < big_end; block += block_size) { +                FreeBlock(block, big_index); +            } +            before_end = big_start; +            after_start = big_end; +            break; +        } +        big_index--; +    } +    ASSERT(big_index >= 0); + +    // Free space before the big blocks +    for (s32 i{big_index - 1}; i >= 0; i--) { +        const std::size_t block_size{blocks[i].GetSize()}; +        while (before_start + block_size <= before_end) { +            before_end -= block_size; +            FreeBlock(before_end, i); +        } +    } + +    // Free space after the big blocks +    for (s32 i{big_index - 1}; i >= 0; i--) { +        const std::size_t block_size{blocks[i].GetSize()}; +        while (after_start + block_size <= after_end) { +            FreeBlock(after_start, i); +            after_start += block_size; +        } +    } +} + +std::size_t PageHeap::CalculateMetadataOverheadSize(std::size_t region_size) { +    std::size_t overhead_size = 0; +    for (std::size_t i = 0; i < MemoryBlockPageShifts.size(); i++) { +        const std::size_t cur_block_shift{MemoryBlockPageShifts[i]}; +        const std::size_t next_block_shift{ +            (i != MemoryBlockPageShifts.size() - 1) ? MemoryBlockPageShifts[i + 1] : 0}; +        overhead_size += PageHeap::Block::CalculateMetadataOverheadSize( +            region_size, cur_block_shift, next_block_shift); +    } +    return Common::AlignUp(overhead_size, PageSize); +} + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/page_heap.h b/src/core/hle/kernel/memory/page_heap.h new file mode 100644 index 000000000..380c3f5a1 --- /dev/null +++ b/src/core/hle/kernel/memory/page_heap.h @@ -0,0 +1,370 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// This file references various implementation details from Atmosphère, an open-source firmware for +// the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX. + +#pragma once + +#include <array> +#include <vector> + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/bit_util.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/kernel/memory/memory_types.h" + +namespace Kernel::Memory { + +class PageHeap final : NonCopyable { +public: +    static constexpr s32 GetAlignedBlockIndex(std::size_t num_pages, std::size_t align_pages) { +        const auto target_pages{std::max(num_pages, align_pages)}; +        for (std::size_t i = 0; i < NumMemoryBlockPageShifts; i++) { +            if (target_pages <= +                (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { +                return static_cast<s32>(i); +            } +        } +        return -1; +    } + +    static constexpr s32 GetBlockIndex(std::size_t num_pages) { +        for (s32 i{static_cast<s32>(NumMemoryBlockPageShifts) - 1}; i >= 0; i--) { +            if (num_pages >= (static_cast<std::size_t>(1) << MemoryBlockPageShifts[i]) / PageSize) { +                return i; +            } +        } +        return -1; +    } + +    static constexpr std::size_t GetBlockSize(std::size_t index) { +        return static_cast<std::size_t>(1) << MemoryBlockPageShifts[index]; +    } + +    static constexpr std::size_t GetBlockNumPages(std::size_t index) { +        return GetBlockSize(index) / PageSize; +    } + +private: +    static constexpr std::size_t NumMemoryBlockPageShifts{7}; +    static constexpr std::array<std::size_t, NumMemoryBlockPageShifts> MemoryBlockPageShifts{ +        0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, +    }; + +    class Block final : NonCopyable { +    private: +        class Bitmap final : NonCopyable { +        public: +            static constexpr std::size_t MaxDepth{4}; + +        private: +            std::array<u64*, MaxDepth> bit_storages{}; +            std::size_t num_bits{}; +            std::size_t used_depths{}; + +        public: +            constexpr Bitmap() = default; + +            constexpr std::size_t GetNumBits() const { +                return num_bits; +            } +            constexpr s32 GetHighestDepthIndex() const { +                return static_cast<s32>(used_depths) - 1; +            } + +            constexpr u64* Initialize(u64* storage, std::size_t size) { +                //* Initially, everything is un-set +                num_bits = 0; + +                // Calculate the needed bitmap depth +                used_depths = static_cast<std::size_t>(GetRequiredDepth(size)); +                ASSERT(used_depths <= MaxDepth); + +                // Set the bitmap pointers +                for (s32 depth{GetHighestDepthIndex()}; depth >= 0; depth--) { +                    bit_storages[depth] = storage; +                    size = Common::AlignUp(size, 64) / 64; +                    storage += size; +                } + +                return storage; +            } + +            s64 FindFreeBlock() const { +                uintptr_t offset{}; +                s32 depth{}; + +                do { +                    const u64 v{bit_storages[depth][offset]}; +                    if (v == 0) { +                        // Non-zero depth indicates that a previous level had a free block +                        ASSERT(depth == 0); +                        return -1; +                    } +                    offset = offset * 64 + Common::CountTrailingZeroes64(v); +                    ++depth; +                } while (depth < static_cast<s32>(used_depths)); + +                return static_cast<s64>(offset); +            } + +            constexpr void SetBit(std::size_t offset) { +                SetBit(GetHighestDepthIndex(), offset); +                num_bits++; +            } + +            constexpr void ClearBit(std::size_t offset) { +                ClearBit(GetHighestDepthIndex(), offset); +                num_bits--; +            } + +            constexpr bool ClearRange(std::size_t offset, std::size_t count) { +                const s32 depth{GetHighestDepthIndex()}; +                const auto bit_ind{offset / 64}; +                u64* bits{bit_storages[depth]}; +                if (count < 64) { +                    const auto shift{offset % 64}; +                    ASSERT(shift + count <= 64); +                    // Check that all the bits are set +                    const u64 mask{((1ULL << count) - 1) << shift}; +                    u64 v{bits[bit_ind]}; +                    if ((v & mask) != mask) { +                        return false; +                    } + +                    // Clear the bits +                    v &= ~mask; +                    bits[bit_ind] = v; +                    if (v == 0) { +                        ClearBit(depth - 1, bit_ind); +                    } +                } else { +                    ASSERT(offset % 64 == 0); +                    ASSERT(count % 64 == 0); +                    // Check that all the bits are set +                    std::size_t remaining{count}; +                    std::size_t i = 0; +                    do { +                        if (bits[bit_ind + i++] != ~u64(0)) { +                            return false; +                        } +                        remaining -= 64; +                    } while (remaining > 0); + +                    // Clear the bits +                    remaining = count; +                    i = 0; +                    do { +                        bits[bit_ind + i] = 0; +                        ClearBit(depth - 1, bit_ind + i); +                        i++; +                        remaining -= 64; +                    } while (remaining > 0); +                } + +                num_bits -= count; +                return true; +            } + +        private: +            constexpr void SetBit(s32 depth, std::size_t offset) { +                while (depth >= 0) { +                    const auto ind{offset / 64}; +                    const auto which{offset % 64}; +                    const u64 mask{1ULL << which}; + +                    u64* bit{std::addressof(bit_storages[depth][ind])}; +                    const u64 v{*bit}; +                    ASSERT((v & mask) == 0); +                    *bit = v | mask; +                    if (v) { +                        break; +                    } +                    offset = ind; +                    depth--; +                } +            } + +            constexpr void ClearBit(s32 depth, std::size_t offset) { +                while (depth >= 0) { +                    const auto ind{offset / 64}; +                    const auto which{offset % 64}; +                    const u64 mask{1ULL << which}; + +                    u64* bit{std::addressof(bit_storages[depth][ind])}; +                    u64 v{*bit}; +                    ASSERT((v & mask) != 0); +                    v &= ~mask; +                    *bit = v; +                    if (v) { +                        break; +                    } +                    offset = ind; +                    depth--; +                } +            } + +        private: +            static constexpr s32 GetRequiredDepth(std::size_t region_size) { +                s32 depth = 0; +                while (true) { +                    region_size /= 64; +                    depth++; +                    if (region_size == 0) { +                        return depth; +                    } +                } +            } + +        public: +            static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size) { +                std::size_t overhead_bits = 0; +                for (s32 depth{GetRequiredDepth(region_size) - 1}; depth >= 0; depth--) { +                    region_size = Common::AlignUp(region_size, 64) / 64; +                    overhead_bits += region_size; +                } +                return overhead_bits * sizeof(u64); +            } +        }; + +    private: +        Bitmap bitmap; +        VAddr heap_address{}; +        uintptr_t end_offset{}; +        std::size_t block_shift{}; +        std::size_t next_block_shift{}; + +    public: +        constexpr Block() = default; + +        constexpr std::size_t GetShift() const { +            return block_shift; +        } +        constexpr std::size_t GetNextShift() const { +            return next_block_shift; +        } +        constexpr std::size_t GetSize() const { +            return static_cast<std::size_t>(1) << GetShift(); +        } +        constexpr std::size_t GetNumPages() const { +            return GetSize() / PageSize; +        } +        constexpr std::size_t GetNumFreeBlocks() const { +            return bitmap.GetNumBits(); +        } +        constexpr std::size_t GetNumFreePages() const { +            return GetNumFreeBlocks() * GetNumPages(); +        } + +        constexpr u64* Initialize(VAddr addr, std::size_t size, std::size_t bs, std::size_t nbs, +                                  u64* bit_storage) { +            // Set shifts +            block_shift = bs; +            next_block_shift = nbs; + +            // Align up the address +            VAddr end{addr + size}; +            const auto align{(next_block_shift != 0) ? (1ULL << next_block_shift) +                                                     : (1ULL << block_shift)}; +            addr = Common::AlignDown((addr), align); +            end = Common::AlignUp((end), align); + +            heap_address = addr; +            end_offset = (end - addr) / (1ULL << block_shift); +            return bitmap.Initialize(bit_storage, end_offset); +        } + +        constexpr VAddr PushBlock(VAddr address) { +            // Set the bit for the free block +            std::size_t offset{(address - heap_address) >> GetShift()}; +            bitmap.SetBit(offset); + +            // If we have a next shift, try to clear the blocks below and return the address +            if (GetNextShift()) { +                const auto diff{1ULL << (GetNextShift() - GetShift())}; +                offset = Common::AlignDown(offset, diff); +                if (bitmap.ClearRange(offset, diff)) { +                    return heap_address + (offset << GetShift()); +                } +            } + +            // We couldn't coalesce, or we're already as big as possible +            return 0; +        } + +        VAddr PopBlock() { +            // Find a free block +            const s64 soffset{bitmap.FindFreeBlock()}; +            if (soffset < 0) { +                return 0; +            } +            const auto offset{static_cast<std::size_t>(soffset)}; + +            // Update our tracking and return it +            bitmap.ClearBit(offset); +            return heap_address + (offset << GetShift()); +        } + +    public: +        static constexpr std::size_t CalculateMetadataOverheadSize(std::size_t region_size, +                                                                   std::size_t cur_block_shift, +                                                                   std::size_t next_block_shift) { +            const auto cur_block_size{(1ULL << cur_block_shift)}; +            const auto next_block_size{(1ULL << next_block_shift)}; +            const auto align{(next_block_shift != 0) ? next_block_size : cur_block_size}; +            return Bitmap::CalculateMetadataOverheadSize( +                (align * 2 + Common::AlignUp(region_size, align)) / cur_block_size); +        } +    }; + +public: +    PageHeap() = default; + +    constexpr VAddr GetAddress() const { +        return heap_address; +    } +    constexpr std::size_t GetSize() const { +        return heap_size; +    } +    constexpr VAddr GetEndAddress() const { +        return GetAddress() + GetSize(); +    } +    constexpr std::size_t GetPageOffset(VAddr block) const { +        return (block - GetAddress()) / PageSize; +    } + +    void Initialize(VAddr heap_address, std::size_t heap_size, std::size_t metadata_size); +    VAddr AllocateBlock(s32 index); +    void Free(VAddr addr, std::size_t num_pages); + +    void UpdateUsedSize() { +        used_size = heap_size - (GetNumFreePages() * PageSize); +    } + +    static std::size_t CalculateMetadataOverheadSize(std::size_t region_size); + +private: +    constexpr std::size_t GetNumFreePages() const { +        std::size_t num_free{}; + +        for (const auto& block : blocks) { +            num_free += block.GetNumFreePages(); +        } + +        return num_free; +    } + +    void FreeBlock(VAddr block, s32 index); + +    VAddr heap_address{}; +    std::size_t heap_size{}; +    std::size_t used_size{}; +    std::array<Block, NumMemoryBlockPageShifts> blocks{}; +    std::vector<u64> metadata; +}; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/page_linked_list.h b/src/core/hle/kernel/memory/page_linked_list.h new file mode 100644 index 000000000..0668d00c6 --- /dev/null +++ b/src/core/hle/kernel/memory/page_linked_list.h @@ -0,0 +1,93 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <list> + +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "core/hle/kernel/memory/memory_types.h" +#include "core/hle/result.h" + +namespace Kernel::Memory { + +class PageLinkedList final { +public: +    class Node final { +    public: +        constexpr Node(u64 addr, std::size_t num_pages) : addr{addr}, num_pages{num_pages} {} + +        constexpr u64 GetAddress() const { +            return addr; +        } + +        constexpr std::size_t GetNumPages() const { +            return num_pages; +        } + +    private: +        u64 addr{}; +        std::size_t num_pages{}; +    }; + +public: +    PageLinkedList() = default; +    PageLinkedList(u64 address, u64 num_pages) { +        ASSERT(AddBlock(address, num_pages).IsSuccess()); +    } + +    constexpr std::list<Node>& Nodes() { +        return nodes; +    } + +    constexpr const std::list<Node>& Nodes() const { +        return nodes; +    } + +    std::size_t GetNumPages() const { +        std::size_t num_pages = 0; +        for (const Node& node : nodes) { +            num_pages += node.GetNumPages(); +        } +        return num_pages; +    } + +    bool IsEqual(PageLinkedList& other) const { +        auto this_node = nodes.begin(); +        auto other_node = other.nodes.begin(); +        while (this_node != nodes.end() && other_node != other.nodes.end()) { +            if (this_node->GetAddress() != other_node->GetAddress() || +                this_node->GetNumPages() != other_node->GetNumPages()) { +                return false; +            } +            this_node = std::next(this_node); +            other_node = std::next(other_node); +        } + +        return this_node == nodes.end() && other_node == other.nodes.end(); +    } + +    ResultCode AddBlock(u64 address, u64 num_pages) { +        if (!num_pages) { +            return RESULT_SUCCESS; +        } +        if (!nodes.empty()) { +            const auto node = nodes.back(); +            if (node.GetAddress() + node.GetNumPages() * PageSize == address) { +                address = node.GetAddress(); +                num_pages += node.GetNumPages(); +                nodes.pop_back(); +            } +        } +        nodes.push_back({address, num_pages}); +        return RESULT_SUCCESS; +    } + +private: +    std::list<Node> nodes; +}; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp new file mode 100644 index 000000000..091e52ca4 --- /dev/null +++ b/src/core/hle/kernel/memory/page_table.cpp @@ -0,0 +1,1130 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/alignment.h" +#include "common/assert.h" +#include "common/scope_exit.h" +#include "core/core.h" +#include "core/device_memory.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory/address_space_info.h" +#include "core/hle/kernel/memory/memory_block.h" +#include "core/hle/kernel/memory/memory_block_manager.h" +#include "core/hle/kernel/memory/page_linked_list.h" +#include "core/hle/kernel/memory/page_table.h" +#include "core/hle/kernel/memory/system_control.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" +#include "core/memory.h" + +namespace Kernel::Memory { + +namespace { + +constexpr std::size_t GetAddressSpaceWidthFromType(FileSys::ProgramAddressSpaceType as_type) { +    switch (as_type) { +    case FileSys::ProgramAddressSpaceType::Is32Bit: +    case FileSys::ProgramAddressSpaceType::Is32BitNoMap: +        return 32; +    case FileSys::ProgramAddressSpaceType::Is36Bit: +        return 36; +    case FileSys::ProgramAddressSpaceType::Is39Bit: +        return 39; +    default: +        UNREACHABLE(); +        return {}; +    } +} + +constexpr u64 GetAddressInRange(const MemoryInfo& info, VAddr addr) { +    if (info.GetAddress() < addr) { +        return addr; +    } +    return info.GetAddress(); +} + +constexpr std::size_t GetSizeInRange(const MemoryInfo& info, VAddr start, VAddr end) { +    std::size_t size{info.GetSize()}; +    if (info.GetAddress() < start) { +        size -= start - info.GetAddress(); +    } +    if (info.GetEndAddress() > end) { +        size -= info.GetEndAddress() - end; +    } +    return size; +} + +} // namespace + +PageTable::PageTable(Core::System& system) : system{system} {} + +ResultCode PageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, +                                           bool enable_aslr, VAddr code_addr, std::size_t code_size, +                                           Memory::MemoryManager::Pool pool) { + +    const auto GetSpaceStart = [this](AddressSpaceInfo::Type type) { +        return AddressSpaceInfo::GetAddressSpaceStart(address_space_width, type); +    }; +    const auto GetSpaceSize = [this](AddressSpaceInfo::Type type) { +        return AddressSpaceInfo::GetAddressSpaceSize(address_space_width, type); +    }; + +    //  Set our width and heap/alias sizes +    address_space_width = GetAddressSpaceWidthFromType(as_type); +    const VAddr start = 0; +    const VAddr end{1ULL << address_space_width}; +    std::size_t alias_region_size{GetSpaceSize(AddressSpaceInfo::Type::Alias)}; +    std::size_t heap_region_size{GetSpaceSize(AddressSpaceInfo::Type::Heap)}; + +    ASSERT(start <= code_addr); +    ASSERT(code_addr < code_addr + code_size); +    ASSERT(code_addr + code_size - 1 <= end - 1); + +    // Adjust heap/alias size if we don't have an alias region +    if (as_type == FileSys::ProgramAddressSpaceType::Is32BitNoMap) { +        heap_region_size += alias_region_size; +        alias_region_size = 0; +    } + +    // Set code regions and determine remaining +    constexpr std::size_t RegionAlignment{2 * 1024 * 1024}; +    VAddr process_code_start{}; +    VAddr process_code_end{}; +    std::size_t stack_region_size{}; +    std::size_t kernel_map_region_size{}; + +    if (address_space_width == 39) { +        alias_region_size = GetSpaceSize(AddressSpaceInfo::Type::Alias); +        heap_region_size = GetSpaceSize(AddressSpaceInfo::Type::Heap); +        stack_region_size = GetSpaceSize(AddressSpaceInfo::Type::Stack); +        kernel_map_region_size = GetSpaceSize(AddressSpaceInfo::Type::Is32Bit); +        code_region_start = GetSpaceStart(AddressSpaceInfo::Type::Large64Bit); +        code_region_end = code_region_start + GetSpaceSize(AddressSpaceInfo::Type::Large64Bit); +        alias_code_region_start = code_region_start; +        alias_code_region_end = code_region_end; +        process_code_start = Common::AlignDown(code_addr, RegionAlignment); +        process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment); +    } else { +        stack_region_size = 0; +        kernel_map_region_size = 0; +        code_region_start = GetSpaceStart(AddressSpaceInfo::Type::Is32Bit); +        code_region_end = code_region_start + GetSpaceSize(AddressSpaceInfo::Type::Is32Bit); +        stack_region_start = code_region_start; +        alias_code_region_start = code_region_start; +        alias_code_region_end = GetSpaceStart(AddressSpaceInfo::Type::Small64Bit) + +                                GetSpaceSize(AddressSpaceInfo::Type::Small64Bit); +        stack_region_end = code_region_end; +        kernel_map_region_start = code_region_start; +        kernel_map_region_end = code_region_end; +        process_code_start = code_region_start; +        process_code_end = code_region_end; +    } + +    // Set other basic fields +    is_aslr_enabled = enable_aslr; +    address_space_start = start; +    address_space_end = end; +    is_kernel = false; + +    // Determine the region we can place our undetermineds in +    VAddr alloc_start{}; +    std::size_t alloc_size{}; +    if ((process_code_start - code_region_start) >= (end - process_code_end)) { +        alloc_start = code_region_start; +        alloc_size = process_code_start - code_region_start; +    } else { +        alloc_start = process_code_end; +        alloc_size = end - process_code_end; +    } +    const std::size_t needed_size{ +        (alias_region_size + heap_region_size + stack_region_size + kernel_map_region_size)}; +    if (alloc_size < needed_size) { +        UNREACHABLE(); +        return ERR_OUT_OF_MEMORY; +    } + +    const std::size_t remaining_size{alloc_size - needed_size}; + +    // Determine random placements for each region +    std::size_t alias_rnd{}, heap_rnd{}, stack_rnd{}, kmap_rnd{}; +    if (enable_aslr) { +        alias_rnd = SystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * +                    RegionAlignment; +        heap_rnd = SystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * +                   RegionAlignment; +        stack_rnd = SystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * +                    RegionAlignment; +        kmap_rnd = SystemControl::GenerateRandomRange(0, remaining_size / RegionAlignment) * +                   RegionAlignment; +    } + +    // Setup heap and alias regions +    alias_region_start = alloc_start + alias_rnd; +    alias_region_end = alias_region_start + alias_region_size; +    heap_region_start = alloc_start + heap_rnd; +    heap_region_end = heap_region_start + heap_region_size; + +    if (alias_rnd <= heap_rnd) { +        heap_region_start += alias_region_size; +        heap_region_end += alias_region_size; +    } else { +        alias_region_start += heap_region_size; +        alias_region_end += heap_region_size; +    } + +    // Setup stack region +    if (stack_region_size) { +        stack_region_start = alloc_start + stack_rnd; +        stack_region_end = stack_region_start + stack_region_size; + +        if (alias_rnd < stack_rnd) { +            stack_region_start += alias_region_size; +            stack_region_end += alias_region_size; +        } else { +            alias_region_start += stack_region_size; +            alias_region_end += stack_region_size; +        } + +        if (heap_rnd < stack_rnd) { +            stack_region_start += heap_region_size; +            stack_region_end += heap_region_size; +        } else { +            heap_region_start += stack_region_size; +            heap_region_end += stack_region_size; +        } +    } + +    // Setup kernel map region +    if (kernel_map_region_size) { +        kernel_map_region_start = alloc_start + kmap_rnd; +        kernel_map_region_end = kernel_map_region_start + kernel_map_region_size; + +        if (alias_rnd < kmap_rnd) { +            kernel_map_region_start += alias_region_size; +            kernel_map_region_end += alias_region_size; +        } else { +            alias_region_start += kernel_map_region_size; +            alias_region_end += kernel_map_region_size; +        } + +        if (heap_rnd < kmap_rnd) { +            kernel_map_region_start += heap_region_size; +            kernel_map_region_end += heap_region_size; +        } else { +            heap_region_start += kernel_map_region_size; +            heap_region_end += kernel_map_region_size; +        } + +        if (stack_region_size) { +            if (stack_rnd < kmap_rnd) { +                kernel_map_region_start += stack_region_size; +                kernel_map_region_end += stack_region_size; +            } else { +                stack_region_start += kernel_map_region_size; +                stack_region_end += kernel_map_region_size; +            } +        } +    } + +    // Set heap members +    current_heap_end = heap_region_start; +    max_heap_size = 0; +    max_physical_memory_size = 0; + +    // Ensure that we regions inside our address space +    auto IsInAddressSpace = [&](VAddr addr) { +        return address_space_start <= addr && addr <= address_space_end; +    }; +    ASSERT(IsInAddressSpace(alias_region_start)); +    ASSERT(IsInAddressSpace(alias_region_end)); +    ASSERT(IsInAddressSpace(heap_region_start)); +    ASSERT(IsInAddressSpace(heap_region_end)); +    ASSERT(IsInAddressSpace(stack_region_start)); +    ASSERT(IsInAddressSpace(stack_region_end)); +    ASSERT(IsInAddressSpace(kernel_map_region_start)); +    ASSERT(IsInAddressSpace(kernel_map_region_end)); + +    // Ensure that we selected regions that don't overlap +    const VAddr alias_start{alias_region_start}; +    const VAddr alias_last{alias_region_end - 1}; +    const VAddr heap_start{heap_region_start}; +    const VAddr heap_last{heap_region_end - 1}; +    const VAddr stack_start{stack_region_start}; +    const VAddr stack_last{stack_region_end - 1}; +    const VAddr kmap_start{kernel_map_region_start}; +    const VAddr kmap_last{kernel_map_region_end - 1}; +    ASSERT(alias_last < heap_start || heap_last < alias_start); +    ASSERT(alias_last < stack_start || stack_last < alias_start); +    ASSERT(alias_last < kmap_start || kmap_last < alias_start); +    ASSERT(heap_last < stack_start || stack_last < heap_start); +    ASSERT(heap_last < kmap_start || kmap_last < heap_start); + +    current_heap_addr = heap_region_start; +    heap_capacity = 0; +    physical_memory_usage = 0; +    memory_pool = pool; + +    page_table_impl.Resize(address_space_width, PageBits, true); + +    return InitializeMemoryLayout(start, end); +} + +ResultCode PageTable::MapProcessCode(VAddr addr, std::size_t num_pages, MemoryState state, +                                     MemoryPermission perm) { +    std::lock_guard lock{page_table_lock}; + +    const u64 size{num_pages * PageSize}; + +    if (!CanContain(addr, size, state)) { +        return ERR_INVALID_ADDRESS_STATE; +    } + +    if (IsRegionMapped(addr, size)) { +        return ERR_INVALID_ADDRESS_STATE; +    } + +    PageLinkedList page_linked_list; +    CASCADE_CODE( +        system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool)); +    CASCADE_CODE(Operate(addr, num_pages, page_linked_list, OperationType::MapGroup)); + +    block_manager->Update(addr, num_pages, state, perm); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +    std::lock_guard lock{page_table_lock}; + +    const std::size_t num_pages{size / PageSize}; + +    MemoryState state{}; +    MemoryPermission perm{}; +    CASCADE_CODE(CheckMemoryState(&state, &perm, nullptr, src_addr, size, MemoryState::All, +                                  MemoryState::Normal, MemoryPermission::Mask, +                                  MemoryPermission::ReadAndWrite, MemoryAttribute::Mask, +                                  MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped)); + +    if (IsRegionMapped(dst_addr, size)) { +        return ERR_INVALID_ADDRESS_STATE; +    } + +    PageLinkedList page_linked_list; +    AddRegionToPages(src_addr, num_pages, page_linked_list); + +    { +        auto block_guard = detail::ScopeExit( +            [&] { Operate(src_addr, num_pages, perm, OperationType::ChangePermissions); }); + +        CASCADE_CODE( +            Operate(src_addr, num_pages, MemoryPermission::None, OperationType::ChangePermissions)); +        CASCADE_CODE(MapPages(dst_addr, page_linked_list, MemoryPermission::None)); + +        block_guard.Cancel(); +    } + +    block_manager->Update(src_addr, num_pages, state, MemoryPermission::None, +                          MemoryAttribute::Locked); +    block_manager->Update(dst_addr, num_pages, MemoryState::AliasCode); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size) { +    std::lock_guard lock{page_table_lock}; + +    if (!size) { +        return RESULT_SUCCESS; +    } + +    const std::size_t num_pages{size / PageSize}; + +    CASCADE_CODE(CheckMemoryState(nullptr, nullptr, nullptr, src_addr, size, MemoryState::All, +                                  MemoryState::Normal, MemoryPermission::None, +                                  MemoryPermission::None, MemoryAttribute::Mask, +                                  MemoryAttribute::Locked, MemoryAttribute::IpcAndDeviceMapped)); + +    MemoryState state{}; +    CASCADE_CODE(CheckMemoryState( +        &state, nullptr, nullptr, dst_addr, PageSize, MemoryState::FlagCanCodeAlias, +        MemoryState::FlagCanCodeAlias, MemoryPermission::None, MemoryPermission::None, +        MemoryAttribute::Mask, MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped)); +    CASCADE_CODE(CheckMemoryState(dst_addr, size, MemoryState::All, state, MemoryPermission::None, +                                  MemoryPermission::None, MemoryAttribute::Mask, +                                  MemoryAttribute::None)); +    CASCADE_CODE(Operate(dst_addr, num_pages, MemoryPermission::None, OperationType::Unmap)); + +    block_manager->Update(dst_addr, num_pages, MemoryState::Free); +    block_manager->Update(src_addr, num_pages, MemoryState::Normal, MemoryPermission::ReadAndWrite); + +    return RESULT_SUCCESS; +} + +void PageTable::MapPhysicalMemory(PageLinkedList& page_linked_list, VAddr start, VAddr end) { +    auto node{page_linked_list.Nodes().begin()}; +    PAddr map_addr{node->GetAddress()}; +    std::size_t src_num_pages{node->GetNumPages()}; + +    block_manager->IterateForRange(start, end, [&](const MemoryInfo& info) { +        if (info.state != MemoryState::Free) { +            return; +        } + +        std::size_t dst_num_pages{GetSizeInRange(info, start, end) / PageSize}; +        VAddr dst_addr{GetAddressInRange(info, start)}; + +        while (dst_num_pages) { +            if (!src_num_pages) { +                node = std::next(node); +                map_addr = node->GetAddress(); +                src_num_pages = node->GetNumPages(); +            } + +            const std::size_t num_pages{std::min(src_num_pages, dst_num_pages)}; +            Operate(dst_addr, num_pages, MemoryPermission::ReadAndWrite, OperationType::Map, +                    map_addr); + +            dst_addr += num_pages * PageSize; +            map_addr += num_pages * PageSize; +            src_num_pages -= num_pages; +            dst_num_pages -= num_pages; +        } +    }); +} + +ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { +    std::lock_guard lock{page_table_lock}; + +    std::size_t mapped_size{}; +    const VAddr end_addr{addr + size}; + +    block_manager->IterateForRange(addr, end_addr, [&](const MemoryInfo& info) { +        if (info.state != MemoryState::Free) { +            mapped_size += GetSizeInRange(info, addr, end_addr); +        } +    }); + +    if (mapped_size == size) { +        return RESULT_SUCCESS; +    } + +    auto process{system.Kernel().CurrentProcess()}; +    const std::size_t remaining_size{size - mapped_size}; +    const std::size_t remaining_pages{remaining_size / PageSize}; + +    if (process->GetResourceLimit() && +        !process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, remaining_size)) { +        return ERR_RESOURCE_LIMIT_EXCEEDED; +    } + +    PageLinkedList page_linked_list; +    { +        auto block_guard = detail::ScopeExit([&] { +            system.Kernel().MemoryManager().Free(page_linked_list, remaining_pages, memory_pool); +            process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, remaining_size); +        }); + +        CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, +                                                              memory_pool)); + +        block_guard.Cancel(); +    } + +    MapPhysicalMemory(page_linked_list, addr, end_addr); + +    physical_memory_usage += remaining_size; + +    const std::size_t num_pages{size / PageSize}; +    block_manager->Update(addr, num_pages, MemoryState::Free, MemoryPermission::None, +                          MemoryAttribute::None, MemoryState::Normal, +                          MemoryPermission::ReadAndWrite, MemoryAttribute::None); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) { +    std::lock_guard lock{page_table_lock}; + +    const VAddr end_addr{addr + size}; +    ResultCode result{RESULT_SUCCESS}; +    std::size_t mapped_size{}; + +    // Verify that the region can be unmapped +    block_manager->IterateForRange(addr, end_addr, [&](const MemoryInfo& info) { +        if (info.state == MemoryState::Normal) { +            if (info.attribute != MemoryAttribute::None) { +                result = ERR_INVALID_ADDRESS_STATE; +                return; +            } +            mapped_size += GetSizeInRange(info, addr, end_addr); +        } else if (info.state != MemoryState::Free) { +            result = ERR_INVALID_ADDRESS_STATE; +        } +    }); + +    if (result.IsError()) { +        return result; +    } + +    if (!mapped_size) { +        return RESULT_SUCCESS; +    } + +    CASCADE_CODE(UnmapMemory(addr, size)); + +    auto process{system.Kernel().CurrentProcess()}; +    process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, mapped_size); +    physical_memory_usage -= mapped_size; + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::UnmapMemory(VAddr addr, std::size_t size) { +    std::lock_guard lock{page_table_lock}; + +    const VAddr end_addr{addr + size}; +    ResultCode result{RESULT_SUCCESS}; +    PageLinkedList page_linked_list; + +    // Unmap each region within the range +    block_manager->IterateForRange(addr, end_addr, [&](const MemoryInfo& info) { +        if (info.state == MemoryState::Normal) { +            const std::size_t block_size{GetSizeInRange(info, addr, end_addr)}; +            const std::size_t block_num_pages{block_size / PageSize}; +            const VAddr block_addr{GetAddressInRange(info, addr)}; + +            AddRegionToPages(block_addr, block_size / PageSize, page_linked_list); + +            if (result = Operate(block_addr, block_num_pages, MemoryPermission::None, +                                 OperationType::Unmap); +                result.IsError()) { +                return; +            } +        } +    }); + +    if (result.IsError()) { +        return result; +    } + +    const std::size_t num_pages{size / PageSize}; +    system.Kernel().MemoryManager().Free(page_linked_list, num_pages, memory_pool); + +    block_manager->Update(addr, num_pages, MemoryState::Free); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::Map(VAddr dst_addr, VAddr src_addr, std::size_t size) { +    std::lock_guard lock{page_table_lock}; + +    MemoryState src_state{}; +    CASCADE_CODE(CheckMemoryState( +        &src_state, nullptr, nullptr, src_addr, size, MemoryState::FlagCanAlias, +        MemoryState::FlagCanAlias, MemoryPermission::Mask, MemoryPermission::ReadAndWrite, +        MemoryAttribute::Mask, MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped)); + +    if (IsRegionMapped(dst_addr, size)) { +        return ERR_INVALID_ADDRESS_STATE; +    } + +    PageLinkedList page_linked_list; +    const std::size_t num_pages{size / PageSize}; + +    AddRegionToPages(src_addr, num_pages, page_linked_list); + +    { +        auto block_guard = detail::ScopeExit([&] { +            Operate(src_addr, num_pages, MemoryPermission::ReadAndWrite, +                    OperationType::ChangePermissions); +        }); + +        CASCADE_CODE( +            Operate(src_addr, num_pages, MemoryPermission::None, OperationType::ChangePermissions)); +        CASCADE_CODE(MapPages(dst_addr, page_linked_list, MemoryPermission::ReadAndWrite)); + +        block_guard.Cancel(); +    } + +    block_manager->Update(src_addr, num_pages, src_state, MemoryPermission::None, +                          MemoryAttribute::Locked); +    block_manager->Update(dst_addr, num_pages, MemoryState::Stack, MemoryPermission::ReadAndWrite); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size) { +    std::lock_guard lock{page_table_lock}; + +    MemoryState src_state{}; +    CASCADE_CODE(CheckMemoryState( +        &src_state, nullptr, nullptr, src_addr, size, MemoryState::FlagCanAlias, +        MemoryState::FlagCanAlias, MemoryPermission::Mask, MemoryPermission::None, +        MemoryAttribute::Mask, MemoryAttribute::Locked, MemoryAttribute::IpcAndDeviceMapped)); + +    MemoryPermission dst_perm{}; +    CASCADE_CODE(CheckMemoryState(nullptr, &dst_perm, nullptr, dst_addr, size, MemoryState::All, +                                  MemoryState::Stack, MemoryPermission::None, +                                  MemoryPermission::None, MemoryAttribute::Mask, +                                  MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped)); + +    PageLinkedList src_pages; +    PageLinkedList dst_pages; +    const std::size_t num_pages{size / PageSize}; + +    AddRegionToPages(src_addr, num_pages, src_pages); +    AddRegionToPages(dst_addr, num_pages, dst_pages); + +    if (!dst_pages.IsEqual(src_pages)) { +        return ERR_INVALID_MEMORY_RANGE; +    } + +    { +        auto block_guard = detail::ScopeExit([&] { MapPages(dst_addr, dst_pages, dst_perm); }); + +        CASCADE_CODE(Operate(dst_addr, num_pages, MemoryPermission::None, OperationType::Unmap)); +        CASCADE_CODE(Operate(src_addr, num_pages, MemoryPermission::ReadAndWrite, +                             OperationType::ChangePermissions)); + +        block_guard.Cancel(); +    } + +    block_manager->Update(src_addr, num_pages, src_state, MemoryPermission::ReadAndWrite); +    block_manager->Update(dst_addr, num_pages, MemoryState::Free); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_list, +                               MemoryPermission perm) { +    VAddr cur_addr{addr}; + +    for (const auto& node : page_linked_list.Nodes()) { +        if (const auto result{ +                Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; +            result.IsError()) { +            const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()}; +            const std::size_t num_pages{(addr - cur_addr) / PageSize}; + +            ASSERT( +                Operate(addr, num_pages, MemoryPermission::None, OperationType::Unmap).IsSuccess()); + +            return result; +        } + +        cur_addr += node.GetNumPages() * PageSize; +    } + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::MapPages(VAddr addr, PageLinkedList& page_linked_list, MemoryState state, +                               MemoryPermission perm) { +    std::lock_guard lock{page_table_lock}; + +    const std::size_t num_pages{page_linked_list.GetNumPages()}; +    const std::size_t size{num_pages * PageSize}; + +    if (!CanContain(addr, size, state)) { +        return ERR_INVALID_ADDRESS_STATE; +    } + +    if (IsRegionMapped(addr, num_pages * PageSize)) { +        return ERR_INVALID_ADDRESS_STATE; +    } + +    CASCADE_CODE(MapPages(addr, page_linked_list, perm)); + +    block_manager->Update(addr, num_pages, state, perm); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::SetCodeMemoryPermission(VAddr addr, std::size_t size, MemoryPermission perm) { + +    std::lock_guard lock{page_table_lock}; + +    MemoryState prev_state{}; +    MemoryPermission prev_perm{}; + +    CASCADE_CODE(CheckMemoryState( +        &prev_state, &prev_perm, nullptr, addr, size, MemoryState::FlagCode, MemoryState::FlagCode, +        MemoryPermission::None, MemoryPermission::None, MemoryAttribute::Mask, +        MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped)); + +    MemoryState state{prev_state}; + +    // Ensure state is mutable if permission allows write +    if ((perm & MemoryPermission::Write) != MemoryPermission::None) { +        if (prev_state == MemoryState::Code) { +            state = MemoryState::CodeData; +        } else if (prev_state == MemoryState::AliasCode) { +            state = MemoryState::AliasCodeData; +        } else { +            UNREACHABLE(); +        } +    } + +    // Return early if there is nothing to change +    if (state == prev_state && perm == prev_perm) { +        return RESULT_SUCCESS; +    } + +    const std::size_t num_pages{size / PageSize}; +    const OperationType operation{(perm & MemoryPermission::Execute) != MemoryPermission::None +                                      ? OperationType::ChangePermissionsAndRefresh +                                      : OperationType::ChangePermissions}; + +    CASCADE_CODE(Operate(addr, num_pages, perm, operation)); + +    block_manager->Update(addr, num_pages, state, perm); + +    return RESULT_SUCCESS; +} + +MemoryInfo PageTable::QueryInfoImpl(VAddr addr) { +    std::lock_guard lock{page_table_lock}; + +    return block_manager->FindBlock(addr).GetMemoryInfo(); +} + +MemoryInfo PageTable::QueryInfo(VAddr addr) { +    if (!Contains(addr, 1)) { +        return {address_space_end,      0 - address_space_end, MemoryState::Inaccessible, +                MemoryPermission::None, MemoryAttribute::None, MemoryPermission::None}; +    } + +    return QueryInfoImpl(addr); +} + +ResultCode PageTable::ReserveTransferMemory(VAddr addr, std::size_t size, MemoryPermission perm) { +    std::lock_guard lock{page_table_lock}; + +    MemoryState state{}; +    MemoryAttribute attribute{}; + +    CASCADE_CODE(CheckMemoryState(&state, nullptr, &attribute, addr, size, +                                  MemoryState::FlagCanTransfer | MemoryState::FlagReferenceCounted, +                                  MemoryState::FlagCanTransfer | MemoryState::FlagReferenceCounted, +                                  MemoryPermission::Mask, MemoryPermission::ReadAndWrite, +                                  MemoryAttribute::Mask, MemoryAttribute::None, +                                  MemoryAttribute::IpcAndDeviceMapped)); + +    block_manager->Update(addr, size / PageSize, state, perm, attribute | MemoryAttribute::Locked); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::ResetTransferMemory(VAddr addr, std::size_t size) { +    std::lock_guard lock{page_table_lock}; + +    MemoryState state{}; + +    CASCADE_CODE(CheckMemoryState(&state, nullptr, nullptr, addr, size, +                                  MemoryState::FlagCanTransfer | MemoryState::FlagReferenceCounted, +                                  MemoryState::FlagCanTransfer | MemoryState::FlagReferenceCounted, +                                  MemoryPermission::None, MemoryPermission::None, +                                  MemoryAttribute::Mask, MemoryAttribute::Locked, +                                  MemoryAttribute::IpcAndDeviceMapped)); + +    block_manager->Update(addr, size / PageSize, state, MemoryPermission::ReadAndWrite); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::SetMemoryAttribute(VAddr addr, std::size_t size, MemoryAttribute mask, +                                         MemoryAttribute value) { +    std::lock_guard lock{page_table_lock}; + +    MemoryState state{}; +    MemoryPermission perm{}; +    MemoryAttribute attribute{}; + +    CASCADE_CODE(CheckMemoryState(&state, &perm, &attribute, addr, size, +                                  MemoryState::FlagCanChangeAttribute, +                                  MemoryState::FlagCanChangeAttribute, MemoryPermission::None, +                                  MemoryPermission::None, MemoryAttribute::LockedAndIpcLocked, +                                  MemoryAttribute::None, MemoryAttribute::DeviceSharedAndUncached)); + +    attribute = attribute & ~mask; +    attribute = attribute | (mask & value); + +    block_manager->Update(addr, size / PageSize, state, perm, attribute); + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::SetHeapCapacity(std::size_t new_heap_capacity) { +    std::lock_guard lock{page_table_lock}; +    heap_capacity = new_heap_capacity; +    return RESULT_SUCCESS; +} + +ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) { + +    if (size > heap_region_end - heap_region_start) { +        return ERR_OUT_OF_MEMORY; +    } + +    const u64 previous_heap_size{GetHeapSize()}; + +    UNIMPLEMENTED_IF_MSG(previous_heap_size > size, "Heap shrink is unimplemented"); + +    // Increase the heap size +    { +        std::lock_guard lock{page_table_lock}; + +        const u64 delta{size - previous_heap_size}; + +        auto process{system.Kernel().CurrentProcess()}; +        if (process->GetResourceLimit() && delta != 0 && +            !process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, delta)) { +            return ERR_RESOURCE_LIMIT_EXCEEDED; +        } + +        PageLinkedList page_linked_list; +        const std::size_t num_pages{delta / PageSize}; + +        CASCADE_CODE( +            system.Kernel().MemoryManager().Allocate(page_linked_list, num_pages, memory_pool)); + +        if (IsRegionMapped(current_heap_addr, delta)) { +            return ERR_INVALID_ADDRESS_STATE; +        } + +        CASCADE_CODE( +            Operate(current_heap_addr, num_pages, page_linked_list, OperationType::MapGroup)); + +        block_manager->Update(current_heap_addr, num_pages, MemoryState::Normal, +                              MemoryPermission::ReadAndWrite); + +        current_heap_addr = heap_region_start + size; +    } + +    return MakeResult<VAddr>(heap_region_start); +} + +ResultVal<VAddr> PageTable::AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, +                                                 bool is_map_only, VAddr region_start, +                                                 std::size_t region_num_pages, MemoryState state, +                                                 MemoryPermission perm, PAddr map_addr) { +    std::lock_guard lock{page_table_lock}; + +    if (!CanContain(region_start, region_num_pages * PageSize, state)) { +        return ERR_INVALID_ADDRESS_STATE; +    } + +    if (region_num_pages <= needed_num_pages) { +        return ERR_OUT_OF_MEMORY; +    } + +    const VAddr addr{ +        AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)}; +    if (!addr) { +        return ERR_OUT_OF_MEMORY; +    } + +    if (is_map_only) { +        CASCADE_CODE(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); +    } else { +        PageLinkedList page_group; +        CASCADE_CODE( +            system.Kernel().MemoryManager().Allocate(page_group, needed_num_pages, memory_pool)); +        CASCADE_CODE(Operate(addr, needed_num_pages, page_group, OperationType::MapGroup)); +    } + +    block_manager->Update(addr, needed_num_pages, state, perm); + +    return MakeResult<VAddr>(addr); +} + +ResultCode PageTable::InitializeMemoryLayout(VAddr start, VAddr end) { +    block_manager = std::make_unique<MemoryBlockManager>(start, end); + +    return RESULT_SUCCESS; +} + +bool PageTable::IsRegionMapped(VAddr address, u64 size) { +    return CheckMemoryState(address, size, MemoryState::All, MemoryState::Free, +                            MemoryPermission::Mask, MemoryPermission::None, MemoryAttribute::Mask, +                            MemoryAttribute::None, MemoryAttribute::IpcAndDeviceMapped) +        .IsError(); +} + +bool PageTable::IsRegionContiguous(VAddr addr, u64 size) const { +    auto start_ptr = system.Memory().GetPointer(addr); +    for (u64 offset{}; offset < size; offset += PageSize) { +        if (start_ptr != system.Memory().GetPointer(addr + offset)) { +            return false; +        } +        start_ptr += PageSize; +    } +    return true; +} + +void PageTable::AddRegionToPages(VAddr start, std::size_t num_pages, +                                 PageLinkedList& page_linked_list) { +    VAddr addr{start}; +    while (addr < start + (num_pages * PageSize)) { +        const PAddr paddr{GetPhysicalAddr(addr)}; +        if (!paddr) { +            UNREACHABLE(); +        } +        page_linked_list.AddBlock(paddr, 1); +        addr += PageSize; +    } +} + +VAddr PageTable::AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, +                                       u64 needed_num_pages, std::size_t align) { +    if (is_aslr_enabled) { +        UNIMPLEMENTED(); +    } +    return block_manager->FindFreeArea(start, region_num_pages, needed_num_pages, align, 0, +                                       IsKernel() ? 1 : 4); +} + +ResultCode PageTable::Operate(VAddr addr, std::size_t num_pages, const PageLinkedList& page_group, +                              OperationType operation) { +    std::lock_guard lock{page_table_lock}; + +    ASSERT(Common::IsAligned(addr, PageSize)); +    ASSERT(num_pages > 0); +    ASSERT(num_pages == page_group.GetNumPages()); + +    for (const auto& node : page_group.Nodes()) { +        const std::size_t size{node.GetNumPages() * PageSize}; + +        switch (operation) { +        case OperationType::MapGroup: +            system.Memory().MapMemoryRegion(page_table_impl, addr, size, node.GetAddress()); +            break; +        default: +            UNREACHABLE(); +        } + +        addr += size; +    } + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::Operate(VAddr addr, std::size_t num_pages, MemoryPermission perm, +                              OperationType operation, PAddr map_addr) { +    std::lock_guard lock{page_table_lock}; + +    ASSERT(num_pages > 0); +    ASSERT(Common::IsAligned(addr, PageSize)); +    ASSERT(ContainsPages(addr, num_pages)); + +    switch (operation) { +    case OperationType::Unmap: +        system.Memory().UnmapRegion(page_table_impl, addr, num_pages * PageSize); +        break; +    case OperationType::Map: { +        ASSERT(map_addr); +        ASSERT(Common::IsAligned(map_addr, PageSize)); +        system.Memory().MapMemoryRegion(page_table_impl, addr, num_pages * PageSize, map_addr); +        break; +    } +    case OperationType::ChangePermissions: +    case OperationType::ChangePermissionsAndRefresh: +        break; +    default: +        UNREACHABLE(); +    } +    return RESULT_SUCCESS; +} + +constexpr VAddr PageTable::GetRegionAddress(MemoryState state) const { +    switch (state) { +    case MemoryState::Free: +    case MemoryState::Kernel: +        return address_space_start; +    case MemoryState::Normal: +        return heap_region_start; +    case MemoryState::Ipc: +    case MemoryState::NonSecureIpc: +    case MemoryState::NonDeviceIpc: +        return alias_region_start; +    case MemoryState::Stack: +        return stack_region_start; +    case MemoryState::Io: +    case MemoryState::Static: +    case MemoryState::ThreadLocal: +        return kernel_map_region_start; +    case MemoryState::Shared: +    case MemoryState::AliasCode: +    case MemoryState::AliasCodeData: +    case MemoryState::Transfered: +    case MemoryState::SharedTransfered: +    case MemoryState::SharedCode: +    case MemoryState::GeneratedCode: +    case MemoryState::CodeOut: +        return alias_code_region_start; +    case MemoryState::Code: +    case MemoryState::CodeData: +        return code_region_start; +    default: +        UNREACHABLE(); +        return {}; +    } +} + +constexpr std::size_t PageTable::GetRegionSize(MemoryState state) const { +    switch (state) { +    case MemoryState::Free: +    case MemoryState::Kernel: +        return address_space_end - address_space_start; +    case MemoryState::Normal: +        return heap_region_end - heap_region_start; +    case MemoryState::Ipc: +    case MemoryState::NonSecureIpc: +    case MemoryState::NonDeviceIpc: +        return alias_region_end - alias_region_start; +    case MemoryState::Stack: +        return stack_region_end - stack_region_start; +    case MemoryState::Io: +    case MemoryState::Static: +    case MemoryState::ThreadLocal: +        return kernel_map_region_end - kernel_map_region_start; +    case MemoryState::Shared: +    case MemoryState::AliasCode: +    case MemoryState::AliasCodeData: +    case MemoryState::Transfered: +    case MemoryState::SharedTransfered: +    case MemoryState::SharedCode: +    case MemoryState::GeneratedCode: +    case MemoryState::CodeOut: +        return alias_code_region_end - alias_code_region_start; +    case MemoryState::Code: +    case MemoryState::CodeData: +        return code_region_end - code_region_start; +    default: +        UNREACHABLE(); +        return {}; +    } +} + +constexpr bool PageTable::CanContain(VAddr addr, std::size_t size, MemoryState state) const { +    const VAddr end{addr + size}; +    const VAddr last{end - 1}; +    const VAddr region_start{GetRegionAddress(state)}; +    const std::size_t region_size{GetRegionSize(state)}; +    const bool is_in_region{region_start <= addr && addr < end && +                            last <= region_start + region_size - 1}; +    const bool is_in_heap{!(end <= heap_region_start || heap_region_end <= addr)}; +    const bool is_in_alias{!(end <= alias_region_start || alias_region_end <= addr)}; + +    switch (state) { +    case MemoryState::Free: +    case MemoryState::Kernel: +        return is_in_region; +    case MemoryState::Io: +    case MemoryState::Static: +    case MemoryState::Code: +    case MemoryState::CodeData: +    case MemoryState::Shared: +    case MemoryState::AliasCode: +    case MemoryState::AliasCodeData: +    case MemoryState::Stack: +    case MemoryState::ThreadLocal: +    case MemoryState::Transfered: +    case MemoryState::SharedTransfered: +    case MemoryState::SharedCode: +    case MemoryState::GeneratedCode: +    case MemoryState::CodeOut: +        return is_in_region && !is_in_heap && !is_in_alias; +    case MemoryState::Normal: +        ASSERT(is_in_heap); +        return is_in_region && !is_in_alias; +    case MemoryState::Ipc: +    case MemoryState::NonSecureIpc: +    case MemoryState::NonDeviceIpc: +        ASSERT(is_in_alias); +        return is_in_region && !is_in_heap; +    default: +        return false; +    } +} + +constexpr ResultCode PageTable::CheckMemoryState(const MemoryInfo& info, MemoryState state_mask, +                                                 MemoryState state, MemoryPermission perm_mask, +                                                 MemoryPermission perm, MemoryAttribute attr_mask, +                                                 MemoryAttribute attr) const { +    // Validate the states match expectation +    if ((info.state & state_mask) != state) { +        return ERR_INVALID_ADDRESS_STATE; +    } +    if ((info.perm & perm_mask) != perm) { +        return ERR_INVALID_ADDRESS_STATE; +    } +    if ((info.attribute & attr_mask) != attr) { +        return ERR_INVALID_ADDRESS_STATE; +    } + +    return RESULT_SUCCESS; +} + +ResultCode PageTable::CheckMemoryState(MemoryState* out_state, MemoryPermission* out_perm, +                                       MemoryAttribute* out_attr, VAddr addr, std::size_t size, +                                       MemoryState state_mask, MemoryState state, +                                       MemoryPermission perm_mask, MemoryPermission perm, +                                       MemoryAttribute attr_mask, MemoryAttribute attr, +                                       MemoryAttribute ignore_attr) { +    std::lock_guard lock{page_table_lock}; + +    // Get information about the first block +    const VAddr last_addr{addr + size - 1}; +    MemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)}; +    MemoryInfo info{it->GetMemoryInfo()}; + +    // Validate all blocks in the range have correct state +    const MemoryState first_state{info.state}; +    const MemoryPermission first_perm{info.perm}; +    const MemoryAttribute first_attr{info.attribute}; + +    while (true) { +        // Validate the current block +        if (!(info.state == first_state)) { +            return ERR_INVALID_ADDRESS_STATE; +        } +        if (!(info.perm == first_perm)) { +            return ERR_INVALID_ADDRESS_STATE; +        } +        if (!((info.attribute | static_cast<MemoryAttribute>(ignore_attr)) == +              (first_attr | static_cast<MemoryAttribute>(ignore_attr)))) { +            return ERR_INVALID_ADDRESS_STATE; +        } + +        // Validate against the provided masks +        CASCADE_CODE(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr)); + +        // Break once we're done +        if (last_addr <= info.GetLastAddress()) { +            break; +        } + +        // Advance our iterator +        it++; +        ASSERT(it != block_manager->cend()); +        info = it->GetMemoryInfo(); +    } + +    // Write output state +    if (out_state) { +        *out_state = first_state; +    } +    if (out_perm) { +        *out_perm = first_perm; +    } +    if (out_attr) { +        *out_attr = first_attr & static_cast<MemoryAttribute>(~ignore_attr); +    } + +    return RESULT_SUCCESS; +} + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/page_table.h b/src/core/hle/kernel/memory/page_table.h new file mode 100644 index 000000000..80384ab0f --- /dev/null +++ b/src/core/hle/kernel/memory/page_table.h @@ -0,0 +1,276 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <list> +#include <memory> +#include <mutex> + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/page_table.h" +#include "core/file_sys/program_metadata.h" +#include "core/hle/kernel/memory/memory_block.h" +#include "core/hle/kernel/memory/memory_manager.h" + +namespace Core { +class System; +} + +namespace Kernel::Memory { + +class MemoryBlockManager; + +class PageTable final : NonCopyable { +public: +    explicit PageTable(Core::System& system); + +    ResultCode InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, +                                    VAddr code_addr, std::size_t code_size, +                                    Memory::MemoryManager::Pool pool); +    ResultCode MapProcessCode(VAddr addr, std::size_t pages_count, MemoryState state, +                              MemoryPermission perm); +    ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); +    ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size); +    ResultCode MapPhysicalMemory(VAddr addr, std::size_t size); +    ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size); +    ResultCode UnmapMemory(VAddr addr, std::size_t size); +    ResultCode Map(VAddr dst_addr, VAddr src_addr, std::size_t size); +    ResultCode Unmap(VAddr dst_addr, VAddr src_addr, std::size_t size); +    ResultCode MapPages(VAddr addr, PageLinkedList& page_linked_list, MemoryState state, +                        MemoryPermission perm); +    ResultCode SetCodeMemoryPermission(VAddr addr, std::size_t size, MemoryPermission perm); +    MemoryInfo QueryInfo(VAddr addr); +    ResultCode ReserveTransferMemory(VAddr addr, std::size_t size, MemoryPermission perm); +    ResultCode ResetTransferMemory(VAddr addr, std::size_t size); +    ResultCode SetMemoryAttribute(VAddr addr, std::size_t size, MemoryAttribute mask, +                                  MemoryAttribute value); +    ResultCode SetHeapCapacity(std::size_t new_heap_capacity); +    ResultVal<VAddr> SetHeapSize(std::size_t size); +    ResultVal<VAddr> AllocateAndMapMemory(std::size_t needed_num_pages, std::size_t align, +                                          bool is_map_only, VAddr region_start, +                                          std::size_t region_num_pages, MemoryState state, +                                          MemoryPermission perm, PAddr map_addr = 0); + +    Common::PageTable& PageTableImpl() { +        return page_table_impl; +    } + +    const Common::PageTable& PageTableImpl() const { +        return page_table_impl; +    } + +private: +    enum class OperationType : u32 { +        Map, +        MapGroup, +        Unmap, +        ChangePermissions, +        ChangePermissionsAndRefresh, +    }; + +    static constexpr MemoryAttribute DefaultMemoryIgnoreAttr = +        MemoryAttribute::DontCareMask | MemoryAttribute::IpcLocked | MemoryAttribute::DeviceShared; + +    ResultCode InitializeMemoryLayout(VAddr start, VAddr end); +    ResultCode MapPages(VAddr addr, const PageLinkedList& page_linked_list, MemoryPermission perm); +    void MapPhysicalMemory(PageLinkedList& page_linked_list, VAddr start, VAddr end); +    bool IsRegionMapped(VAddr address, u64 size); +    bool IsRegionContiguous(VAddr addr, u64 size) const; +    void AddRegionToPages(VAddr start, std::size_t num_pages, PageLinkedList& page_linked_list); +    MemoryInfo QueryInfoImpl(VAddr addr); +    VAddr AllocateVirtualMemory(VAddr start, std::size_t region_num_pages, u64 needed_num_pages, +                                std::size_t align); +    ResultCode Operate(VAddr addr, std::size_t num_pages, const PageLinkedList& page_group, +                       OperationType operation); +    ResultCode Operate(VAddr addr, std::size_t num_pages, MemoryPermission perm, +                       OperationType operation, PAddr map_addr = 0); +    constexpr VAddr GetRegionAddress(MemoryState state) const; +    constexpr std::size_t GetRegionSize(MemoryState state) const; +    constexpr bool CanContain(VAddr addr, std::size_t size, MemoryState state) const; + +    constexpr ResultCode CheckMemoryState(const MemoryInfo& info, MemoryState state_mask, +                                          MemoryState state, MemoryPermission perm_mask, +                                          MemoryPermission perm, MemoryAttribute attr_mask, +                                          MemoryAttribute attr) const; +    ResultCode CheckMemoryState(MemoryState* out_state, MemoryPermission* out_perm, +                                MemoryAttribute* out_attr, VAddr addr, std::size_t size, +                                MemoryState state_mask, MemoryState state, +                                MemoryPermission perm_mask, MemoryPermission perm, +                                MemoryAttribute attr_mask, MemoryAttribute attr, +                                MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr); +    ResultCode CheckMemoryState(VAddr addr, std::size_t size, MemoryState state_mask, +                                MemoryState state, MemoryPermission perm_mask, +                                MemoryPermission perm, MemoryAttribute attr_mask, +                                MemoryAttribute attr, +                                MemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) { +        return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask, +                                perm, attr_mask, attr, ignore_attr); +    } + +    std::recursive_mutex page_table_lock; +    std::unique_ptr<MemoryBlockManager> block_manager; + +public: +    constexpr VAddr GetAddressSpaceStart() const { +        return address_space_start; +    } +    constexpr VAddr GetAddressSpaceEnd() const { +        return address_space_end; +    } +    constexpr std::size_t GetAddressSpaceSize() const { +        return address_space_end - address_space_start; +    } +    constexpr VAddr GetHeapRegionStart() const { +        return heap_region_start; +    } +    constexpr VAddr GetHeapRegionEnd() const { +        return heap_region_end; +    } +    constexpr std::size_t GetHeapRegionSize() const { +        return heap_region_end - heap_region_start; +    } +    constexpr VAddr GetAliasRegionStart() const { +        return alias_region_start; +    } +    constexpr VAddr GetAliasRegionEnd() const { +        return alias_region_end; +    } +    constexpr std::size_t GetAliasRegionSize() const { +        return alias_region_end - alias_region_start; +    } +    constexpr VAddr GetStackRegionStart() const { +        return stack_region_start; +    } +    constexpr VAddr GetStackRegionEnd() const { +        return stack_region_end; +    } +    constexpr std::size_t GetStackRegionSize() const { +        return stack_region_end - stack_region_start; +    } +    constexpr VAddr GetKernelMapRegionStart() const { +        return kernel_map_region_start; +    } +    constexpr VAddr GetKernelMapRegionEnd() const { +        return kernel_map_region_end; +    } +    constexpr VAddr GetCodeRegionStart() const { +        return code_region_start; +    } +    constexpr VAddr GetCodeRegionEnd() const { +        return code_region_end; +    } +    constexpr VAddr GetAliasCodeRegionStart() const { +        return alias_code_region_start; +    } +    constexpr VAddr GetAliasCodeRegionSize() const { +        return alias_code_region_end - alias_code_region_start; +    } +    constexpr std::size_t GetAddressSpaceWidth() const { +        return address_space_width; +    } +    constexpr std::size_t GetHeapSize() { +        return current_heap_addr - heap_region_start; +    } +    constexpr std::size_t GetTotalHeapSize() { +        return GetHeapSize() + physical_memory_usage; +    } +    constexpr bool IsInsideAddressSpace(VAddr address, std::size_t size) const { +        return address_space_start <= address && address + size - 1 <= address_space_end - 1; +    } +    constexpr bool IsOutsideAliasRegion(VAddr address, std::size_t size) const { +        return alias_region_start > address || address + size - 1 > alias_region_end - 1; +    } +    constexpr bool IsOutsideStackRegion(VAddr address, std::size_t size) const { +        return stack_region_start > address || address + size - 1 > stack_region_end - 1; +    } +    constexpr bool IsInvalidRegion(VAddr address, std::size_t size) const { +        return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1; +    } +    constexpr bool IsInsideHeapRegion(VAddr address, std::size_t size) const { +        return address + size > heap_region_start && heap_region_end > address; +    } +    constexpr bool IsInsideAliasRegion(VAddr address, std::size_t size) const { +        return address + size > alias_region_start && alias_region_end > address; +    } +    constexpr bool IsOutsideASLRRegion(VAddr address, std::size_t size) const { +        if (IsInvalidRegion(address, size)) { +            return true; +        } +        if (IsInsideHeapRegion(address, size)) { +            return true; +        } +        if (IsInsideAliasRegion(address, size)) { +            return true; +        } +        return {}; +    } +    constexpr bool IsInsideASLRRegion(VAddr address, std::size_t size) const { +        return !IsOutsideASLRRegion(address, size); +    } +    constexpr PAddr GetPhysicalAddr(VAddr addr) { +        return page_table_impl.backing_addr[addr >> Memory::PageBits] + addr; +    } + +private: +    constexpr bool Contains(VAddr addr) const { +        return address_space_start <= addr && addr <= address_space_end - 1; +    } +    constexpr bool Contains(VAddr addr, std::size_t size) const { +        return address_space_start <= addr && addr < addr + size && +               addr + size - 1 <= address_space_end - 1; +    } +    constexpr bool IsKernel() const { +        return is_kernel; +    } +    constexpr bool IsAslrEnabled() const { +        return is_aslr_enabled; +    } + +    constexpr std::size_t GetNumGuardPages() const { +        return IsKernel() ? 1 : 4; +    } + +    constexpr bool ContainsPages(VAddr addr, std::size_t num_pages) const { +        return (address_space_start <= addr) && +               (num_pages <= (address_space_end - address_space_start) / PageSize) && +               (addr + num_pages * PageSize - 1 <= address_space_end - 1); +    } + +private: +    VAddr address_space_start{}; +    VAddr address_space_end{}; +    VAddr heap_region_start{}; +    VAddr heap_region_end{}; +    VAddr current_heap_end{}; +    VAddr alias_region_start{}; +    VAddr alias_region_end{}; +    VAddr stack_region_start{}; +    VAddr stack_region_end{}; +    VAddr kernel_map_region_start{}; +    VAddr kernel_map_region_end{}; +    VAddr code_region_start{}; +    VAddr code_region_end{}; +    VAddr alias_code_region_start{}; +    VAddr alias_code_region_end{}; +    VAddr current_heap_addr{}; + +    std::size_t heap_capacity{}; +    std::size_t physical_memory_usage{}; +    std::size_t max_heap_size{}; +    std::size_t max_physical_memory_size{}; +    std::size_t address_space_width{}; + +    bool is_kernel{}; +    bool is_aslr_enabled{}; + +    MemoryManager::Pool memory_pool{MemoryManager::Pool::Application}; + +    Common::PageTable page_table_impl; + +    Core::System& system; +}; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/slab_heap.h b/src/core/hle/kernel/memory/slab_heap.h new file mode 100644 index 000000000..be95fc3f7 --- /dev/null +++ b/src/core/hle/kernel/memory/slab_heap.h @@ -0,0 +1,164 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +// This file references various implementation details from Atmosphère, an open-source firmware for +// the Nintendo Switch. Copyright 2018-2020 Atmosphère-NX. + +#pragma once + +#include <atomic> + +#include "common/assert.h" +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Kernel::Memory { + +namespace impl { + +class SlabHeapImpl final : NonCopyable { +public: +    struct Node { +        Node* next{}; +    }; + +    constexpr SlabHeapImpl() = default; + +    void Initialize(std::size_t size) { +        ASSERT(head == nullptr); +        obj_size = size; +    } + +    constexpr std::size_t GetObjectSize() const { +        return obj_size; +    } + +    Node* GetHead() const { +        return head; +    } + +    void* Allocate() { +        Node* ret = head.load(); + +        do { +            if (ret == nullptr) { +                break; +            } +        } while (!head.compare_exchange_weak(ret, ret->next)); + +        return ret; +    } + +    void Free(void* obj) { +        Node* node = static_cast<Node*>(obj); + +        Node* cur_head = head.load(); +        do { +            node->next = cur_head; +        } while (!head.compare_exchange_weak(cur_head, node)); +    } + +private: +    std::atomic<Node*> head{}; +    std::size_t obj_size{}; +}; + +} // namespace impl + +class SlabHeapBase : NonCopyable { +public: +    constexpr SlabHeapBase() = default; + +    constexpr bool Contains(uintptr_t addr) const { +        return start <= addr && addr < end; +    } + +    constexpr std::size_t GetSlabHeapSize() const { +        return (end - start) / GetObjectSize(); +    } + +    constexpr std::size_t GetObjectSize() const { +        return impl.GetObjectSize(); +    } + +    constexpr uintptr_t GetSlabHeapAddress() const { +        return start; +    } + +    std::size_t GetObjectIndexImpl(const void* obj) const { +        return (reinterpret_cast<uintptr_t>(obj) - start) / GetObjectSize(); +    } + +    std::size_t GetPeakIndex() const { +        return GetObjectIndexImpl(reinterpret_cast<const void*>(peak)); +    } + +    void* AllocateImpl() { +        return impl.Allocate(); +    } + +    void FreeImpl(void* obj) { +        // Don't allow freeing an object that wasn't allocated from this heap +        ASSERT(Contains(reinterpret_cast<uintptr_t>(obj))); +        impl.Free(obj); +    } + +    void InitializeImpl(std::size_t obj_size, void* memory, std::size_t memory_size) { +        // Ensure we don't initialize a slab using null memory +        ASSERT(memory != nullptr); + +        // Initialize the base allocator +        impl.Initialize(obj_size); + +        // Set our tracking variables +        const std::size_t num_obj = (memory_size / obj_size); +        start = reinterpret_cast<uintptr_t>(memory); +        end = start + num_obj * obj_size; +        peak = start; + +        // Free the objects +        u8* cur = reinterpret_cast<u8*>(end); + +        for (std::size_t i{}; i < num_obj; i++) { +            cur -= obj_size; +            impl.Free(cur); +        } +    } + +private: +    using Impl = impl::SlabHeapImpl; + +    Impl impl; +    uintptr_t peak{}; +    uintptr_t start{}; +    uintptr_t end{}; +}; + +template <typename T> +class SlabHeap final : public SlabHeapBase { +public: +    constexpr SlabHeap() : SlabHeapBase() {} + +    void Initialize(void* memory, std::size_t memory_size) { +        InitializeImpl(sizeof(T), memory, memory_size); +    } + +    T* Allocate() { +        T* obj = static_cast<T*>(AllocateImpl()); +        if (obj != nullptr) { +            new (obj) T(); +        } +        return obj; +    } + +    void Free(T* obj) { +        FreeImpl(obj); +    } + +    constexpr std::size_t GetObjectIndex(const T* obj) const { +        return GetObjectIndexImpl(obj); +    } +}; + +} // namespace Kernel::Memory diff --git a/src/core/hle/kernel/memory/system_control.cpp b/src/core/hle/kernel/memory/system_control.cpp new file mode 100644 index 000000000..9cae3c6cb --- /dev/null +++ b/src/core/hle/kernel/memory/system_control.cpp @@ -0,0 +1,41 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <random> + +#include "core/hle/kernel/memory/system_control.h" + +namespace Kernel::Memory::SystemControl { + +u64 GenerateRandomU64ForInit() { +    static std::random_device device; +    static std::mt19937 gen(device()); +    static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); +    return distribution(gen); +} + +template <typename F> +u64 GenerateUniformRange(u64 min, u64 max, F f) { +    /* Handle the case where the difference is too large to represent. */ +    if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { +        return f(); +    } + +    /* Iterate until we get a value in range. */ +    const u64 range_size = ((max + 1) - min); +    const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; +    while (true) { +        if (const u64 rnd = f(); rnd < effective_max) { +            return min + (rnd % range_size); +        } +    } +} + +u64 GenerateRandomRange(u64 min, u64 max) { +    return GenerateUniformRange(min, max, GenerateRandomU64ForInit); +} + +} // namespace Kernel::Memory::SystemControl diff --git a/src/core/hle/kernel/memory/system_control.h b/src/core/hle/kernel/memory/system_control.h new file mode 100644 index 000000000..3fa93111d --- /dev/null +++ b/src/core/hle/kernel/memory/system_control.h @@ -0,0 +1,18 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +namespace Kernel::Memory::SystemControl { + +u64 GenerateRandomU64ForInit(); + +template <typename F> +u64 GenerateUniformRange(u64 min, u64 max, F f); + +u64 GenerateRandomRange(u64 min, u64 max); + +} // namespace Kernel::Memory::SystemControl diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h index b689e8e8b..7a0266780 100644 --- a/src/core/hle/kernel/physical_memory.h +++ b/src/core/hle/kernel/physical_memory.h @@ -4,6 +4,8 @@  #pragma once +#include <vector> +  #include "common/alignment.h"  namespace Kernel { diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index edc414d69..36724569f 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -10,15 +10,18 @@  #include "common/assert.h"  #include "common/logging/log.h"  #include "core/core.h" +#include "core/device_memory.h"  #include "core/file_sys/program_metadata.h"  #include "core/hle/kernel/code_set.h"  #include "core/hle/kernel/errors.h"  #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory/memory_block_manager.h" +#include "core/hle/kernel/memory/page_table.h" +#include "core/hle/kernel/memory/slab_heap.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/resource_limit.h"  #include "core/hle/kernel/scheduler.h"  #include "core/hle/kernel/thread.h" -#include "core/hle/kernel/vm_manager.h"  #include "core/memory.h"  #include "core/settings.h" @@ -31,10 +34,8 @@ namespace {   * @param kernel The kernel instance to create the main thread under.   * @param priority The priority to give the main thread   */ -void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) { -    const auto& vm_manager = owner_process.VMManager(); -    const VAddr entry_point = vm_manager.GetCodeRegionBaseAddress(); -    const VAddr stack_top = vm_manager.GetTLSIORegionEndAddress(); +void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, VAddr stack_top) { +    const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();      auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,                                       owner_process.GetIdealCore(), stack_top, owner_process); @@ -42,6 +43,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {      // Register 1 must be a handle to the main thread      const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); +    thread->GetContext32().cpu_registers[0] = 0; +    thread->GetContext64().cpu_registers[0] = 0;      thread->GetContext32().cpu_registers[1] = thread_handle;      thread->GetContext64().cpu_registers[1] = thread_handle; @@ -57,7 +60,8 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {  // (whichever page happens to have an available slot).  class TLSPage {  public: -    static constexpr std::size_t num_slot_entries = Memory::PAGE_SIZE / Memory::TLS_ENTRY_SIZE; +    static constexpr std::size_t num_slot_entries = +        Core::Memory::PAGE_SIZE / Core::Memory::TLS_ENTRY_SIZE;      explicit TLSPage(VAddr address) : base_address{address} {} @@ -76,7 +80,7 @@ public:              }              is_slot_used[i] = true; -            return base_address + (i * Memory::TLS_ENTRY_SIZE); +            return base_address + (i * Core::Memory::TLS_ENTRY_SIZE);          }          return std::nullopt; @@ -86,15 +90,15 @@ public:          // Ensure that all given addresses are consistent with how TLS pages          // are intended to be used when releasing slots.          ASSERT(IsWithinPage(address)); -        ASSERT((address % Memory::TLS_ENTRY_SIZE) == 0); +        ASSERT((address % Core::Memory::TLS_ENTRY_SIZE) == 0); -        const std::size_t index = (address - base_address) / Memory::TLS_ENTRY_SIZE; +        const std::size_t index = (address - base_address) / Core::Memory::TLS_ENTRY_SIZE;          is_slot_used[index] = false;      }  private:      bool IsWithinPage(VAddr address) const { -        return base_address <= address && address < base_address + Memory::PAGE_SIZE; +        return base_address <= address && address < base_address + Core::Memory::PAGE_SIZE;      }      VAddr base_address; @@ -106,7 +110,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,      std::shared_ptr<Process> process = std::make_shared<Process>(system);      process->name = std::move(name); -    process->resource_limit = kernel.GetSystemResourceLimit(); +    process->resource_limit = ResourceLimit::Create(kernel);      process->status = ProcessStatus::Created;      process->program_id = 0;      process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() @@ -127,7 +131,14 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {  }  u64 Process::GetTotalPhysicalMemoryAvailable() const { -    return vm_manager.GetTotalPhysicalMemoryAvailable(); +    const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + +                       page_table->GetTotalHeapSize() + image_size + main_thread_stack_size}; + +    if (capacity < memory_usage_capacity) { +        return capacity; +    } + +    return memory_usage_capacity;  }  u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { @@ -135,8 +146,7 @@ u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {  }  u64 Process::GetTotalPhysicalMemoryUsed() const { -    return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size + -           GetSystemResourceUsage(); +    return image_size + main_thread_stack_size + page_table->GetTotalHeapSize();  }  u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { @@ -209,33 +219,82 @@ ResultCode Process::ClearSignalState() {      return RESULT_SUCCESS;  } -ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { +ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, +                                     std::size_t code_size) {      program_id = metadata.GetTitleID();      ideal_core = metadata.GetMainThreadCore();      is_64bit_process = metadata.Is64BitProgram();      system_resource_size = metadata.GetSystemResourceSize(); +    image_size = code_size; + +    // Initialize proces address space +    if (const ResultCode result{ +            page_table->InitializeForProcess(metadata.GetAddressSpaceType(), false, 0x8000000, +                                             code_size, Memory::MemoryManager::Pool::Application)}; +        result.IsError()) { +        return result; +    } -    vm_manager.Reset(metadata.GetAddressSpaceType()); +    // Map process code region +    if (const ResultCode result{page_table->MapProcessCode( +            page_table->GetCodeRegionStart(), code_size / Memory::PageSize, +            Memory::MemoryState::Code, Memory::MemoryPermission::None)}; +        result.IsError()) { +        return result; +    } -    const auto& caps = metadata.GetKernelCapabilities(); -    const auto capability_init_result = -        capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager); -    if (capability_init_result.IsError()) { -        return capability_init_result; +    // Initialize process capabilities +    const auto& caps{metadata.GetKernelCapabilities()}; +    if (const ResultCode result{ +            capabilities.InitializeForUserProcess(caps.data(), caps.size(), *page_table)}; +        result.IsError()) { +        return result;      } +    // Set memory usage capacity +    switch (metadata.GetAddressSpaceType()) { +    case FileSys::ProgramAddressSpaceType::Is32Bit: +    case FileSys::ProgramAddressSpaceType::Is36Bit: +    case FileSys::ProgramAddressSpaceType::Is39Bit: +        memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart(); +        break; + +    case FileSys::ProgramAddressSpaceType::Is32BitNoMap: +        memory_usage_capacity = page_table->GetHeapRegionEnd() - page_table->GetHeapRegionStart() + +                                page_table->GetAliasRegionEnd() - page_table->GetAliasRegionStart(); +        break; + +    default: +        UNREACHABLE(); +    } + +    // Set initial resource limits +    resource_limit->SetLimitValue( +        ResourceType::PhysicalMemory, +        kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application)); +    resource_limit->SetLimitValue(ResourceType::Threads, 608); +    resource_limit->SetLimitValue(ResourceType::Events, 700); +    resource_limit->SetLimitValue(ResourceType::TransferMemory, 128); +    resource_limit->SetLimitValue(ResourceType::Sessions, 894); +    ASSERT(resource_limit->Reserve(ResourceType::PhysicalMemory, code_size)); + +    // Create TLS region +    tls_region_address = CreateTLSRegion(); +      return handle_table.SetSize(capabilities.GetHandleTableSize());  }  void Process::Run(s32 main_thread_priority, u64 stack_size) {      AllocateMainThreadStack(stack_size); -    tls_region_address = CreateTLSRegion(); -    vm_manager.LogLayout(); +    const std::size_t heap_capacity{memory_usage_capacity - main_thread_stack_size - image_size}; +    ASSERT(!page_table->SetHeapCapacity(heap_capacity).IsError());      ChangeStatus(ProcessStatus::Running); -    SetupMainThread(*this, kernel, main_thread_priority); +    SetupMainThread(*this, kernel, main_thread_priority, main_thread_stack_top); +    resource_limit->Reserve(ResourceType::Threads, 1); +    resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size);  }  void Process::PrepareForTermination() { @@ -279,32 +338,37 @@ static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {  }  VAddr Process::CreateTLSRegion() { -    auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages); +    if (auto tls_page_iter{FindTLSPageWithAvailableSlots(tls_pages)}; +        tls_page_iter != tls_pages.cend()) { +        return *tls_page_iter->ReserveSlot(); +    } -    if (tls_page_iter == tls_pages.cend()) { -        const auto region_address = -            vm_manager.FindFreeRegion(vm_manager.GetTLSIORegionBaseAddress(), -                                      vm_manager.GetTLSIORegionEndAddress(), Memory::PAGE_SIZE); -        ASSERT(region_address.Succeeded()); +    Memory::Page* const tls_page_ptr{kernel.GetUserSlabHeapPages().Allocate()}; +    ASSERT(tls_page_ptr); -        const auto map_result = vm_manager.MapMemoryBlock( -            *region_address, std::make_shared<PhysicalMemory>(Memory::PAGE_SIZE), 0, -            Memory::PAGE_SIZE, MemoryState::ThreadLocal); -        ASSERT(map_result.Succeeded()); +    const VAddr start{page_table->GetKernelMapRegionStart()}; +    const VAddr size{page_table->GetKernelMapRegionEnd() - start}; +    const PAddr tls_map_addr{system.DeviceMemory().GetPhysicalAddr(tls_page_ptr)}; +    const VAddr tls_page_addr{ +        page_table +            ->AllocateAndMapMemory(1, Memory::PageSize, true, start, size / Memory::PageSize, +                                   Memory::MemoryState::ThreadLocal, +                                   Memory::MemoryPermission::ReadAndWrite, tls_map_addr) +            .ValueOr(0)}; -        tls_pages.emplace_back(*region_address); +    ASSERT(tls_page_addr); -        const auto reserve_result = tls_pages.back().ReserveSlot(); -        ASSERT(reserve_result.has_value()); +    std::memset(tls_page_ptr, 0, Memory::PageSize); +    tls_pages.emplace_back(tls_page_addr); -        return *reserve_result; -    } +    const auto reserve_result{tls_pages.back().ReserveSlot()}; +    ASSERT(reserve_result.has_value()); -    return *tls_page_iter->ReserveSlot(); +    return *reserve_result;  }  void Process::FreeTLSRegion(VAddr tls_address) { -    const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE); +    const VAddr aligned_address = Common::AlignDown(tls_address, Core::Memory::PAGE_SIZE);      auto iter =          std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {              return page.GetBaseAddress() == aligned_address; @@ -317,28 +381,22 @@ void Process::FreeTLSRegion(VAddr tls_address) {      iter->ReleaseSlot(tls_address);  } -void Process::LoadModule(CodeSet module_, VAddr base_addr) { -    code_memory_size += module_.memory.size(); - -    const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory)); - -    const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions, -                                MemoryState memory_state) { -        const auto vma = vm_manager -                             .MapMemoryBlock(segment.addr + base_addr, memory, segment.offset, -                                             segment.size, memory_state) -                             .Unwrap(); -        vm_manager.Reprotect(vma, permissions); +void Process::LoadModule(CodeSet code_set, VAddr base_addr) { +    const auto ReprotectSegment = [&](const CodeSet::Segment& segment, +                                      Memory::MemoryPermission permission) { +        page_table->SetCodeMemoryPermission(segment.addr + base_addr, segment.size, permission);      }; -    // Map CodeSet segments -    MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::Code); -    MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeData); -    MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeData); +    system.Memory().WriteBlock(*this, base_addr, code_set.memory.data(), code_set.memory.size()); + +    ReprotectSegment(code_set.CodeSegment(), Memory::MemoryPermission::ReadAndExecute); +    ReprotectSegment(code_set.RODataSegment(), Memory::MemoryPermission::Read); +    ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite);  }  Process::Process(Core::System& system) -    : SynchronizationObject{system.Kernel()}, vm_manager{system}, +    : SynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>( +                                                  system)},        address_arbiter{system}, mutex{system}, system{system} {}  Process::~Process() = default; @@ -361,16 +419,24 @@ void Process::ChangeStatus(ProcessStatus new_status) {      Signal();  } -void Process::AllocateMainThreadStack(u64 stack_size) { +ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) { +    ASSERT(stack_size); +      // The kernel always ensures that the given stack size is page aligned. -    main_thread_stack_size = Common::AlignUp(stack_size, Memory::PAGE_SIZE); - -    // Allocate and map the main thread stack -    const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size; -    vm_manager -        .MapMemoryBlock(mapping_address, std::make_shared<PhysicalMemory>(main_thread_stack_size), -                        0, main_thread_stack_size, MemoryState::Stack) -        .Unwrap(); +    main_thread_stack_size = Common::AlignUp(stack_size, Memory::PageSize); + +    const VAddr start{page_table->GetStackRegionStart()}; +    const std::size_t size{page_table->GetStackRegionEnd() - start}; + +    CASCADE_RESULT(main_thread_stack_top, +                   page_table->AllocateAndMapMemory( +                       main_thread_stack_size / Memory::PageSize, Memory::PageSize, false, start, +                       size / Memory::PageSize, Memory::MemoryState::Stack, +                       Memory::MemoryPermission::ReadAndWrite)); + +    main_thread_stack_top += main_thread_stack_size; + +    return RESULT_SUCCESS;  }  } // namespace Kernel diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 4887132a7..9dabe3568 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -16,7 +16,6 @@  #include "core/hle/kernel/mutex.h"  #include "core/hle/kernel/process_capability.h"  #include "core/hle/kernel/synchronization_object.h" -#include "core/hle/kernel/vm_manager.h"  #include "core/hle/result.h"  namespace Core { @@ -36,6 +35,10 @@ class TLSPage;  struct CodeSet; +namespace Memory { +class PageTable; +} +  enum class MemoryRegion : u16 {      APPLICATION = 1,      SYSTEM = 2, @@ -100,14 +103,14 @@ public:          return HANDLE_TYPE;      } -    /// Gets a reference to the process' memory manager. -    Kernel::VMManager& VMManager() { -        return vm_manager; +    /// Gets a reference to the process' page table. +    Memory::PageTable& PageTable() { +        return *page_table;      } -    /// Gets a const reference to the process' memory manager. -    const Kernel::VMManager& VMManager() const { -        return vm_manager; +    /// Gets const a reference to the process' page table. +    const Memory::PageTable& PageTable() const { +        return *page_table;      }      /// Gets a reference to the process' handle table. @@ -273,7 +276,7 @@ public:       * @returns RESULT_SUCCESS if all relevant metadata was able to be       *          loaded and parsed. Otherwise, an error code is returned.       */ -    ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata); +    ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std::size_t code_size);      /**       * Starts the main application thread for this process. @@ -289,7 +292,7 @@ public:       */      void PrepareForTermination(); -    void LoadModule(CodeSet module_, VAddr base_addr); +    void LoadModule(CodeSet code_set, VAddr base_addr);      ///////////////////////////////////////////////////////////////////////////////////////////////      // Thread-local storage management @@ -313,16 +316,10 @@ private:      void ChangeStatus(ProcessStatus new_status);      /// Allocates the main thread stack for the process, given the stack size in bytes. -    void AllocateMainThreadStack(u64 stack_size); - -    /// Memory manager for this process. -    Kernel::VMManager vm_manager; - -    /// Size of the main thread's stack in bytes. -    u64 main_thread_stack_size = 0; +    ResultCode AllocateMainThreadStack(std::size_t stack_size); -    /// Size of the loaded code memory in bytes. -    u64 code_memory_size = 0; +    /// Memory manager for this process +    std::unique_ptr<Memory::PageTable> page_table;      /// Current status of the process      ProcessStatus status{}; @@ -390,6 +387,18 @@ private:      /// Name of this process      std::string name; + +    /// Address of the top of the main thread's stack +    VAddr main_thread_stack_top{}; + +    /// Size of the main thread's stack +    std::size_t main_thread_stack_size{}; + +    /// Memory usage capacity for the process +    std::size_t memory_usage_capacity{}; + +    /// Process total image size +    std::size_t image_size{};  };  } // namespace Kernel diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp index 583e35b79..48e5ae682 100644 --- a/src/core/hle/kernel/process_capability.cpp +++ b/src/core/hle/kernel/process_capability.cpp @@ -5,8 +5,8 @@  #include "common/bit_util.h"  #include "core/hle/kernel/errors.h"  #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process_capability.h" -#include "core/hle/kernel/vm_manager.h"  namespace Kernel {  namespace { @@ -66,7 +66,7 @@ u32 GetFlagBitOffset(CapabilityType type) {  ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,                                                             std::size_t num_capabilities, -                                                           VMManager& vm_manager) { +                                                           Memory::PageTable& page_table) {      Clear();      // Allow all cores and priorities. @@ -74,15 +74,15 @@ ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabiliti      priority_mask = 0xFFFFFFFFFFFFFFFF;      kernel_version = PackedKernelVersion; -    return ParseCapabilities(capabilities, num_capabilities, vm_manager); +    return ParseCapabilities(capabilities, num_capabilities, page_table);  }  ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,                                                           std::size_t num_capabilities, -                                                         VMManager& vm_manager) { +                                                         Memory::PageTable& page_table) {      Clear(); -    return ParseCapabilities(capabilities, num_capabilities, vm_manager); +    return ParseCapabilities(capabilities, num_capabilities, page_table);  }  void ProcessCapabilities::InitializeForMetadatalessProcess() { @@ -105,7 +105,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {  ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,                                                    std::size_t num_capabilities, -                                                  VMManager& vm_manager) { +                                                  Memory::PageTable& page_table) {      u32 set_flags = 0;      u32 set_svc_bits = 0; @@ -127,13 +127,13 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,                  return ERR_INVALID_COMBINATION;              } -            const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager); +            const auto result = HandleMapPhysicalFlags(descriptor, size_flags, page_table);              if (result.IsError()) {                  return result;              }          } else {              const auto result = -                ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager); +                ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, page_table);              if (result.IsError()) {                  return result;              } @@ -144,7 +144,7 @@ ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,  }  ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, -                                                          u32 flag, VMManager& vm_manager) { +                                                          u32 flag, Memory::PageTable& page_table) {      const auto type = GetCapabilityType(flag);      if (type == CapabilityType::Unset) { @@ -172,7 +172,7 @@ ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& s      case CapabilityType::Syscall:          return HandleSyscallFlags(set_svc_bits, flag);      case CapabilityType::MapIO: -        return HandleMapIOFlags(flag, vm_manager); +        return HandleMapIOFlags(flag, page_table);      case CapabilityType::Interrupt:          return HandleInterruptFlags(flag);      case CapabilityType::ProgramType: @@ -269,12 +269,12 @@ ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags)  }  ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags, -                                                       VMManager& vm_manager) { +                                                       Memory::PageTable& page_table) {      // TODO(Lioncache): Implement once the memory manager can handle this.      return RESULT_SUCCESS;  } -ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) { +ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, Memory::PageTable& page_table) {      // TODO(Lioncache): Implement once the memory manager can handle this.      return RESULT_SUCCESS;  } diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h index 5cdd80747..ea9d12c16 100644 --- a/src/core/hle/kernel/process_capability.h +++ b/src/core/hle/kernel/process_capability.h @@ -12,7 +12,9 @@ union ResultCode;  namespace Kernel { -class VMManager; +namespace Memory { +class PageTable; +}  /// The possible types of programs that may be indicated  /// by the program type capability descriptor. @@ -81,27 +83,27 @@ public:      ///      /// @param capabilities     The capabilities to parse      /// @param num_capabilities The number of capabilities to parse. -    /// @param vm_manager       The memory manager to use for handling any mapping-related +    /// @param page_table       The memory manager to use for handling any mapping-related      ///                         operations (such as mapping IO memory, etc).      ///      /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,      ///          otherwise, an error code upon failure.      ///      ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities, -                                          VMManager& vm_manager); +                                          Memory::PageTable& page_table);      /// Initializes this process capabilities instance for a userland process.      ///      /// @param capabilities     The capabilities to parse.      /// @param num_capabilities The total number of capabilities to parse. -    /// @param vm_manager       The memory manager to use for handling any mapping-related +    /// @param page_table       The memory manager to use for handling any mapping-related      ///                         operations (such as mapping IO memory, etc).      ///      /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,      ///          otherwise, an error code upon failure.      ///      ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities, -                                        VMManager& vm_manager); +                                        Memory::PageTable& page_table);      /// Initializes this process capabilities instance for a process that does not      /// have any metadata to parse. @@ -181,13 +183,13 @@ private:      ///      /// @param capabilities     The sequence of capability descriptors to parse.      /// @param num_capabilities The number of descriptors within the given sequence. -    /// @param vm_manager       The memory manager that will perform any memory +    /// @param page_table       The memory manager that will perform any memory      ///                         mapping if necessary.      ///      /// @return RESULT_SUCCESS if no errors occur, otherwise an error code.      ///      ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities, -                                 VMManager& vm_manager); +                                 Memory::PageTable& page_table);      /// Attempts to parse a capability descriptor that is only represented by a      /// single flag set. @@ -196,13 +198,13 @@ private:      ///                     flags being initialized more than once when they shouldn't be.      /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.      /// @param flag         The flag to attempt to parse. -    /// @param vm_manager   The memory manager that will perform any memory +    /// @param page_table   The memory manager that will perform any memory      ///                     mapping if necessary.      ///      /// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.      ///      ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag, -                                         VMManager& vm_manager); +                                         Memory::PageTable& page_table);      /// Clears the internal state of this process capability instance. Necessary,      /// to have a sane starting point due to us allowing running executables without @@ -226,10 +228,10 @@ private:      ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);      /// Handles flags related to mapping physical memory pages. -    ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager); +    ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, Memory::PageTable& page_table);      /// Handles flags related to mapping IO pages. -    ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager); +    ResultCode HandleMapIOFlags(u32 flags, Memory::PageTable& page_table);      /// Handles flags related to the interrupt capability flags.      ResultCode HandleInterruptFlags(u32 flags); diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index b53423462..96e5b9892 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -16,26 +16,60 @@ constexpr std::size_t ResourceTypeToIndex(ResourceType type) {  ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}  ResourceLimit::~ResourceLimit() = default; +bool ResourceLimit::Reserve(ResourceType resource, s64 amount) { +    return Reserve(resource, amount, 10000000000); +} + +bool ResourceLimit::Reserve(ResourceType resource, s64 amount, u64 timeout) { +    const std::size_t index{ResourceTypeToIndex(resource)}; + +    s64 new_value = current[index] + amount; +    while (new_value > limit[index] && available[index] + amount <= limit[index]) { +        // TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout +        new_value = current[index] + amount; + +        if (timeout >= 0) { +            break; +        } +    } + +    if (new_value <= limit[index]) { +        current[index] = new_value; +        return true; +    } +    return false; +} + +void ResourceLimit::Release(ResourceType resource, u64 amount) { +    Release(resource, amount, amount); +} + +void ResourceLimit::Release(ResourceType resource, u64 used_amount, u64 available_amount) { +    const std::size_t index{ResourceTypeToIndex(resource)}; + +    current[index] -= used_amount; +    available[index] -= available_amount; +} +  std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) {      return std::make_shared<ResourceLimit>(kernel);  }  s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { -    return values.at(ResourceTypeToIndex(resource)); +    return limit.at(ResourceTypeToIndex(resource)) - current.at(ResourceTypeToIndex(resource));  }  s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { -    return limits.at(ResourceTypeToIndex(resource)); +    return limit.at(ResourceTypeToIndex(resource));  }  ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) { -    const auto index = ResourceTypeToIndex(resource); - -    if (value < values[index]) { +    const std::size_t index{ResourceTypeToIndex(resource)}; +    if (current[index] <= value) { +        limit[index] = value; +        return RESULT_SUCCESS; +    } else {          return ERR_INVALID_STATE;      } - -    values[index] = value; -    return RESULT_SUCCESS;  }  } // namespace Kernel diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 53b89e621..936cc4d0f 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -51,6 +51,11 @@ public:          return HANDLE_TYPE;      } +    bool Reserve(ResourceType resource, s64 amount); +    bool Reserve(ResourceType resource, s64 amount, u64 timeout); +    void Release(ResourceType resource, u64 amount); +    void Release(ResourceType resource, u64 used_amount, u64 available_amount); +      /**       * Gets the current value for the specified resource.       * @param resource Requested resource type @@ -91,10 +96,9 @@ private:      using ResourceArray =          std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>; -    /// Maximum values a resource type may reach. -    ResourceArray limits{}; -    /// Current resource limit values. -    ResourceArray values{}; +    ResourceArray limit{}; +    ResourceArray current{}; +    ResourceArray available{};  };  } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 4604e35c5..0f102ca44 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -134,7 +134,8 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con      return RESULT_SUCCESS;  } -ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) { +ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, +                                           Core::Memory::Memory& memory) {      u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};      std::shared_ptr<Kernel::HLERequestContext> context{          std::make_shared<Kernel::HLERequestContext>(SharedFrom(this), std::move(thread))}; @@ -178,7 +179,7 @@ ResultCode ServerSession::CompleteSyncRequest() {  }  ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, -                                            Memory::Memory& memory) { +                                            Core::Memory::Memory& memory) {      Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {});      return QueueSyncRequest(std::move(thread), memory);  } diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 77e4f6721..403aaf10b 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -13,7 +13,7 @@  #include "core/hle/kernel/synchronization_object.h"  #include "core/hle/result.h" -namespace Memory { +namespace Core::Memory {  class Memory;  } @@ -92,7 +92,7 @@ public:       *       * @returns ResultCode from the operation.       */ -    ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory); +    ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);      bool ShouldWait(const Thread* thread) const override; @@ -126,7 +126,7 @@ public:  private:      /// Queues a sync request from the emulated application. -    ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory); +    ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);      /// Completes a sync request from the emulated application.      ResultCode CompleteSyncRequest(); diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index afb2e3fc2..c67696757 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -2,149 +2,56 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. -#include <utility> -  #include "common/assert.h" -#include "common/logging/log.h" -#include "core/hle/kernel/errors.h" +#include "core/core.h"  #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/shared_memory.h"  namespace Kernel { -SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {} -SharedMemory::~SharedMemory() = default; - -std::shared_ptr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_process, -                                                   u64 size, MemoryPermission permissions, -                                                   MemoryPermission other_permissions, -                                                   VAddr address, MemoryRegion region, -                                                   std::string name) { -    std::shared_ptr<SharedMemory> shared_memory = std::make_shared<SharedMemory>(kernel); - -    shared_memory->owner_process = owner_process; -    shared_memory->name = std::move(name); -    shared_memory->size = size; -    shared_memory->permissions = permissions; -    shared_memory->other_permissions = other_permissions; - -    if (address == 0) { -        shared_memory->backing_block = std::make_shared<Kernel::PhysicalMemory>(size); -        shared_memory->backing_block_offset = 0; - -        // Refresh the address mappings for the current process. -        if (kernel.CurrentProcess() != nullptr) { -            kernel.CurrentProcess()->VMManager().RefreshMemoryBlockMappings( -                shared_memory->backing_block.get()); -        } -    } else { -        const auto& vm_manager = shared_memory->owner_process->VMManager(); +SharedMemory::SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory) +    : Object{kernel}, device_memory{device_memory} {} -        // The memory is already available and mapped in the owner process. -        const auto vma = vm_manager.FindVMA(address); -        ASSERT_MSG(vm_manager.IsValidHandle(vma), "Invalid memory address"); -        ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); - -        // The returned VMA might be a bigger one encompassing the desired address. -        const auto vma_offset = address - vma->first; -        ASSERT_MSG(vma_offset + size <= vma->second.size, -                   "Shared memory exceeds bounds of mapped block"); - -        shared_memory->backing_block = vma->second.backing_block; -        shared_memory->backing_block_offset = vma->second.offset + vma_offset; -    } - -    shared_memory->base_address = address; +SharedMemory::~SharedMemory() = default; -    return shared_memory; -} +std::shared_ptr<SharedMemory> SharedMemory::Create( +    KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process, +    Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission, +    Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size, +    std::string name) { -std::shared_ptr<SharedMemory> SharedMemory::CreateForApplet( -    KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset, -    u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) { -    std::shared_ptr<SharedMemory> shared_memory = std::make_shared<SharedMemory>(kernel); +    std::shared_ptr<SharedMemory> shared_memory{ +        std::make_shared<SharedMemory>(kernel, device_memory)}; -    shared_memory->owner_process = nullptr; -    shared_memory->name = std::move(name); +    shared_memory->owner_process = owner_process; +    shared_memory->page_list = std::move(page_list); +    shared_memory->owner_permission = owner_permission; +    shared_memory->user_permission = user_permission; +    shared_memory->physical_address = physical_address;      shared_memory->size = size; -    shared_memory->permissions = permissions; -    shared_memory->other_permissions = other_permissions; -    shared_memory->backing_block = std::move(heap_block); -    shared_memory->backing_block_offset = offset; -    shared_memory->base_address = -        kernel.CurrentProcess()->VMManager().GetHeapRegionBaseAddress() + offset; +    shared_memory->name = name;      return shared_memory;  } -ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions, -                             MemoryPermission other_permissions) { -    const MemoryPermission own_other_permissions = -        &target_process == owner_process ? this->permissions : this->other_permissions; - -    // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare -    if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { -        return ERR_INVALID_MEMORY_PERMISSIONS; -    } - -    // Error out if the requested permissions don't match what the creator process allows. -    if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { -        LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", -                  GetObjectId(), address, name); -        return ERR_INVALID_MEMORY_PERMISSIONS; -    } +ResultCode SharedMemory::Map(Process& target_process, VAddr address, std::size_t size, +                             Memory::MemoryPermission permission) { +    const u64 page_count{(size + Memory::PageSize - 1) / Memory::PageSize}; -    // Error out if the provided permissions are not compatible with what the creator process needs. -    if (other_permissions != MemoryPermission::DontCare && -        static_cast<u32>(this->permissions) & ~static_cast<u32>(other_permissions)) { -        LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", -                  GetObjectId(), address, name); -        return ERR_INVALID_MEMORY_PERMISSIONS; +    if (page_list.GetNumPages() != page_count) { +        UNIMPLEMENTED_MSG("Page count does not match");      } -    VAddr target_address = address; +    Memory::MemoryPermission expected = +        &target_process == owner_process ? owner_permission : user_permission; -    // Map the memory block into the target process -    auto result = target_process.VMManager().MapMemoryBlock( -        target_address, backing_block, backing_block_offset, size, MemoryState::Shared); -    if (result.Failed()) { -        LOG_ERROR( -            Kernel, -            "cannot map id={}, target_address=0x{:X} name={}, error mapping to virtual memory", -            GetObjectId(), target_address, name); -        return result.Code(); +    if (permission != expected) { +        UNIMPLEMENTED_MSG("Permission does not match");      } -    return target_process.VMManager().ReprotectRange(target_address, size, -                                                     ConvertPermissions(permissions)); -} - -ResultCode SharedMemory::Unmap(Process& target_process, VAddr address, u64 unmap_size) { -    if (unmap_size != size) { -        LOG_ERROR(Kernel, -                  "Invalid size passed to Unmap. Size must be equal to the size of the " -                  "memory managed. Shared memory size=0x{:016X}, Unmap size=0x{:016X}", -                  size, unmap_size); -        return ERR_INVALID_SIZE; -    } - -    // TODO(Subv): Verify what happens if the application tries to unmap an address that is not -    // mapped to a SharedMemory. -    return target_process.VMManager().UnmapRange(address, size); -} - -VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { -    u32 masked_permissions = -        static_cast<u32>(permission) & static_cast<u32>(MemoryPermission::ReadWriteExecute); -    return static_cast<VMAPermission>(masked_permissions); -} - -u8* SharedMemory::GetPointer(std::size_t offset) { -    return backing_block->data() + backing_block_offset + offset; -} - -const u8* SharedMemory::GetPointer(std::size_t offset) const { -    return backing_block->data() + backing_block_offset + offset; +    return target_process.PageTable().MapPages(address, page_list, Memory::MemoryState::Shared, +                                               permission);  }  } // namespace Kernel diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 014951d82..cd16d6412 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -8,8 +8,10 @@  #include <string>  #include "common/common_types.h" +#include "core/device_memory.h" +#include "core/hle/kernel/memory/memory_block.h" +#include "core/hle/kernel/memory/page_linked_list.h"  #include "core/hle/kernel/object.h" -#include "core/hle/kernel/physical_memory.h"  #include "core/hle/kernel/process.h"  #include "core/hle/result.h" @@ -17,63 +19,21 @@ namespace Kernel {  class KernelCore; -/// Permissions for mapped shared memory blocks -enum class MemoryPermission : u32 { -    None = 0, -    Read = (1u << 0), -    Write = (1u << 1), -    ReadWrite = (Read | Write), -    Execute = (1u << 2), -    ReadExecute = (Read | Execute), -    WriteExecute = (Write | Execute), -    ReadWriteExecute = (Read | Write | Execute), -    DontCare = (1u << 28) -}; -  class SharedMemory final : public Object {  public: -    explicit SharedMemory(KernelCore& kernel); +    explicit SharedMemory(KernelCore& kernel, Core::DeviceMemory& device_memory);      ~SharedMemory() override; -    /** -     * Creates a shared memory object. -     * @param kernel The kernel instance to create a shared memory instance under. -     * @param owner_process Process that created this shared memory object. -     * @param size Size of the memory block. Must be page-aligned. -     * @param permissions Permission restrictions applied to the process which created the block. -     * @param other_permissions Permission restrictions applied to other processes mapping the -     * block. -     * @param address The address from which to map the Shared Memory. -     * @param region If the address is 0, the shared memory will be allocated in this region of the -     * linear heap. -     * @param name Optional object name, used for debugging purposes. -     */ -    static std::shared_ptr<SharedMemory> Create(KernelCore& kernel, Process* owner_process, -                                                u64 size, MemoryPermission permissions, -                                                MemoryPermission other_permissions, -                                                VAddr address = 0, -                                                MemoryRegion region = MemoryRegion::BASE, -                                                std::string name = "Unknown"); - -    /** -     * Creates a shared memory object from a block of memory managed by an HLE applet. -     * @param kernel The kernel instance to create a shared memory instance under. -     * @param heap_block Heap block of the HLE applet. -     * @param offset The offset into the heap block that the SharedMemory will map. -     * @param size Size of the memory block. Must be page-aligned. -     * @param permissions Permission restrictions applied to the process which created the block. -     * @param other_permissions Permission restrictions applied to other processes mapping the -     * block. -     * @param name Optional object name, used for debugging purposes. -     */ -    static std::shared_ptr<SharedMemory> CreateForApplet( -        KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset, -        u64 size, MemoryPermission permissions, MemoryPermission other_permissions, -        std::string name = "Unknown Applet"); +    static std::shared_ptr<SharedMemory> Create( +        KernelCore& kernel, Core::DeviceMemory& device_memory, Process* owner_process, +        Memory::PageLinkedList&& page_list, Memory::MemoryPermission owner_permission, +        Memory::MemoryPermission user_permission, PAddr physical_address, std::size_t size, +        std::string name);      std::string GetTypeName() const override {          return "SharedMemory";      } +      std::string GetName() const override {          return name;      } @@ -83,71 +43,42 @@ public:          return HANDLE_TYPE;      } -    /// Gets the size of the underlying memory block in bytes. -    u64 GetSize() const { -        return size; -    } - -    /** -     * Converts the specified MemoryPermission into the equivalent VMAPermission. -     * @param permission The MemoryPermission to convert. -     */ -    static VMAPermission ConvertPermissions(MemoryPermission permission); -      /**       * Maps a shared memory block to an address in the target process' address space -     * @param target_process Process on which to map the memory block. +     * @param target_process Process on which to map the memory block       * @param address Address in system memory to map shared memory block to +     * @param size Size of the shared memory block to map       * @param permissions Memory block map permissions (specified by SVC field) -     * @param other_permissions Memory block map other permissions (specified by SVC field) -     */ -    ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions, -                   MemoryPermission other_permissions); - -    /** -     * Unmaps a shared memory block from the specified address in system memory -     * -     * @param target_process Process from which to unmap the memory block. -     * @param address        Address in system memory where the shared memory block is mapped. -     * @param unmap_size     The amount of bytes to unmap from this shared memory instance. -     * -     * @return Result code of the unmap operation -     * -     * @pre The given size to unmap must be the same size as the amount of memory managed by -     *      the SharedMemory instance itself, otherwise ERR_INVALID_SIZE will be returned.       */ -    ResultCode Unmap(Process& target_process, VAddr address, u64 unmap_size); +    ResultCode Map(Process& target_process, VAddr address, std::size_t size, +                   Memory::MemoryPermission permission);      /**       * Gets a pointer to the shared memory block       * @param offset Offset from the start of the shared memory block to get pointer       * @return A pointer to the shared memory block from the specified offset       */ -    u8* GetPointer(std::size_t offset = 0); +    u8* GetPointer(std::size_t offset = 0) { +        return device_memory.GetPointer(physical_address + offset); +    }      /** -     * Gets a constant pointer to the shared memory block +     * Gets a pointer to the shared memory block       * @param offset Offset from the start of the shared memory block to get pointer -     * @return A constant pointer to the shared memory block from the specified offset +     * @return A pointer to the shared memory block from the specified offset       */ -    const u8* GetPointer(std::size_t offset = 0) const; +    const u8* GetPointer(std::size_t offset = 0) const { +        return device_memory.GetPointer(physical_address + offset); +    }  private: -    /// Backing memory for this shared memory block. -    std::shared_ptr<PhysicalMemory> backing_block; -    /// Offset into the backing block for this shared memory. -    std::size_t backing_block_offset = 0; -    /// Size of the memory block. Page-aligned. -    u64 size = 0; -    /// Permission restrictions applied to the process which created the block. -    MemoryPermission permissions{}; -    /// Permission restrictions applied to other processes mapping the block. -    MemoryPermission other_permissions{}; -    /// Process that created this shared memory block. -    Process* owner_process; -    /// Address of shared memory block in the owner process if specified. -    VAddr base_address = 0; -    /// Name of shared memory object. +    Core::DeviceMemory& device_memory; +    Process* owner_process{}; +    Memory::PageLinkedList page_list; +    Memory::MemoryPermission owner_permission{}; +    Memory::MemoryPermission user_permission{}; +    PAddr physical_address{}; +    std::size_t size{};      std::string name;  }; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 4ffc113c2..4134acf65 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -24,6 +24,8 @@  #include "core/hle/kernel/errors.h"  #include "core/hle/kernel/handle_table.h"  #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory/memory_block.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/mutex.h"  #include "core/hle/kernel/process.h"  #include "core/hle/kernel/readable_event.h" @@ -31,6 +33,7 @@  #include "core/hle/kernel/scheduler.h"  #include "core/hle/kernel/shared_memory.h"  #include "core/hle/kernel/svc.h" +#include "core/hle/kernel/svc_types.h"  #include "core/hle/kernel/svc_wrap.h"  #include "core/hle/kernel/synchronization.h"  #include "core/hle/kernel/thread.h" @@ -42,7 +45,7 @@  #include "core/memory.h"  #include "core/reporter.h" -namespace Kernel { +namespace Kernel::Svc {  namespace {  // Checks if address + size is greater than the given address @@ -58,8 +61,8 @@ constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;  // Helper function that performs the common sanity checks for svcMapMemory  // and svcUnmapMemory. This is doable, as both functions perform their sanitizing  // in the same order. -ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr, -                                      u64 size) { +ResultCode MapUnmapMemorySanityChecks(const Memory::PageTable& manager, VAddr dst_addr, +                                      VAddr src_addr, u64 size) {      if (!Common::Is4KBAligned(dst_addr)) {          LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);          return ERR_INVALID_ADDRESS; @@ -93,36 +96,33 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add          return ERR_INVALID_ADDRESS_STATE;      } -    if (!vm_manager.IsWithinAddressSpace(src_addr, size)) { +    if (!manager.IsInsideAddressSpace(src_addr, size)) {          LOG_ERROR(Kernel_SVC,                    "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",                    src_addr, size);          return ERR_INVALID_ADDRESS_STATE;      } -    if (!vm_manager.IsWithinStackRegion(dst_addr, size)) { +    if (manager.IsOutsideStackRegion(dst_addr, size)) {          LOG_ERROR(Kernel_SVC,                    "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",                    dst_addr, size);          return ERR_INVALID_MEMORY_RANGE;      } -    const VAddr dst_end_address = dst_addr + size; -    if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() && -        vm_manager.GetHeapRegionEndAddress() > dst_addr) { +    if (manager.IsInsideHeapRegion(dst_addr, size)) {          LOG_ERROR(Kernel_SVC,                    "Destination does not fit within the heap region, addr=0x{:016X}, " -                  "size=0x{:016X}, end_addr=0x{:016X}", -                  dst_addr, size, dst_end_address); +                  "size=0x{:016X}", +                  dst_addr, size);          return ERR_INVALID_MEMORY_RANGE;      } -    if (dst_end_address > vm_manager.GetMapRegionBaseAddress() && -        vm_manager.GetMapRegionEndAddress() > dst_addr) { +    if (manager.IsInsideAliasRegion(dst_addr, size)) {          LOG_ERROR(Kernel_SVC,                    "Destination does not fit within the map region, addr=0x{:016X}, " -                  "size=0x{:016X}, end_addr=0x{:016X}", -                  dst_addr, size, dst_end_address); +                  "size=0x{:016X}", +                  dst_addr, size);          return ERR_INVALID_MEMORY_RANGE;      } @@ -177,13 +177,10 @@ static ResultCode SetHeapSize(Core::System& system, VAddr* heap_addr, u64 heap_s          return ERR_INVALID_SIZE;      } -    auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); -    const auto alloc_result = vm_manager.SetHeapSize(heap_size); -    if (alloc_result.Failed()) { -        return alloc_result.Code(); -    } +    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; + +    CASCADE_RESULT(*heap_addr, page_table.SetHeapSize(heap_size)); -    *heap_addr = *alloc_result;      return RESULT_SUCCESS;  } @@ -194,63 +191,6 @@ static ResultCode SetHeapSize32(Core::System& system, u32* heap_addr, u32 heap_s      return result;  } -static ResultCode SetMemoryPermission(Core::System& system, VAddr addr, u64 size, u32 prot) { -    LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot); - -    if (!Common::Is4KBAligned(addr)) { -        LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr); -        return ERR_INVALID_ADDRESS; -    } - -    if (size == 0) { -        LOG_ERROR(Kernel_SVC, "Size is 0"); -        return ERR_INVALID_SIZE; -    } - -    if (!Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size); -        return ERR_INVALID_SIZE; -    } - -    if (!IsValidAddressRange(addr, size)) { -        LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}", -                  addr, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    const auto permission = static_cast<MemoryPermission>(prot); -    if (permission != MemoryPermission::None && permission != MemoryPermission::Read && -        permission != MemoryPermission::ReadWrite) { -        LOG_ERROR(Kernel_SVC, "Invalid memory permission specified, Got memory permission=0x{:08X}", -                  static_cast<u32>(permission)); -        return ERR_INVALID_MEMORY_PERMISSIONS; -    } - -    auto* const current_process = system.Kernel().CurrentProcess(); -    auto& vm_manager = current_process->VMManager(); - -    if (!vm_manager.IsWithinAddressSpace(addr, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, -                  size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    const VMManager::VMAHandle iter = vm_manager.FindVMA(addr); -    if (!vm_manager.IsValidHandle(iter)) { -        LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented."); -    // TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't -    // make sense to allow changing permissions on kernel memory itself, etc). - -    const auto converted_permissions = SharedMemory::ConvertPermissions(permission); - -    return vm_manager.ReprotectRange(addr, size, converted_permissions); -} -  static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 size, u32 mask,                                       u32 attribute) {      LOG_DEBUG(Kernel_SVC, @@ -274,30 +214,19 @@ static ResultCode SetMemoryAttribute(Core::System& system, VAddr address, u64 si          return ERR_INVALID_ADDRESS_STATE;      } -    const auto mem_attribute = static_cast<MemoryAttribute>(attribute); -    const auto mem_mask = static_cast<MemoryAttribute>(mask); -    const auto attribute_with_mask = mem_attribute | mem_mask; - -    if (attribute_with_mask != mem_mask) { +    const auto attributes{static_cast<Memory::MemoryAttribute>(mask | attribute)}; +    if (attributes != static_cast<Memory::MemoryAttribute>(mask) || +        (attributes | Memory::MemoryAttribute::Uncached) != Memory::MemoryAttribute::Uncached) {          LOG_ERROR(Kernel_SVC,                    "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",                    attribute, mask);          return ERR_INVALID_COMBINATION;      } -    if ((attribute_with_mask | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) { -        LOG_ERROR(Kernel_SVC, "Specified attribute isn't equal to MemoryAttributeUncached (8)."); -        return ERR_INVALID_COMBINATION; -    } - -    auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); -    if (!vm_manager.IsWithinAddressSpace(address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Given address (0x{:016X}) is outside the bounds of the address space.", address); -        return ERR_INVALID_ADDRESS_STATE; -    } +    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; -    return vm_manager.SetMemoryAttribute(address, size, mem_mask, mem_attribute); +    return page_table.SetMemoryAttribute(address, size, static_cast<Memory::MemoryAttribute>(mask), +                                         static_cast<Memory::MemoryAttribute>(attribute));  }  /// Maps a memory range into a different range. @@ -305,14 +234,14 @@ static ResultCode MapMemory(Core::System& system, VAddr dst_addr, VAddr src_addr      LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,                src_addr, size); -    auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); -    const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); +    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; -    if (result.IsError()) { +    if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; +        result.IsError()) {          return result;      } -    return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack); +    return page_table.Map(dst_addr, src_addr, size);  }  /// Unmaps a region that was previously mapped with svcMapMemory @@ -320,21 +249,14 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad      LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,                src_addr, size); -    auto& vm_manager = system.Kernel().CurrentProcess()->VMManager(); -    const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size); +    auto& page_table{system.Kernel().CurrentProcess()->PageTable()}; -    if (result.IsError()) { +    if (const ResultCode result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; +        result.IsError()) {          return result;      } -    const auto unmap_res = vm_manager.UnmapRange(dst_addr, size); - -    // Reprotect the source mapping on success -    if (unmap_res.IsSuccess()) { -        ASSERT(vm_manager.ReprotectRange(src_addr, size, VMAPermission::ReadWrite).IsSuccess()); -    } - -    return unmap_res; +    return page_table.Unmap(dst_addr, src_addr, size);  }  /// Connect to an OS service given the port name, returns the handle to the port to out @@ -367,6 +289,8 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle,          return ERR_NOT_FOUND;      } +    ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Sessions, 1)); +      auto client_port = it->second;      std::shared_ptr<ClientSession> client_session; @@ -538,7 +462,7 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand                "requesting_current_thread_handle=0x{:08X}",                holding_thread_handle, mutex_addr, requesting_thread_handle); -    if (Memory::IsKernelVirtualAddress(mutex_addr)) { +    if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {          LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",                    mutex_addr);          return ERR_INVALID_ADDRESS_STATE; @@ -558,7 +482,7 @@ static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_hand  static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {      LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); -    if (Memory::IsKernelVirtualAddress(mutex_addr)) { +    if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {          LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",                    mutex_addr);          return ERR_INVALID_ADDRESS_STATE; @@ -683,7 +607,6 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {          auto* const current_thread = system.CurrentScheduler().GetCurrentThread();          const auto thread_processor_id = current_thread->GetProcessorID();          system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); -        ASSERT(false);          system.Kernel().CurrentProcess()->PrepareForTermination(); @@ -785,35 +708,35 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha              return RESULT_SUCCESS;          case GetInfoType::MapRegionBaseAddr: -            *result = process->VMManager().GetMapRegionBaseAddress(); +            *result = process->PageTable().GetAliasRegionStart();              return RESULT_SUCCESS;          case GetInfoType::MapRegionSize: -            *result = process->VMManager().GetMapRegionSize(); +            *result = process->PageTable().GetAliasRegionSize();              return RESULT_SUCCESS;          case GetInfoType::HeapRegionBaseAddr: -            *result = process->VMManager().GetHeapRegionBaseAddress(); +            *result = process->PageTable().GetHeapRegionStart();              return RESULT_SUCCESS;          case GetInfoType::HeapRegionSize: -            *result = process->VMManager().GetHeapRegionSize(); +            *result = process->PageTable().GetHeapRegionSize();              return RESULT_SUCCESS;          case GetInfoType::ASLRRegionBaseAddr: -            *result = process->VMManager().GetASLRRegionBaseAddress(); +            *result = process->PageTable().GetAliasCodeRegionStart();              return RESULT_SUCCESS;          case GetInfoType::ASLRRegionSize: -            *result = process->VMManager().GetASLRRegionSize(); +            *result = process->PageTable().GetAliasCodeRegionSize();              return RESULT_SUCCESS;          case GetInfoType::StackRegionBaseAddr: -            *result = process->VMManager().GetStackRegionBaseAddress(); +            *result = process->PageTable().GetStackRegionStart();              return RESULT_SUCCESS;          case GetInfoType::StackRegionSize: -            *result = process->VMManager().GetStackRegionSize(); +            *result = process->PageTable().GetStackRegionSize();              return RESULT_SUCCESS;          case GetInfoType::TotalPhysicalMemoryAvailable: @@ -987,20 +910,29 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size)          return ERR_INVALID_MEMORY_RANGE;      } -    Process* const current_process = system.Kernel().CurrentProcess(); -    auto& vm_manager = current_process->VMManager(); +    Process* const current_process{system.Kernel().CurrentProcess()}; +    auto& page_table{current_process->PageTable()};      if (current_process->GetSystemResourceSize() == 0) {          LOG_ERROR(Kernel_SVC, "System Resource Size is zero");          return ERR_INVALID_STATE;      } -    if (!vm_manager.IsWithinMapRegion(addr, size)) { -        LOG_ERROR(Kernel_SVC, "Range not within map region"); +    if (!page_table.IsInsideAddressSpace(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, +                  size); +        return ERR_INVALID_MEMORY_RANGE; +    } + +    if (page_table.IsOutsideAliasRegion(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, +                  size);          return ERR_INVALID_MEMORY_RANGE;      } -    return vm_manager.MapPhysicalMemory(addr, size); +    return page_table.MapPhysicalMemory(addr, size);  }  /// Unmaps memory previously mapped via MapPhysicalMemory @@ -1027,20 +959,29 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size          return ERR_INVALID_MEMORY_RANGE;      } -    Process* const current_process = system.Kernel().CurrentProcess(); -    auto& vm_manager = current_process->VMManager(); +    Process* const current_process{system.Kernel().CurrentProcess()}; +    auto& page_table{current_process->PageTable()};      if (current_process->GetSystemResourceSize() == 0) {          LOG_ERROR(Kernel_SVC, "System Resource Size is zero");          return ERR_INVALID_STATE;      } -    if (!vm_manager.IsWithinMapRegion(addr, size)) { -        LOG_ERROR(Kernel_SVC, "Range not within map region"); +    if (!page_table.IsInsideAddressSpace(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, +                  size); +        return ERR_INVALID_MEMORY_RANGE; +    } + +    if (page_table.IsOutsideAliasRegion(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, +                  size);          return ERR_INVALID_MEMORY_RANGE;      } -    return vm_manager.UnmapPhysicalMemory(addr, size); +    return page_table.UnmapPhysicalMemory(addr, size);  }  /// Sets the thread activity @@ -1197,74 +1138,49 @@ static ResultCode MapSharedMemory(Core::System& system, Handle shared_memory_han          return ERR_INVALID_ADDRESS_STATE;      } -    const auto permissions_type = static_cast<MemoryPermission>(permissions); -    if (permissions_type != MemoryPermission::Read && -        permissions_type != MemoryPermission::ReadWrite) { +    const auto permission_type = static_cast<Memory::MemoryPermission>(permissions); +    if ((permission_type | Memory::MemoryPermission::Write) != +        Memory::MemoryPermission::ReadAndWrite) {          LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",                    permissions);          return ERR_INVALID_MEMORY_PERMISSIONS;      } -    auto* const current_process = system.Kernel().CurrentProcess(); -    auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); -    if (!shared_memory) { -        LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}", -                  shared_memory_handle); -        return ERR_INVALID_HANDLE; -    } +    auto* const current_process{system.Kernel().CurrentProcess()}; +    auto& page_table{current_process->PageTable()}; -    const auto& vm_manager = current_process->VMManager(); -    if (!vm_manager.IsWithinASLRRegion(addr, size)) { -        LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}", +    if (page_table.IsInvalidRegion(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Addr does not fit within the valid region, addr=0x{:016X}, " +                  "size=0x{:016X}",                    addr, size);          return ERR_INVALID_MEMORY_RANGE;      } -    return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare); -} - -static ResultCode UnmapSharedMemory(Core::System& system, Handle shared_memory_handle, VAddr addr, -                                    u64 size) { -    LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", -                shared_memory_handle, addr, size); - -    if (!Common::Is4KBAligned(addr)) { -        LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr); -        return ERR_INVALID_ADDRESS; -    } - -    if (size == 0) { -        LOG_ERROR(Kernel_SVC, "Size is 0"); -        return ERR_INVALID_SIZE; -    } - -    if (!Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size); -        return ERR_INVALID_SIZE; +    if (page_table.IsInsideHeapRegion(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Addr does not fit within the heap region, addr=0x{:016X}, " +                  "size=0x{:016X}", +                  addr, size); +        return ERR_INVALID_MEMORY_RANGE;      } -    if (!IsValidAddressRange(addr, size)) { -        LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}", +    if (page_table.IsInsideAliasRegion(addr, size)) { +        LOG_ERROR(Kernel_SVC, +                  "Address does not fit within the map region, addr=0x{:016X}, " +                  "size=0x{:016X}",                    addr, size); -        return ERR_INVALID_ADDRESS_STATE; +        return ERR_INVALID_MEMORY_RANGE;      } -    auto* const current_process = system.Kernel().CurrentProcess(); -    auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle); +    auto shared_memory{current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle)};      if (!shared_memory) {          LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",                    shared_memory_handle);          return ERR_INVALID_HANDLE;      } -    const auto& vm_manager = current_process->VMManager(); -    if (!vm_manager.IsWithinASLRRegion(addr, size)) { -        LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}", -                  addr, size); -        return ERR_INVALID_MEMORY_RANGE; -    } - -    return shared_memory->Unmap(*current_process, addr, size); +    return shared_memory->Map(*current_process, addr, size, permission_type);  }  static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address, @@ -1279,18 +1195,17 @@ static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_add          return ERR_INVALID_HANDLE;      } -    auto& memory = system.Memory(); -    const auto& vm_manager = process->VMManager(); -    const MemoryInfo memory_info = vm_manager.QueryMemory(address); - -    memory.Write64(memory_info_address, memory_info.base_address); -    memory.Write64(memory_info_address + 8, memory_info.size); -    memory.Write32(memory_info_address + 16, memory_info.state); -    memory.Write32(memory_info_address + 20, memory_info.attributes); -    memory.Write32(memory_info_address + 24, memory_info.permission); -    memory.Write32(memory_info_address + 32, memory_info.ipc_ref_count); -    memory.Write32(memory_info_address + 28, memory_info.device_ref_count); -    memory.Write32(memory_info_address + 36, 0); +    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 + 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 + 0x24, 0);      // Page info appears to be currently unused by the kernel and is always set to zero.      memory.Write32(page_info_address, 0); @@ -1314,142 +1229,6 @@ static ResultCode QueryMemory32(Core::System& system, u32 memory_info_address,      return QueryMemory(system, memory_info_address, page_info_address, query_address);  } -static ResultCode MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst_address, -                                       u64 src_address, u64 size) { -    LOG_DEBUG(Kernel_SVC, -              "called. process_handle=0x{:08X}, dst_address=0x{:016X}, " -              "src_address=0x{:016X}, size=0x{:016X}", -              process_handle, dst_address, src_address, size); - -    if (!Common::Is4KBAligned(src_address)) { -        LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", -                  src_address); -        return ERR_INVALID_ADDRESS; -    } - -    if (!Common::Is4KBAligned(dst_address)) { -        LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", -                  dst_address); -        return ERR_INVALID_ADDRESS; -    } - -    if (size == 0 || !Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X})", size); -        return ERR_INVALID_SIZE; -    } - -    if (!IsValidAddressRange(dst_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination address range overflows the address space (dst_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  dst_address, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    if (!IsValidAddressRange(src_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source address range overflows the address space (src_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  src_address, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); -    auto process = handle_table.Get<Process>(process_handle); -    if (!process) { -        LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", -                  process_handle); -        return ERR_INVALID_HANDLE; -    } - -    auto& vm_manager = process->VMManager(); -    if (!vm_manager.IsWithinAddressSpace(src_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source address range is not within the address space (src_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  src_address, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  dst_address, size); -        return ERR_INVALID_MEMORY_RANGE; -    } - -    return vm_manager.MapCodeMemory(dst_address, src_address, size); -} - -static ResultCode UnmapProcessCodeMemory(Core::System& system, Handle process_handle, -                                         u64 dst_address, u64 src_address, u64 size) { -    LOG_DEBUG(Kernel_SVC, -              "called. process_handle=0x{:08X}, dst_address=0x{:016X}, src_address=0x{:016X}, " -              "size=0x{:016X}", -              process_handle, dst_address, src_address, size); - -    if (!Common::Is4KBAligned(dst_address)) { -        LOG_ERROR(Kernel_SVC, "dst_address is not page-aligned (dst_address=0x{:016X}).", -                  dst_address); -        return ERR_INVALID_ADDRESS; -    } - -    if (!Common::Is4KBAligned(src_address)) { -        LOG_ERROR(Kernel_SVC, "src_address is not page-aligned (src_address=0x{:016X}).", -                  src_address); -        return ERR_INVALID_ADDRESS; -    } - -    if (size == 0 || Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is zero or not page-aligned (size=0x{:016X}).", size); -        return ERR_INVALID_SIZE; -    } - -    if (!IsValidAddressRange(dst_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination address range overflows the address space (dst_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  dst_address, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    if (!IsValidAddressRange(src_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source address range overflows the address space (src_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  src_address, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); -    auto process = handle_table.Get<Process>(process_handle); -    if (!process) { -        LOG_ERROR(Kernel_SVC, "Invalid process handle specified (handle=0x{:08X}).", -                  process_handle); -        return ERR_INVALID_HANDLE; -    } - -    auto& vm_manager = process->VMManager(); -    if (!vm_manager.IsWithinAddressSpace(src_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Source address range is not within the address space (src_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  src_address, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    if (!vm_manager.IsWithinASLRRegion(dst_address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " -                  "size=0x{:016X}).", -                  dst_address, size); -        return ERR_INVALID_MEMORY_RANGE; -    } - -    return vm_manager.UnmapCodeMemory(dst_address, src_address, size); -} -  /// Exits the current process  static void ExitProcess(Core::System& system) {      auto* current_process = system.Kernel().CurrentProcess(); @@ -1506,6 +1285,9 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e      }      auto& kernel = system.Kernel(); + +    ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); +      CASCADE_RESULT(std::shared_ptr<Thread> thread,                     Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top,                                    *current_process)); @@ -1610,7 +1392,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add          "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",          mutex_addr, condition_variable_addr, thread_handle, nano_seconds); -    if (Memory::IsKernelVirtualAddress(mutex_addr)) { +    if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) {          LOG_ERROR(              Kernel_SVC,              "Given mutex address must not be within the kernel address space. address=0x{:016X}", @@ -1741,7 +1523,7 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,                type, value, timeout);      // If the passed address is a kernel virtual address, return invalid memory state. -    if (Memory::IsKernelVirtualAddress(address)) { +    if (Core::Memory::IsKernelVirtualAddress(address)) {          LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);          return ERR_INVALID_ADDRESS_STATE;      } @@ -1769,7 +1551,7 @@ static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type,                address, type, value, num_to_wake);      // If the passed address is a kernel virtual address, return invalid memory state. -    if (Memory::IsKernelVirtualAddress(address)) { +    if (Core::Memory::IsKernelVirtualAddress(address)) {          LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);          return ERR_INVALID_ADDRESS_STATE;      } @@ -1865,9 +1647,9 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd          return ERR_INVALID_ADDRESS_STATE;      } -    const auto perms = static_cast<MemoryPermission>(permissions); -    if (perms != MemoryPermission::None && perms != MemoryPermission::Read && -        perms != MemoryPermission::ReadWrite) { +    const auto perms{static_cast<Memory::MemoryPermission>(permissions)}; +    if (perms > Memory::MemoryPermission::ReadAndWrite || +        perms == Memory::MemoryPermission::Write) {          LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",                    permissions);          return ERR_INVALID_MEMORY_PERMISSIONS; @@ -1890,111 +1672,6 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd      return RESULT_SUCCESS;  } -static ResultCode MapTransferMemory(Core::System& system, Handle handle, VAddr address, u64 size, -                                    u32 permission_raw) { -    LOG_DEBUG(Kernel_SVC, -              "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}, permissions=0x{:08X}", -              handle, address, size, permission_raw); - -    if (!Common::Is4KBAligned(address)) { -        LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).", -                  address); -        return ERR_INVALID_ADDRESS; -    } - -    if (size == 0 || !Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, -                  "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).", -                  size); -        return ERR_INVALID_SIZE; -    } - -    if (!IsValidAddressRange(address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Given address and size overflows the 64-bit range (address=0x{:016X}, " -                  "size=0x{:016X}).", -                  address, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    const auto permissions = static_cast<MemoryPermission>(permission_raw); -    if (permissions != MemoryPermission::None && permissions != MemoryPermission::Read && -        permissions != MemoryPermission::ReadWrite) { -        LOG_ERROR(Kernel_SVC, "Invalid transfer memory permissions given (permissions=0x{:08X}).", -                  permission_raw); -        return ERR_INVALID_STATE; -    } - -    const auto& kernel = system.Kernel(); -    const auto* const current_process = kernel.CurrentProcess(); -    const auto& handle_table = current_process->GetHandleTable(); - -    auto transfer_memory = handle_table.Get<TransferMemory>(handle); -    if (!transfer_memory) { -        LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).", -                  handle); -        return ERR_INVALID_HANDLE; -    } - -    if (!current_process->VMManager().IsWithinASLRRegion(address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Given address and size don't fully fit within the ASLR region " -                  "(address=0x{:016X}, size=0x{:016X}).", -                  address, size); -        return ERR_INVALID_MEMORY_RANGE; -    } - -    return transfer_memory->MapMemory(address, size, permissions); -} - -static ResultCode UnmapTransferMemory(Core::System& system, Handle handle, VAddr address, -                                      u64 size) { -    LOG_DEBUG(Kernel_SVC, "called. handle=0x{:08X}, address=0x{:016X}, size=0x{:016X}", handle, -              address, size); - -    if (!Common::Is4KBAligned(address)) { -        LOG_ERROR(Kernel_SVC, "Transfer memory addresses must be 4KB aligned (size=0x{:016X}).", -                  address); -        return ERR_INVALID_ADDRESS; -    } - -    if (size == 0 || !Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, -                  "Transfer memory sizes must be 4KB aligned and not be zero (size=0x{:016X}).", -                  size); -        return ERR_INVALID_SIZE; -    } - -    if (!IsValidAddressRange(address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Given address and size overflows the 64-bit range (address=0x{:016X}, " -                  "size=0x{:016X}).", -                  address, size); -        return ERR_INVALID_ADDRESS_STATE; -    } - -    const auto& kernel = system.Kernel(); -    const auto* const current_process = kernel.CurrentProcess(); -    const auto& handle_table = current_process->GetHandleTable(); - -    auto transfer_memory = handle_table.Get<TransferMemory>(handle); -    if (!transfer_memory) { -        LOG_ERROR(Kernel_SVC, "Nonexistent transfer memory handle given (handle=0x{:08X}).", -                  handle); -        return ERR_INVALID_HANDLE; -    } - -    if (!current_process->VMManager().IsWithinASLRRegion(address, size)) { -        LOG_ERROR(Kernel_SVC, -                  "Given address and size don't fully fit within the ASLR region " -                  "(address=0x{:016X}, size=0x{:016X}).", -                  address, size); -        return ERR_INVALID_MEMORY_RANGE; -    } - -    return transfer_memory->UnmapMemory(address, size); -} -  static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core,                                      u64* mask) {      LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); @@ -2073,52 +1750,6 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,      return RESULT_SUCCESS;  } -static ResultCode CreateSharedMemory(Core::System& system, Handle* handle, u64 size, -                                     u32 local_permissions, u32 remote_permissions) { -    LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, -              local_permissions, remote_permissions); -    if (size == 0) { -        LOG_ERROR(Kernel_SVC, "Size is 0"); -        return ERR_INVALID_SIZE; -    } -    if (!Common::Is4KBAligned(size)) { -        LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size); -        return ERR_INVALID_SIZE; -    } - -    if (size >= MAIN_MEMORY_SIZE) { -        LOG_ERROR(Kernel_SVC, "Size is not less than 8GB, 0x{:016X}", size); -        return ERR_INVALID_SIZE; -    } - -    const auto local_perms = static_cast<MemoryPermission>(local_permissions); -    if (local_perms != MemoryPermission::Read && local_perms != MemoryPermission::ReadWrite) { -        LOG_ERROR(Kernel_SVC, -                  "Invalid local memory permissions, expected Read or ReadWrite but got " -                  "local_permissions={}", -                  static_cast<u32>(local_permissions)); -        return ERR_INVALID_MEMORY_PERMISSIONS; -    } - -    const auto remote_perms = static_cast<MemoryPermission>(remote_permissions); -    if (remote_perms != MemoryPermission::Read && remote_perms != MemoryPermission::ReadWrite && -        remote_perms != MemoryPermission::DontCare) { -        LOG_ERROR(Kernel_SVC, -                  "Invalid remote memory permissions, expected Read, ReadWrite or DontCare but got " -                  "remote_permissions={}", -                  static_cast<u32>(remote_permissions)); -        return ERR_INVALID_MEMORY_PERMISSIONS; -    } - -    auto& kernel = system.Kernel(); -    auto process = kernel.CurrentProcess(); -    auto& handle_table = process->GetHandleTable(); -    auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); - -    CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); -    return RESULT_SUCCESS; -} -  static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) {      LOG_DEBUG(Kernel_SVC, "called"); @@ -2305,11 +1936,10 @@ static ResultCode GetProcessList(Core::System& system, u32* out_num_processes,      }      const auto& kernel = system.Kernel(); -    const auto& vm_manager = kernel.CurrentProcess()->VMManager();      const auto total_copy_size = out_process_ids_size * sizeof(u64); -    if (out_process_ids_size > 0 && -        !vm_manager.IsWithinAddressSpace(out_process_ids, total_copy_size)) { +    if (out_process_ids_size > 0 && !kernel.CurrentProcess()->PageTable().IsInsideAddressSpace( +                                        out_process_ids, total_copy_size)) {          LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",                    out_process_ids, out_process_ids + total_copy_size);          return ERR_INVALID_ADDRESS_STATE; @@ -2345,11 +1975,10 @@ static ResultCode GetThreadList(Core::System& system, u32* out_num_threads, VAdd      }      const auto* const current_process = system.Kernel().CurrentProcess(); -    const auto& vm_manager = current_process->VMManager();      const auto total_copy_size = out_thread_ids_size * sizeof(u64);      if (out_thread_ids_size > 0 && -        !vm_manager.IsWithinAddressSpace(out_thread_ids, total_copy_size)) { +        !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) {          LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}",                    out_thread_ids, out_thread_ids + total_copy_size);          return ERR_INVALID_ADDRESS_STATE; @@ -2510,7 +2139,7 @@ static const FunctionDef SVC_Table_32[] = {  static const FunctionDef SVC_Table_64[] = {      {0x00, nullptr, "Unknown"},      {0x01, SvcWrap64<SetHeapSize>, "SetHeapSize"}, -    {0x02, SvcWrap64<SetMemoryPermission>, "SetMemoryPermission"}, +    {0x02, nullptr, "SetMemoryPermission"},      {0x03, SvcWrap64<SetMemoryAttribute>, "SetMemoryAttribute"},      {0x04, SvcWrap64<MapMemory>, "MapMemory"},      {0x05, SvcWrap64<UnmapMemory>, "UnmapMemory"}, @@ -2528,7 +2157,7 @@ static const FunctionDef SVC_Table_64[] = {      {0x11, SvcWrap64<SignalEvent>, "SignalEvent"},      {0x12, SvcWrap64<ClearEvent>, "ClearEvent"},      {0x13, SvcWrap64<MapSharedMemory>, "MapSharedMemory"}, -    {0x14, SvcWrap64<UnmapSharedMemory>, "UnmapSharedMemory"}, +    {0x14, nullptr, "UnmapSharedMemory"},      {0x15, SvcWrap64<CreateTransferMemory>, "CreateTransferMemory"},      {0x16, SvcWrap64<CloseHandle>, "CloseHandle"},      {0x17, SvcWrap64<ResetSignal>, "ResetSignal"}, @@ -2588,9 +2217,9 @@ static const FunctionDef SVC_Table_64[] = {      {0x4D, nullptr, "SleepSystem"},      {0x4E, nullptr, "ReadWriteRegister"},      {0x4F, nullptr, "SetProcessActivity"}, -    {0x50, SvcWrap64<CreateSharedMemory>, "CreateSharedMemory"}, -    {0x51, SvcWrap64<MapTransferMemory>, "MapTransferMemory"}, -    {0x52, SvcWrap64<UnmapTransferMemory>, "UnmapTransferMemory"}, +    {0x50, nullptr, "CreateSharedMemory"}, +    {0x51, nullptr, "MapTransferMemory"}, +    {0x52, nullptr, "UnmapTransferMemory"},      {0x53, nullptr, "CreateInterruptEvent"},      {0x54, nullptr, "QueryPhysicalAddress"},      {0x55, nullptr, "QueryIoMapping"}, @@ -2627,8 +2256,8 @@ static const FunctionDef SVC_Table_64[] = {      {0x74, nullptr, "MapProcessMemory"},      {0x75, nullptr, "UnmapProcessMemory"},      {0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"}, -    {0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"}, -    {0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"}, +    {0x77, nullptr, "MapProcessCodeMemory"}, +    {0x78, nullptr, "UnmapProcessCodeMemory"},      {0x79, nullptr, "CreateProcess"},      {0x7A, nullptr, "StartProcess"},      {0x7B, nullptr, "TerminateProcess"}, @@ -2656,7 +2285,7 @@ static const FunctionDef* GetSVCInfo64(u32 func_num) {  MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); -void CallSVC(Core::System& system, u32 immediate) { +void Call(Core::System& system, u32 immediate) {      MICROPROFILE_SCOPE(Kernel_SVC);      // Lock the global kernel mutex when we enter the kernel HLE. @@ -2675,4 +2304,4 @@ void CallSVC(Core::System& system, u32 immediate) {      }  } -} // namespace Kernel +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index c5539ac1c..46e64277e 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h @@ -10,8 +10,8 @@ namespace Core {  class System;  } -namespace Kernel { +namespace Kernel::Svc { -void CallSVC(Core::System& system, u32 immediate); +void Call(Core::System& system, u32 immediate); -} // namespace Kernel +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h new file mode 100644 index 000000000..986724beb --- /dev/null +++ b/src/core/hle/kernel/svc_types.h @@ -0,0 +1,68 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_funcs.h" +#include "common/common_types.h" + +namespace Kernel::Svc { + +enum class MemoryState : u32 { +    Free = 0x00, +    Io = 0x01, +    Static = 0x02, +    Code = 0x03, +    CodeData = 0x04, +    Normal = 0x05, +    Shared = 0x06, +    Alias = 0x07, +    AliasCode = 0x08, +    AliasCodeData = 0x09, +    Ipc = 0x0A, +    Stack = 0x0B, +    ThreadLocal = 0x0C, +    Transfered = 0x0D, +    SharedTransfered = 0x0E, +    SharedCode = 0x0F, +    Inaccessible = 0x10, +    NonSecureIpc = 0x11, +    NonDeviceIpc = 0x12, +    Kernel = 0x13, +    GeneratedCode = 0x14, +    CodeOut = 0x15, +}; +DECLARE_ENUM_FLAG_OPERATORS(MemoryState); + +enum class MemoryAttribute : u32 { +    Locked = (1 << 0), +    IpcLocked = (1 << 1), +    DeviceShared = (1 << 2), +    Uncached = (1 << 3), +}; +DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute); + +enum class MemoryPermission : u32 { +    None = (0 << 0), +    Read = (1 << 0), +    Write = (1 << 1), +    Execute = (1 << 2), +    ReadWrite = Read | Write, +    ReadExecute = Read | Execute, +    DontCare = (1 << 28), +}; +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{}; +}; + +} // namespace Kernel::Svc diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index f2d3f8b49..765f408c3 100644 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp @@ -2,17 +2,16 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. -#include "core/hle/kernel/errors.h"  #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h" -#include "core/hle/kernel/shared_memory.h"  #include "core/hle/kernel/transfer_memory.h"  #include "core/hle/result.h"  #include "core/memory.h"  namespace Kernel { -TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory) +TransferMemory::TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory)      : Object{kernel}, memory{memory} {}  TransferMemory::~TransferMemory() { @@ -20,14 +19,15 @@ TransferMemory::~TransferMemory() {      Reset();  } -std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory, -                                                       VAddr base_address, u64 size, -                                                       MemoryPermission permissions) { +std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, +                                                       Core::Memory::Memory& memory, +                                                       VAddr base_address, std::size_t size, +                                                       Memory::MemoryPermission permissions) {      std::shared_ptr<TransferMemory> transfer_memory{          std::make_shared<TransferMemory>(kernel, memory)};      transfer_memory->base_address = base_address; -    transfer_memory->memory_size = size; +    transfer_memory->size = size;      transfer_memory->owner_permissions = permissions;      transfer_memory->owner_process = kernel.CurrentProcess(); @@ -38,98 +38,12 @@ const u8* TransferMemory::GetPointer() const {      return memory.GetPointer(base_address);  } -u64 TransferMemory::GetSize() const { -    return memory_size; -} - -ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission permissions) { -    if (memory_size != size) { -        return ERR_INVALID_SIZE; -    } - -    if (owner_permissions != permissions) { -        return ERR_INVALID_STATE; -    } - -    if (is_mapped) { -        return ERR_INVALID_STATE; -    } - -    backing_block = std::make_shared<PhysicalMemory>(size); - -    const auto map_state = owner_permissions == MemoryPermission::None -                               ? MemoryState::TransferMemoryIsolated -                               : MemoryState::TransferMemory; -    auto& vm_manager = owner_process->VMManager(); -    const auto map_result = vm_manager.MapMemoryBlock(address, backing_block, 0, size, map_state); -    if (map_result.Failed()) { -        return map_result.Code(); -    } - -    is_mapped = true; -    return RESULT_SUCCESS; -} -  ResultCode TransferMemory::Reserve() { -    auto& vm_manager{owner_process->VMManager()}; -    const auto check_range_result{vm_manager.CheckRangeState( -        base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, -        MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All, -        VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, -        MemoryAttribute::IpcAndDeviceMapped)}; - -    if (check_range_result.Failed()) { -        return check_range_result.Code(); -    } - -    auto [state_, permissions_, attribute] = *check_range_result; - -    if (const auto result{vm_manager.ReprotectRange( -            base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))}; -        result.IsError()) { -        return result; -    } - -    return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, -                                         attribute | MemoryAttribute::Locked); +    return owner_process->PageTable().ReserveTransferMemory(base_address, size, owner_permissions);  }  ResultCode TransferMemory::Reset() { -    auto& vm_manager{owner_process->VMManager()}; -    if (const auto result{vm_manager.CheckRangeState( -            base_address, memory_size, -            MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, -            MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None, -            VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, -            MemoryAttribute::IpcAndDeviceMapped)}; -        result.Failed()) { -        return result.Code(); -    } - -    if (const auto result{ -            vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)}; -        result.IsError()) { -        return result; -    } - -    return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, -                                         MemoryAttribute::None); -} - -ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) { -    if (memory_size != size) { -        return ERR_INVALID_SIZE; -    } - -    auto& vm_manager = owner_process->VMManager(); -    const auto result = vm_manager.UnmapRange(address, size); - -    if (result.IsError()) { -        return result; -    } - -    is_mapped = false; -    return RESULT_SUCCESS; +    return owner_process->PageTable().ResetTransferMemory(base_address, size);  }  } // namespace Kernel diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h index 6e388536a..05e9f7464 100644 --- a/src/core/hle/kernel/transfer_memory.h +++ b/src/core/hle/kernel/transfer_memory.h @@ -6,12 +6,13 @@  #include <memory> +#include "core/hle/kernel/memory/memory_block.h"  #include "core/hle/kernel/object.h"  #include "core/hle/kernel/physical_memory.h"  union ResultCode; -namespace Memory { +namespace Core::Memory {  class Memory;  } @@ -20,8 +21,6 @@ namespace Kernel {  class KernelCore;  class Process; -enum class MemoryPermission : u32; -  /// Defines the interface for transfer memory objects.  ///  /// Transfer memory is typically used for the purpose of @@ -30,14 +29,14 @@ enum class MemoryPermission : u32;  ///  class TransferMemory final : public Object {  public: -    explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory); +    explicit TransferMemory(KernelCore& kernel, Core::Memory::Memory& memory);      ~TransferMemory() override;      static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; -    static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory, -                                                  VAddr base_address, u64 size, -                                                  MemoryPermission permissions); +    static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Core::Memory::Memory& memory, +                                                  VAddr base_address, std::size_t size, +                                                  Memory::MemoryPermission permissions);      TransferMemory(const TransferMemory&) = delete;      TransferMemory& operator=(const TransferMemory&) = delete; @@ -61,29 +60,9 @@ public:      const u8* GetPointer() const;      /// Gets the size of the memory backing this instance in bytes. -    u64 GetSize() const; - -    /// Attempts to map transfer memory with the given range and memory permissions. -    /// -    /// @param address     The base address to being mapping memory at. -    /// @param size        The size of the memory to map, in bytes. -    /// @param permissions The memory permissions to check against when mapping memory. -    /// -    /// @pre The given address, size, and memory permissions must all match -    ///      the same values that were given when creating the transfer memory -    ///      instance. -    /// -    ResultCode MapMemory(VAddr address, u64 size, MemoryPermission permissions); - -    /// Unmaps the transfer memory with the given range -    /// -    /// @param address The base address to begin unmapping memory at. -    /// @param size    The size of the memory to unmap, in bytes. -    /// -    /// @pre The given address and size must be the same as the ones used -    ///      to create the transfer memory instance. -    /// -    ResultCode UnmapMemory(VAddr address, u64 size); +    constexpr std::size_t GetSize() const { +        return size; +    }      /// Reserves the region to be used for the transfer memory, called after the transfer memory is      /// created. @@ -94,25 +73,19 @@ public:      ResultCode Reset();  private: -    /// Memory block backing this instance. -    std::shared_ptr<PhysicalMemory> backing_block; -      /// The base address for the memory managed by this instance. -    VAddr base_address = 0; +    VAddr base_address{};      /// Size of the memory, in bytes, that this instance manages. -    u64 memory_size = 0; +    std::size_t size{};      /// The memory permissions that are applied to this instance. -    MemoryPermission owner_permissions{}; +    Memory::MemoryPermission owner_permissions{};      /// The process that this transfer memory instance was created under. -    Process* owner_process = nullptr; - -    /// Whether or not this transfer memory instance has mapped memory. -    bool is_mapped = false; +    Process* owner_process{}; -    Memory::Memory& memory; +    Core::Memory::Memory& memory;  };  } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp deleted file mode 100644 index 024c22901..000000000 --- a/src/core/hle/kernel/vm_manager.cpp +++ /dev/null @@ -1,1175 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <cstring> -#include <iterator> -#include <utility> -#include "common/alignment.h" -#include "common/assert.h" -#include "common/logging/log.h" -#include "common/memory_hook.h" -#include "core/core.h" -#include "core/file_sys/program_metadata.h" -#include "core/hle/kernel/errors.h" -#include "core/hle/kernel/process.h" -#include "core/hle/kernel/resource_limit.h" -#include "core/hle/kernel/vm_manager.h" -#include "core/memory.h" - -namespace Kernel { -namespace { -const char* GetMemoryStateName(MemoryState state) { -    static constexpr const char* names[] = { -        "Unmapped",       "Io", -        "Normal",         "Code", -        "CodeData",       "Heap", -        "Shared",         "Unknown1", -        "ModuleCode",     "ModuleCodeData", -        "IpcBuffer0",     "Stack", -        "ThreadLocal",    "TransferMemoryIsolated", -        "TransferMemory", "ProcessMemory", -        "Inaccessible",   "IpcBuffer1", -        "IpcBuffer3",     "KernelStack", -    }; - -    return names[ToSvcMemoryState(state)]; -} - -// Checks if a given address range lies within a larger address range. -constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin, -                                    VAddr address_range_end) { -    const VAddr end_address = address + size - 1; -    return address_range_begin <= address && end_address <= address_range_end - 1; -} -} // Anonymous namespace - -bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { -    ASSERT(base + size == next.base); -    if (permissions != next.permissions || state != next.state || attribute != next.attribute || -        type != next.type) { -        return false; -    } -    if ((attribute & MemoryAttribute::DeviceMapped) == MemoryAttribute::DeviceMapped) { -        // TODO: Can device mapped memory be merged sanely? -        // Not merging it may cause inaccuracies versus hardware when memory layout is queried. -        return false; -    } -    if (type == VMAType::AllocatedMemoryBlock) { -        return true; -    } -    if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) { -        return false; -    } -    if (type == VMAType::MMIO && paddr + size != next.paddr) { -        return false; -    } -    return true; -} - -VMManager::VMManager(Core::System& system) : system{system} { -    // Default to assuming a 39-bit address space. This way we have a sane -    // starting point with executables that don't provide metadata. -    Reset(FileSys::ProgramAddressSpaceType::Is39Bit); -} - -VMManager::~VMManager() = default; - -void VMManager::Reset(FileSys::ProgramAddressSpaceType type) { -    Clear(); - -    InitializeMemoryRegionRanges(type); - -    page_table.Resize(address_space_width); - -    // Initialize the map with a single free region covering the entire managed space. -    VirtualMemoryArea initial_vma; -    initial_vma.size = address_space_end; -    vma_map.emplace(initial_vma.base, initial_vma); - -    UpdatePageTableForVMA(initial_vma); -} - -VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { -    if (target >= address_space_end) { -        return vma_map.end(); -    } else { -        return std::prev(vma_map.upper_bound(target)); -    } -} - -bool VMManager::IsValidHandle(VMAHandle handle) const { -    return handle != vma_map.cend(); -} - -ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, -                                                          std::shared_ptr<PhysicalMemory> block, -                                                          std::size_t offset, u64 size, -                                                          MemoryState state, VMAPermission perm) { -    ASSERT(block != nullptr); -    ASSERT(offset + size <= block->size()); - -    // This is the appropriately sized VMA that will turn into our allocation. -    CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); -    VirtualMemoryArea& final_vma = vma_handle->second; -    ASSERT(final_vma.size == size); - -    final_vma.type = VMAType::AllocatedMemoryBlock; -    final_vma.permissions = perm; -    final_vma.state = state; -    final_vma.backing_block = std::move(block); -    final_vma.offset = offset; -    UpdatePageTableForVMA(final_vma); - -    return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); -} - -ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* memory, u64 size, -                                                            MemoryState state) { -    ASSERT(memory != nullptr); - -    // This is the appropriately sized VMA that will turn into our allocation. -    CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); -    VirtualMemoryArea& final_vma = vma_handle->second; -    ASSERT(final_vma.size == size); - -    final_vma.type = VMAType::BackingMemory; -    final_vma.permissions = VMAPermission::ReadWrite; -    final_vma.state = state; -    final_vma.backing_memory = memory; -    UpdatePageTableForVMA(final_vma); - -    return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); -} - -ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { -    return FindFreeRegion(GetASLRRegionBaseAddress(), GetASLRRegionEndAddress(), size); -} - -ResultVal<VAddr> VMManager::FindFreeRegion(VAddr begin, VAddr end, u64 size) const { -    ASSERT(begin < end); -    ASSERT(size <= end - begin); - -    const VMAHandle vma_handle = -        std::find_if(vma_map.begin(), vma_map.end(), [begin, end, size](const auto& vma) { -            if (vma.second.type != VMAType::Free) { -                return false; -            } -            const VAddr vma_base = vma.second.base; -            const VAddr vma_end = vma_base + vma.second.size; -            const VAddr assumed_base = (begin < vma_base) ? vma_base : begin; -            const VAddr used_range = assumed_base + size; - -            return vma_base <= assumed_base && assumed_base < used_range && used_range < end && -                   used_range <= vma_end; -        }); - -    if (vma_handle == vma_map.cend()) { -        // TODO(Subv): Find the correct error code here. -        return RESULT_UNKNOWN; -    } - -    const VAddr target = std::max(begin, vma_handle->second.base); -    return MakeResult<VAddr>(target); -} - -ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, -                                                   MemoryState state, -                                                   Common::MemoryHookPointer mmio_handler) { -    // This is the appropriately sized VMA that will turn into our allocation. -    CASCADE_RESULT(VMAIter vma_handle, CarveVMA(target, size)); -    VirtualMemoryArea& final_vma = vma_handle->second; -    ASSERT(final_vma.size == size); - -    final_vma.type = VMAType::MMIO; -    final_vma.permissions = VMAPermission::ReadWrite; -    final_vma.state = state; -    final_vma.paddr = paddr; -    final_vma.mmio_handler = std::move(mmio_handler); -    UpdatePageTableForVMA(final_vma); - -    return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); -} - -VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { -    VirtualMemoryArea& vma = vma_handle->second; -    vma.type = VMAType::Free; -    vma.permissions = VMAPermission::None; -    vma.state = MemoryState::Unmapped; -    vma.attribute = MemoryAttribute::None; - -    vma.backing_block = nullptr; -    vma.offset = 0; -    vma.backing_memory = nullptr; -    vma.paddr = 0; - -    UpdatePageTableForVMA(vma); - -    return MergeAdjacent(vma_handle); -} - -ResultCode VMManager::UnmapRange(VAddr target, u64 size) { -    CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); -    const VAddr target_end = target + size; - -    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) { -        vma = std::next(Unmap(vma)); -    } - -    ASSERT(FindVMA(target)->second.size >= size); - -    return RESULT_SUCCESS; -} - -VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { -    VMAIter iter = StripIterConstness(vma_handle); - -    VirtualMemoryArea& vma = iter->second; -    vma.permissions = new_perms; -    UpdatePageTableForVMA(vma); - -    return MergeAdjacent(iter); -} - -ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_perms) { -    CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); -    const VAddr target_end = target + size; - -    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) { -        vma = std::next(StripIterConstness(Reprotect(vma, new_perms))); -    } - -    return RESULT_SUCCESS; -} - -ResultVal<VAddr> VMManager::SetHeapSize(u64 size) { -    if (size > GetHeapRegionSize()) { -        return ERR_OUT_OF_MEMORY; -    } - -    // No need to do any additional work if the heap is already the given size. -    if (size == GetCurrentHeapSize()) { -        return MakeResult(heap_region_base); -    } - -    if (heap_memory == nullptr) { -        // Initialize heap -        heap_memory = std::make_shared<PhysicalMemory>(size); -        heap_end = heap_region_base + size; -    } else { -        UnmapRange(heap_region_base, GetCurrentHeapSize()); -    } - -    // If necessary, expand backing vector to cover new heap extents in -    // the case of allocating. Otherwise, shrink the backing memory, -    // if a smaller heap has been requested. -    heap_memory->resize(size); -    heap_memory->shrink_to_fit(); -    RefreshMemoryBlockMappings(heap_memory.get()); - -    heap_end = heap_region_base + size; -    ASSERT(GetCurrentHeapSize() == heap_memory->size()); - -    const auto mapping_result = -        MapMemoryBlock(heap_region_base, heap_memory, 0, size, MemoryState::Heap); -    if (mapping_result.Failed()) { -        return mapping_result.Code(); -    } - -    return MakeResult<VAddr>(heap_region_base); -} - -ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { -    // Check how much memory we've already mapped. -    const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size); -    if (mapped_size_result.Failed()) { -        return mapped_size_result.Code(); -    } - -    // If we've already mapped the desired amount, return early. -    const std::size_t mapped_size = *mapped_size_result; -    if (mapped_size == size) { -        return RESULT_SUCCESS; -    } - -    // Check that we can map the memory we want. -    const auto res_limit = system.CurrentProcess()->GetResourceLimit(); -    const u64 physmem_remaining = res_limit->GetMaxResourceValue(ResourceType::PhysicalMemory) - -                                  res_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory); -    if (physmem_remaining < (size - mapped_size)) { -        return ERR_RESOURCE_LIMIT_EXCEEDED; -    } - -    // Keep track of the memory regions we unmap. -    std::vector<std::pair<u64, u64>> mapped_regions; -    ResultCode result = RESULT_SUCCESS; - -    // Iterate, trying to map memory. -    { -        const auto end_addr = target + size; -        const auto last_addr = end_addr - 1; -        VAddr cur_addr = target; - -        auto iter = FindVMA(target); -        ASSERT(iter != vma_map.end()); - -        while (true) { -            const auto& vma = iter->second; -            const auto vma_start = vma.base; -            const auto vma_end = vma_start + vma.size; -            const auto vma_last = vma_end - 1; - -            // Map the memory block -            const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr); -            if (vma.state == MemoryState::Unmapped) { -                const auto map_res = -                    MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size), 0, -                                   map_size, MemoryState::Heap, VMAPermission::ReadWrite); -                result = map_res.Code(); -                if (result.IsError()) { -                    break; -                } - -                mapped_regions.emplace_back(cur_addr, map_size); -            } - -            // Break once we hit the end of the range. -            if (last_addr <= vma_last) { -                break; -            } - -            // Advance to the next block. -            cur_addr = vma_end; -            iter = FindVMA(cur_addr); -            ASSERT(iter != vma_map.end()); -        } -    } - -    // If we failed, unmap memory. -    if (result.IsError()) { -        for (const auto [unmap_address, unmap_size] : mapped_regions) { -            ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(), -                       "Failed to unmap memory range."); -        } - -        return result; -    } - -    // Update amount of mapped physical memory. -    physical_memory_mapped += size - mapped_size; - -    return RESULT_SUCCESS; -} - -ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { -    // Check how much memory is currently mapped. -    const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size); -    if (mapped_size_result.Failed()) { -        return mapped_size_result.Code(); -    } - -    // If we've already unmapped all the memory, return early. -    const std::size_t mapped_size = *mapped_size_result; -    if (mapped_size == 0) { -        return RESULT_SUCCESS; -    } - -    // Keep track of the memory regions we unmap. -    std::vector<std::pair<u64, u64>> unmapped_regions; -    ResultCode result = RESULT_SUCCESS; - -    // Try to unmap regions. -    { -        const auto end_addr = target + size; -        const auto last_addr = end_addr - 1; -        VAddr cur_addr = target; - -        auto iter = FindVMA(target); -        ASSERT(iter != vma_map.end()); - -        while (true) { -            const auto& vma = iter->second; -            const auto vma_start = vma.base; -            const auto vma_end = vma_start + vma.size; -            const auto vma_last = vma_end - 1; - -            // Unmap the memory block -            const auto unmap_size = std::min(end_addr - cur_addr, vma_end - cur_addr); -            if (vma.state == MemoryState::Heap) { -                result = UnmapRange(cur_addr, unmap_size); -                if (result.IsError()) { -                    break; -                } - -                unmapped_regions.emplace_back(cur_addr, unmap_size); -            } - -            // Break once we hit the end of the range. -            if (last_addr <= vma_last) { -                break; -            } - -            // Advance to the next block. -            cur_addr = vma_end; -            iter = FindVMA(cur_addr); -            ASSERT(iter != vma_map.end()); -        } -    } - -    // If we failed, re-map regions. -    // TODO: Preserve memory contents? -    if (result.IsError()) { -        for (const auto [map_address, map_size] : unmapped_regions) { -            const auto remap_res = -                MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size), 0, map_size, -                               MemoryState::Heap, VMAPermission::None); -            ASSERT_MSG(remap_res.Succeeded(), "Failed to remap a memory block."); -        } - -        return result; -    } - -    // Update mapped amount -    physical_memory_mapped -= mapped_size; - -    return RESULT_SUCCESS; -} - -ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { -    constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; -    const auto src_check_result = CheckRangeState( -        src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::All, -        VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); - -    if (src_check_result.Failed()) { -        return src_check_result.Code(); -    } - -    const auto mirror_result = -        MirrorMemory(dst_address, src_address, size, MemoryState::ModuleCode); -    if (mirror_result.IsError()) { -        return mirror_result; -    } - -    // Ensure we lock the source memory region. -    const auto src_vma_result = CarveVMARange(src_address, size); -    if (src_vma_result.Failed()) { -        return src_vma_result.Code(); -    } -    auto src_vma_iter = *src_vma_result; -    src_vma_iter->second.attribute = MemoryAttribute::Locked; -    Reprotect(src_vma_iter, VMAPermission::Read); - -    // The destination memory region is fine as is, however we need to make it read-only. -    return ReprotectRange(dst_address, size, VMAPermission::Read); -} - -ResultCode VMManager::UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) { -    constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped; -    const auto src_check_result = CheckRangeState( -        src_address, size, MemoryState::All, MemoryState::Heap, VMAPermission::None, -        VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, ignore_attribute); - -    if (src_check_result.Failed()) { -        return src_check_result.Code(); -    } - -    // Yes, the kernel only checks the first page of the region. -    const auto dst_check_result = -        CheckRangeState(dst_address, Memory::PAGE_SIZE, MemoryState::FlagModule, -                        MemoryState::FlagModule, VMAPermission::None, VMAPermission::None, -                        MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); - -    if (dst_check_result.Failed()) { -        return dst_check_result.Code(); -    } - -    const auto dst_memory_state = std::get<MemoryState>(*dst_check_result); -    const auto dst_contiguous_check_result = CheckRangeState( -        dst_address, size, MemoryState::All, dst_memory_state, VMAPermission::None, -        VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::None, ignore_attribute); - -    if (dst_contiguous_check_result.Failed()) { -        return dst_contiguous_check_result.Code(); -    } - -    const auto unmap_result = UnmapRange(dst_address, size); -    if (unmap_result.IsError()) { -        return unmap_result; -    } - -    // With the mirrored portion unmapped, restore the original region's traits. -    const auto src_vma_result = CarveVMARange(src_address, size); -    if (src_vma_result.Failed()) { -        return src_vma_result.Code(); -    } -    auto src_vma_iter = *src_vma_result; -    src_vma_iter->second.state = MemoryState::Heap; -    src_vma_iter->second.attribute = MemoryAttribute::None; -    Reprotect(src_vma_iter, VMAPermission::ReadWrite); - -    if (dst_memory_state == MemoryState::ModuleCode) { -        system.InvalidateCpuInstructionCaches(); -    } - -    return unmap_result; -} - -MemoryInfo VMManager::QueryMemory(VAddr address) const { -    const auto vma = FindVMA(address); -    MemoryInfo memory_info{}; - -    if (IsValidHandle(vma)) { -        memory_info.base_address = vma->second.base; -        memory_info.attributes = ToSvcMemoryAttribute(vma->second.attribute); -        memory_info.permission = static_cast<u32>(vma->second.permissions); -        memory_info.size = vma->second.size; -        memory_info.state = ToSvcMemoryState(vma->second.state); -    } else { -        memory_info.base_address = address_space_end; -        memory_info.permission = static_cast<u32>(VMAPermission::None); -        memory_info.size = 0 - address_space_end; -        memory_info.state = static_cast<u32>(MemoryState::Inaccessible); -    } - -    return memory_info; -} - -ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, -                                         MemoryAttribute attribute) { -    constexpr auto ignore_mask = -        MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked; -    constexpr auto attribute_mask = ~ignore_mask; - -    const auto result = CheckRangeState( -        address, size, MemoryState::FlagUncached, MemoryState::FlagUncached, VMAPermission::None, -        VMAPermission::None, attribute_mask, MemoryAttribute::None, ignore_mask); - -    if (result.Failed()) { -        return result.Code(); -    } - -    const auto [prev_state, prev_permissions, prev_attributes] = *result; -    const auto new_attribute = (prev_attributes & ~mask) | (mask & attribute); - -    const auto carve_result = CarveVMARange(address, size); -    if (carve_result.Failed()) { -        return carve_result.Code(); -    } - -    auto vma_iter = *carve_result; -    vma_iter->second.attribute = new_attribute; - -    MergeAdjacent(vma_iter); -    return RESULT_SUCCESS; -} - -ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { -    const auto vma = FindVMA(src_addr); - -    ASSERT_MSG(vma != vma_map.end(), "Invalid memory address"); -    ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); - -    // The returned VMA might be a bigger one encompassing the desired address. -    const auto vma_offset = src_addr - vma->first; -    ASSERT_MSG(vma_offset + size <= vma->second.size, -               "Shared memory exceeds bounds of mapped block"); - -    const std::shared_ptr<PhysicalMemory>& backing_block = vma->second.backing_block; -    const std::size_t backing_block_offset = vma->second.offset + vma_offset; - -    CASCADE_RESULT(auto new_vma, -                   MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state)); -    // Protect mirror with permissions from old region -    Reprotect(new_vma, vma->second.permissions); -    // Remove permissions from old region -    ReprotectRange(src_addr, size, VMAPermission::None); - -    return RESULT_SUCCESS; -} - -void VMManager::RefreshMemoryBlockMappings(const PhysicalMemory* block) { -    // If this ever proves to have a noticeable performance impact, allow users of the function to -    // specify a specific range of addresses to limit the scan to. -    for (const auto& p : vma_map) { -        const VirtualMemoryArea& vma = p.second; -        if (block == vma.backing_block.get()) { -            UpdatePageTableForVMA(vma); -        } -    } -} - -void VMManager::LogLayout() const { -    for (const auto& p : vma_map) { -        const VirtualMemoryArea& vma = p.second; -        LOG_DEBUG(Kernel, "{:016X} - {:016X} size: {:016X} {}{}{} {}", vma.base, -                  vma.base + vma.size, vma.size, -                  (u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-', -                  (u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-', -                  (u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-', -                  GetMemoryStateName(vma.state)); -    } -} - -VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle& iter) { -    // This uses a neat C++ trick to convert a const_iterator to a regular iterator, given -    // non-const access to its container. -    return vma_map.erase(iter, iter); // Erases an empty range of elements -} - -ResultVal<VMManager::VMAIter> VMManager::CarveVMA(VAddr base, u64 size) { -    ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x{:016X}", size); -    ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x{:016X}", base); - -    VMAIter vma_handle = StripIterConstness(FindVMA(base)); -    if (vma_handle == vma_map.end()) { -        // Target address is outside the range managed by the kernel -        return ERR_INVALID_ADDRESS; -    } - -    const VirtualMemoryArea& vma = vma_handle->second; -    if (vma.type != VMAType::Free) { -        // Region is already allocated -        return ERR_INVALID_ADDRESS_STATE; -    } - -    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 -        return ERR_INVALID_ADDRESS_STATE; -    } - -    if (end_in_vma != vma.size) { -        // Split VMA at the end of the allocated region -        SplitVMA(vma_handle, end_in_vma); -    } -    if (start_in_vma != 0) { -        // Split VMA at the start of the allocated region -        vma_handle = SplitVMA(vma_handle, start_in_vma); -    } - -    return MakeResult<VMAIter>(vma_handle); -} - -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); - -    const VAddr target_end = target + size; -    ASSERT(target_end >= target); -    ASSERT(target_end <= address_space_end); -    ASSERT(size > 0); - -    VMAIter begin_vma = StripIterConstness(FindVMA(target)); -    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) { -        begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base); -    } - -    VMAIter end_vma = StripIterConstness(FindVMA(target_end)); -    if (end_vma != vma_map.end() && target_end != end_vma->second.base) { -        end_vma = SplitVMA(end_vma, target_end - end_vma->second.base); -    } - -    return MakeResult<VMAIter>(begin_vma); -} - -VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) { -    VirtualMemoryArea& old_vma = vma_handle->second; -    VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA - -    // For now, don't allow no-op VMA splits (trying to split at a boundary) because it's probably -    // a bug. This restriction might be removed later. -    ASSERT(offset_in_vma < old_vma.size); -    ASSERT(offset_in_vma > 0); - -    old_vma.size = offset_in_vma; -    new_vma.base += offset_in_vma; -    new_vma.size -= offset_in_vma; - -    switch (new_vma.type) { -    case VMAType::Free: -        break; -    case VMAType::AllocatedMemoryBlock: -        new_vma.offset += offset_in_vma; -        break; -    case VMAType::BackingMemory: -        new_vma.backing_memory += offset_in_vma; -        break; -    case VMAType::MMIO: -        new_vma.paddr += offset_in_vma; -        break; -    } - -    ASSERT(old_vma.CanBeMergedWith(new_vma)); - -    return vma_map.emplace_hint(std::next(vma_handle), new_vma.base, new_vma); -} - -VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { -    const VMAIter next_vma = std::next(iter); -    if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) { -        MergeAdjacentVMA(iter->second, next_vma->second); -        vma_map.erase(next_vma); -    } - -    if (iter != vma_map.begin()) { -        VMAIter prev_vma = std::prev(iter); -        if (prev_vma->second.CanBeMergedWith(iter->second)) { -            MergeAdjacentVMA(prev_vma->second, iter->second); -            vma_map.erase(iter); -            iter = prev_vma; -        } -    } - -    return iter; -} - -void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right) { -    ASSERT(left.CanBeMergedWith(right)); - -    // Always merge allocated memory blocks, even when they don't share the same backing block. -    if (left.type == VMAType::AllocatedMemoryBlock && -        (left.backing_block != right.backing_block || left.offset + left.size != right.offset)) { - -        // Check if we can save work. -        if (left.offset == 0 && left.size == left.backing_block->size()) { -            // Fast case: left is an entire backing block. -            left.backing_block->resize(left.size + right.size); -            std::memcpy(left.backing_block->data() + left.size, -                        right.backing_block->data() + right.offset, right.size); -        } else { -            // Slow case: make a new memory block for left and right. -            auto new_memory = std::make_shared<PhysicalMemory>(); -            new_memory->resize(left.size + right.size); -            std::memcpy(new_memory->data(), left.backing_block->data() + left.offset, left.size); -            std::memcpy(new_memory->data() + left.size, right.backing_block->data() + right.offset, -                        right.size); - -            left.backing_block = std::move(new_memory); -            left.offset = 0; -        } - -        // Page table update is needed, because backing memory changed. -        left.size += right.size; -        UpdatePageTableForVMA(left); -    } else { -        // Just update the size. -        left.size += right.size; -    } -} - -void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { -    auto& memory = system.Memory(); - -    switch (vma.type) { -    case VMAType::Free: -        memory.UnmapRegion(page_table, vma.base, vma.size); -        break; -    case VMAType::AllocatedMemoryBlock: -        memory.MapMemoryRegion(page_table, vma.base, vma.size, *vma.backing_block, vma.offset); -        break; -    case VMAType::BackingMemory: -        memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); -        break; -    case VMAType::MMIO: -        memory.MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); -        break; -    } -} - -void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) { -    u64 map_region_size = 0; -    u64 heap_region_size = 0; -    u64 stack_region_size = 0; -    u64 tls_io_region_size = 0; - -    u64 stack_and_tls_io_end = 0; - -    switch (type) { -    case FileSys::ProgramAddressSpaceType::Is32Bit: -    case FileSys::ProgramAddressSpaceType::Is32BitNoMap: -        address_space_width = 32; -        code_region_base = 0x200000; -        code_region_end = code_region_base + 0x3FE00000; -        aslr_region_base = 0x200000; -        aslr_region_end = aslr_region_base + 0xFFE00000; -        if (type == FileSys::ProgramAddressSpaceType::Is32Bit) { -            map_region_size = 0x40000000; -            heap_region_size = 0x40000000; -        } else { -            map_region_size = 0; -            heap_region_size = 0x80000000; -        } -        stack_and_tls_io_end = 0x40000000; -        break; -    case FileSys::ProgramAddressSpaceType::Is36Bit: -        address_space_width = 36; -        code_region_base = 0x8000000; -        code_region_end = code_region_base + 0x78000000; -        aslr_region_base = 0x8000000; -        aslr_region_end = aslr_region_base + 0xFF8000000; -        map_region_size = 0x180000000; -        heap_region_size = 0x180000000; -        stack_and_tls_io_end = 0x80000000; -        break; -    case FileSys::ProgramAddressSpaceType::Is39Bit: -        address_space_width = 39; -        code_region_base = 0x8000000; -        code_region_end = code_region_base + 0x80000000; -        aslr_region_base = 0x8000000; -        aslr_region_end = aslr_region_base + 0x7FF8000000; -        map_region_size = 0x1000000000; -        heap_region_size = 0x180000000; -        stack_region_size = 0x80000000; -        tls_io_region_size = 0x1000000000; -        break; -    default: -        UNREACHABLE_MSG("Invalid address space type specified: {}", static_cast<u32>(type)); -        return; -    } - -    const u64 stack_and_tls_io_begin = aslr_region_base; - -    address_space_base = 0; -    address_space_end = 1ULL << address_space_width; - -    map_region_base = code_region_end; -    map_region_end = map_region_base + map_region_size; - -    heap_region_base = map_region_end; -    heap_region_end = heap_region_base + heap_region_size; -    heap_end = heap_region_base; - -    stack_region_base = heap_region_end; -    stack_region_end = stack_region_base + stack_region_size; - -    tls_io_region_base = stack_region_end; -    tls_io_region_end = tls_io_region_base + tls_io_region_size; - -    if (stack_region_size == 0) { -        stack_region_base = stack_and_tls_io_begin; -        stack_region_end = stack_and_tls_io_end; -    } - -    if (tls_io_region_size == 0) { -        tls_io_region_base = stack_and_tls_io_begin; -        tls_io_region_end = stack_and_tls_io_end; -    } -} - -void VMManager::Clear() { -    ClearVMAMap(); -    ClearPageTable(); -} - -void VMManager::ClearVMAMap() { -    vma_map.clear(); -} - -void VMManager::ClearPageTable() { -    std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); -    page_table.special_regions.clear(); -    std::fill(page_table.attributes.begin(), page_table.attributes.end(), -              Common::PageType::Unmapped); -} - -VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask, -                                                   MemoryState state, VMAPermission permission_mask, -                                                   VMAPermission permissions, -                                                   MemoryAttribute attribute_mask, -                                                   MemoryAttribute attribute, -                                                   MemoryAttribute ignore_mask) const { -    auto iter = FindVMA(address); - -    // If we don't have a valid VMA handle at this point, then it means this is -    // being called with an address outside of the address space, which is definitely -    // indicative of a bug, as this function only operates on mapped memory regions. -    DEBUG_ASSERT(IsValidHandle(iter)); - -    const VAddr end_address = address + size - 1; -    const MemoryAttribute initial_attributes = iter->second.attribute; -    const VMAPermission initial_permissions = iter->second.permissions; -    const MemoryState initial_state = iter->second.state; - -    while (true) { -        // The iterator should be valid throughout the traversal. Hitting the end of -        // the mapped VMA regions is unquestionably indicative of a bug. -        DEBUG_ASSERT(IsValidHandle(iter)); - -        const auto& vma = iter->second; - -        if (vma.state != initial_state) { -            return ERR_INVALID_ADDRESS_STATE; -        } - -        if ((vma.state & state_mask) != state) { -            return ERR_INVALID_ADDRESS_STATE; -        } - -        if (vma.permissions != initial_permissions) { -            return ERR_INVALID_ADDRESS_STATE; -        } - -        if ((vma.permissions & permission_mask) != permissions) { -            return ERR_INVALID_ADDRESS_STATE; -        } - -        if ((vma.attribute | ignore_mask) != (initial_attributes | ignore_mask)) { -            return ERR_INVALID_ADDRESS_STATE; -        } - -        if ((vma.attribute & attribute_mask) != attribute) { -            return ERR_INVALID_ADDRESS_STATE; -        } - -        if (end_address <= vma.EndAddress()) { -            break; -        } - -        ++iter; -    } - -    return MakeResult( -        std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask)); -} - -ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address, -                                                             std::size_t size) const { -    const VAddr end_addr = address + size; -    const VAddr last_addr = end_addr - 1; -    std::size_t mapped_size = 0; - -    VAddr cur_addr = address; -    auto iter = FindVMA(cur_addr); -    ASSERT(iter != vma_map.end()); - -    while (true) { -        const auto& vma = iter->second; -        const VAddr vma_start = vma.base; -        const VAddr vma_end = vma_start + vma.size; -        const VAddr vma_last = vma_end - 1; - -        // Add size if relevant. -        if (vma.state != MemoryState::Unmapped) { -            mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr); -        } - -        // Break once we hit the end of the range. -        if (last_addr <= vma_last) { -            break; -        } - -        // Advance to the next block. -        cur_addr = vma_end; -        iter = std::next(iter); -        ASSERT(iter != vma_map.end()); -    } - -    return MakeResult(mapped_size); -} - -ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr address, -                                                                        std::size_t size) const { -    const VAddr end_addr = address + size; -    const VAddr last_addr = end_addr - 1; -    std::size_t mapped_size = 0; - -    VAddr cur_addr = address; -    auto iter = FindVMA(cur_addr); -    ASSERT(iter != vma_map.end()); - -    while (true) { -        const auto& vma = iter->second; -        const auto vma_start = vma.base; -        const auto vma_end = vma_start + vma.size; -        const auto vma_last = vma_end - 1; -        const auto state = vma.state; -        const auto attr = vma.attribute; - -        // Memory within region must be free or mapped heap. -        if (!((state == MemoryState::Heap && attr == MemoryAttribute::None) || -              (state == MemoryState::Unmapped))) { -            return ERR_INVALID_ADDRESS_STATE; -        } - -        // Add size if relevant. -        if (state != MemoryState::Unmapped) { -            mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr); -        } - -        // Break once we hit the end of the range. -        if (last_addr <= vma_last) { -            break; -        } - -        // Advance to the next block. -        cur_addr = vma_end; -        iter = std::next(iter); -        ASSERT(iter != vma_map.end()); -    } - -    return MakeResult(mapped_size); -} - -u64 VMManager::GetTotalPhysicalMemoryAvailable() const { -    LOG_WARNING(Kernel, "(STUBBED) called"); -    return 0xF8000000; -} - -VAddr VMManager::GetAddressSpaceBaseAddress() const { -    return address_space_base; -} - -VAddr VMManager::GetAddressSpaceEndAddress() const { -    return address_space_end; -} - -u64 VMManager::GetAddressSpaceSize() const { -    return address_space_end - address_space_base; -} - -u64 VMManager::GetAddressSpaceWidth() const { -    return address_space_width; -} - -bool VMManager::IsWithinAddressSpace(VAddr address, u64 size) const { -    return IsInsideAddressRange(address, size, GetAddressSpaceBaseAddress(), -                                GetAddressSpaceEndAddress()); -} - -VAddr VMManager::GetASLRRegionBaseAddress() const { -    return aslr_region_base; -} - -VAddr VMManager::GetASLRRegionEndAddress() const { -    return aslr_region_end; -} - -u64 VMManager::GetASLRRegionSize() const { -    return aslr_region_end - aslr_region_base; -} - -bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const { -    const VAddr range_end = begin + size; -    const VAddr aslr_start = GetASLRRegionBaseAddress(); -    const VAddr aslr_end = GetASLRRegionEndAddress(); - -    if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) { -        return false; -    } - -    if (range_end > heap_region_base && heap_region_end > begin) { -        return false; -    } - -    if (range_end > map_region_base && map_region_end > begin) { -        return false; -    } - -    return true; -} - -VAddr VMManager::GetCodeRegionBaseAddress() const { -    return code_region_base; -} - -VAddr VMManager::GetCodeRegionEndAddress() const { -    return code_region_end; -} - -u64 VMManager::GetCodeRegionSize() const { -    return code_region_end - code_region_base; -} - -bool VMManager::IsWithinCodeRegion(VAddr address, u64 size) const { -    return IsInsideAddressRange(address, size, GetCodeRegionBaseAddress(), -                                GetCodeRegionEndAddress()); -} - -VAddr VMManager::GetHeapRegionBaseAddress() const { -    return heap_region_base; -} - -VAddr VMManager::GetHeapRegionEndAddress() const { -    return heap_region_end; -} - -u64 VMManager::GetHeapRegionSize() const { -    return heap_region_end - heap_region_base; -} - -u64 VMManager::GetCurrentHeapSize() const { -    return heap_end - heap_region_base; -} - -bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const { -    return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(), -                                GetHeapRegionEndAddress()); -} - -VAddr VMManager::GetMapRegionBaseAddress() const { -    return map_region_base; -} - -VAddr VMManager::GetMapRegionEndAddress() const { -    return map_region_end; -} - -u64 VMManager::GetMapRegionSize() const { -    return map_region_end - map_region_base; -} - -bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const { -    return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress()); -} - -VAddr VMManager::GetStackRegionBaseAddress() const { -    return stack_region_base; -} - -VAddr VMManager::GetStackRegionEndAddress() const { -    return stack_region_end; -} - -u64 VMManager::GetStackRegionSize() const { -    return stack_region_end - stack_region_base; -} - -bool VMManager::IsWithinStackRegion(VAddr address, u64 size) const { -    return IsInsideAddressRange(address, size, GetStackRegionBaseAddress(), -                                GetStackRegionEndAddress()); -} - -VAddr VMManager::GetTLSIORegionBaseAddress() const { -    return tls_io_region_base; -} - -VAddr VMManager::GetTLSIORegionEndAddress() const { -    return tls_io_region_end; -} - -u64 VMManager::GetTLSIORegionSize() const { -    return tls_io_region_end - tls_io_region_base; -} - -bool VMManager::IsWithinTLSIORegion(VAddr address, u64 size) const { -    return IsInsideAddressRange(address, size, GetTLSIORegionBaseAddress(), -                                GetTLSIORegionEndAddress()); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h deleted file mode 100644 index 90b4b006a..000000000 --- a/src/core/hle/kernel/vm_manager.h +++ /dev/null @@ -1,796 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <map> -#include <memory> -#include <tuple> -#include <vector> -#include "common/common_types.h" -#include "common/memory_hook.h" -#include "common/page_table.h" -#include "core/hle/kernel/physical_memory.h" -#include "core/hle/result.h" -#include "core/memory.h" - -namespace Core { -class System; -} - -namespace FileSys { -enum class ProgramAddressSpaceType : u8; -} - -namespace Kernel { - -enum class VMAType : u8 { -    /// VMA represents an unmapped region of the address space. -    Free, -    /// VMA is backed by a ref-counted allocate memory block. -    AllocatedMemoryBlock, -    /// VMA is backed by a raw, unmanaged pointer. -    BackingMemory, -    /// VMA is mapped to MMIO registers at a fixed PAddr. -    MMIO, -    // TODO(yuriks): Implement MemoryAlias to support MAP/UNMAP -}; - -/// Permissions for mapped memory blocks -enum class VMAPermission : u8 { -    None = 0, -    Read = 1, -    Write = 2, -    Execute = 4, - -    ReadWrite = Read | Write, -    ReadExecute = Read | Execute, -    WriteExecute = Write | Execute, -    ReadWriteExecute = Read | Write | Execute, - -    // Used as a wildcard when checking permissions across memory ranges -    All = 0xFF, -}; - -constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) { -    return static_cast<VMAPermission>(u32(lhs) | u32(rhs)); -} - -constexpr VMAPermission operator&(VMAPermission lhs, VMAPermission rhs) { -    return static_cast<VMAPermission>(u32(lhs) & u32(rhs)); -} - -constexpr VMAPermission operator^(VMAPermission lhs, VMAPermission rhs) { -    return static_cast<VMAPermission>(u32(lhs) ^ u32(rhs)); -} - -constexpr VMAPermission operator~(VMAPermission permission) { -    return static_cast<VMAPermission>(~u32(permission)); -} - -constexpr VMAPermission& operator|=(VMAPermission& lhs, VMAPermission rhs) { -    lhs = lhs | rhs; -    return lhs; -} - -constexpr VMAPermission& operator&=(VMAPermission& lhs, VMAPermission rhs) { -    lhs = lhs & rhs; -    return lhs; -} - -constexpr VMAPermission& operator^=(VMAPermission& lhs, VMAPermission rhs) { -    lhs = lhs ^ rhs; -    return lhs; -} - -/// Attribute flags that can be applied to a VMA -enum class MemoryAttribute : u32 { -    Mask = 0xFF, - -    /// No particular qualities -    None = 0, -    /// Memory locked/borrowed for use. e.g. This would be used by transfer memory. -    Locked = 1, -    /// Memory locked for use by IPC-related internals. -    LockedForIPC = 2, -    /// Mapped as part of the device address space. -    DeviceMapped = 4, -    /// Uncached memory -    Uncached = 8, - -    IpcAndDeviceMapped = LockedForIPC | DeviceMapped, -}; - -constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { -    return static_cast<MemoryAttribute>(u32(lhs) | u32(rhs)); -} - -constexpr MemoryAttribute operator&(MemoryAttribute lhs, MemoryAttribute rhs) { -    return static_cast<MemoryAttribute>(u32(lhs) & u32(rhs)); -} - -constexpr MemoryAttribute operator^(MemoryAttribute lhs, MemoryAttribute rhs) { -    return static_cast<MemoryAttribute>(u32(lhs) ^ u32(rhs)); -} - -constexpr MemoryAttribute operator~(MemoryAttribute attribute) { -    return static_cast<MemoryAttribute>(~u32(attribute)); -} - -constexpr MemoryAttribute& operator|=(MemoryAttribute& lhs, MemoryAttribute rhs) { -    lhs = lhs | rhs; -    return lhs; -} - -constexpr MemoryAttribute& operator&=(MemoryAttribute& lhs, MemoryAttribute rhs) { -    lhs = lhs & rhs; -    return lhs; -} - -constexpr MemoryAttribute& operator^=(MemoryAttribute& lhs, MemoryAttribute rhs) { -    lhs = lhs ^ rhs; -    return lhs; -} - -constexpr u32 ToSvcMemoryAttribute(MemoryAttribute attribute) { -    return static_cast<u32>(attribute & MemoryAttribute::Mask); -} - -// clang-format off -/// Represents memory states and any relevant flags, as used by the kernel. -/// svcQueryMemory interprets these by masking away all but the first eight -/// bits when storing memory state into a MemoryInfo instance. -enum class MemoryState : u32 { -    Mask                            = 0xFF, -    FlagProtect                     = 1U << 8, -    FlagDebug                       = 1U << 9, -    FlagIPC0                        = 1U << 10, -    FlagIPC3                        = 1U << 11, -    FlagIPC1                        = 1U << 12, -    FlagMapped                      = 1U << 13, -    FlagCode                        = 1U << 14, -    FlagAlias                       = 1U << 15, -    FlagModule                      = 1U << 16, -    FlagTransfer                    = 1U << 17, -    FlagQueryPhysicalAddressAllowed = 1U << 18, -    FlagSharedDevice                = 1U << 19, -    FlagSharedDeviceAligned         = 1U << 20, -    FlagIPCBuffer                   = 1U << 21, -    FlagMemoryPoolAllocated         = 1U << 22, -    FlagMapProcess                  = 1U << 23, -    FlagUncached                    = 1U << 24, -    FlagCodeMemory                  = 1U << 25, - -    // Wildcard used in range checking to indicate all states. -    All                             = 0xFFFFFFFF, - -    // Convenience flag sets to reduce repetition -    IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, - -    CodeFlags = FlagDebug | IPCFlags | FlagMapped | FlagCode | FlagQueryPhysicalAddressAllowed | -                FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, - -    DataFlags = FlagProtect | IPCFlags | FlagMapped | FlagAlias | FlagTransfer | -                FlagQueryPhysicalAddressAllowed | FlagSharedDevice | FlagSharedDeviceAligned | -                FlagMemoryPoolAllocated | FlagIPCBuffer | FlagUncached, - -    Unmapped               = 0x00, -    Io                     = 0x01 | FlagMapped, -    Normal                 = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, -    Code                   = 0x03 | CodeFlags  | FlagMapProcess, -    CodeData               = 0x04 | DataFlags  | FlagMapProcess | FlagCodeMemory, -    Heap                   = 0x05 | DataFlags  | FlagCodeMemory, -    Shared                 = 0x06 | FlagMapped | FlagMemoryPoolAllocated, -    ModuleCode             = 0x08 | CodeFlags  | FlagModule | FlagMapProcess, -    ModuleCodeData         = 0x09 | DataFlags  | FlagModule | FlagMapProcess | FlagCodeMemory, - -    IpcBuffer0             = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | -                                    IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, - -    Stack                  = 0x0B | FlagMapped | IPCFlags | FlagQueryPhysicalAddressAllowed | -                                    FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, - -    ThreadLocal            = 0x0C | FlagMapped | FlagMemoryPoolAllocated, - -    TransferMemoryIsolated = 0x0D | IPCFlags | FlagMapped | FlagQueryPhysicalAddressAllowed | -                                    FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated | -                                    FlagUncached, - -    TransferMemory         = 0x0E | FlagIPC3   | FlagIPC1   | FlagMapped | FlagQueryPhysicalAddressAllowed | -                                    FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, - -    ProcessMemory          = 0x0F | FlagIPC3   | FlagIPC1   | FlagMapped | FlagMemoryPoolAllocated, - -    // Used to signify an inaccessible or invalid memory region with memory queries -    Inaccessible           = 0x10, - -    IpcBuffer1             = 0x11 | FlagIPC3   | FlagIPC1   | FlagMapped | FlagQueryPhysicalAddressAllowed | -                                    FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, - -    IpcBuffer3             = 0x12 | FlagIPC3   | FlagMapped | FlagQueryPhysicalAddressAllowed | -                                    FlagSharedDeviceAligned | FlagMemoryPoolAllocated, - -    KernelStack            = 0x13 | FlagMapped, -}; -// clang-format on - -constexpr MemoryState operator|(MemoryState lhs, MemoryState rhs) { -    return static_cast<MemoryState>(u32(lhs) | u32(rhs)); -} - -constexpr MemoryState operator&(MemoryState lhs, MemoryState rhs) { -    return static_cast<MemoryState>(u32(lhs) & u32(rhs)); -} - -constexpr MemoryState operator^(MemoryState lhs, MemoryState rhs) { -    return static_cast<MemoryState>(u32(lhs) ^ u32(rhs)); -} - -constexpr MemoryState operator~(MemoryState lhs) { -    return static_cast<MemoryState>(~u32(lhs)); -} - -constexpr MemoryState& operator|=(MemoryState& lhs, MemoryState rhs) { -    lhs = lhs | rhs; -    return lhs; -} - -constexpr MemoryState& operator&=(MemoryState& lhs, MemoryState rhs) { -    lhs = lhs & rhs; -    return lhs; -} - -constexpr MemoryState& operator^=(MemoryState& lhs, MemoryState rhs) { -    lhs = lhs ^ rhs; -    return lhs; -} - -constexpr u32 ToSvcMemoryState(MemoryState state) { -    return static_cast<u32>(state & MemoryState::Mask); -} - -struct MemoryInfo { -    u64 base_address; -    u64 size; -    u32 state; -    u32 attributes; -    u32 permission; -    u32 ipc_ref_count; -    u32 device_ref_count; -}; -static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size."); - -struct PageInfo { -    u32 flags; -}; - -/** - * Represents a VMA in an address space. A VMA is a contiguous region of virtual addressing space - * with homogeneous attributes across its extents. In this particular implementation each VMA is - * also backed by a single host memory allocation. - */ -struct VirtualMemoryArea { -    /// Gets the starting (base) address of this VMA. -    VAddr StartAddress() const { -        return base; -    } - -    /// Gets the ending address of this VMA. -    VAddr EndAddress() const { -        return base + size - 1; -    } - -    /// Virtual base address of the region. -    VAddr base = 0; -    /// Size of the region. -    u64 size = 0; - -    VMAType type = VMAType::Free; -    VMAPermission permissions = VMAPermission::None; -    MemoryState state = MemoryState::Unmapped; -    MemoryAttribute attribute = MemoryAttribute::None; - -    // Settings for type = AllocatedMemoryBlock -    /// Memory block backing this VMA. -    std::shared_ptr<PhysicalMemory> backing_block = nullptr; -    /// Offset into the backing_memory the mapping starts from. -    std::size_t offset = 0; - -    // Settings for type = BackingMemory -    /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. -    u8* backing_memory = nullptr; - -    // Settings for type = MMIO -    /// Physical address of the register area this VMA maps to. -    PAddr paddr = 0; -    Common::MemoryHookPointer mmio_handler = nullptr; - -    /// Tests if this area can be merged to the right with `next`. -    bool CanBeMergedWith(const VirtualMemoryArea& next) const; -}; - -/** - * Manages a process' virtual addressing space. This class maintains a list of allocated and free - * regions in the address space, along with their attributes, and allows kernel clients to - * manipulate it, adjusting the page table to match. - * - * This is similar in idea and purpose to the VM manager present in operating system kernels, with - * the main difference being that it doesn't have to support swapping or memory mapping of files. - * The implementation is also simplified by not having to allocate page frames. See these articles - * about the Linux kernel for an explantion of the concept and implementation: - *  - http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/ - *  - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ - */ -class VMManager final { -    using VMAMap = std::map<VAddr, VirtualMemoryArea>; - -public: -    using VMAHandle = VMAMap::const_iterator; - -    explicit VMManager(Core::System& system); -    ~VMManager(); - -    /// Clears the address space map, re-initializing with a single free area. -    void Reset(FileSys::ProgramAddressSpaceType type); - -    /// Finds the VMA in which the given address is included in, or `vma_map.end()`. -    VMAHandle FindVMA(VAddr target) const; - -    /// Indicates whether or not the given handle is within the VMA map. -    bool IsValidHandle(VMAHandle handle) const; - -    // TODO(yuriks): Should these functions actually return the handle? - -    /** -     * Maps part of a ref-counted block of memory at a given address. -     * -     * @param target The guest address to start the mapping at. -     * @param block The block to be mapped. -     * @param offset Offset into `block` to map from. -     * @param size Size of the mapping. -     * @param state MemoryState tag to attach to the VMA. -     */ -    ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<PhysicalMemory> block, -                                        std::size_t offset, u64 size, MemoryState state, -                                        VMAPermission perm = VMAPermission::ReadWrite); - -    /** -     * Maps an unmanaged host memory pointer at a given address. -     * -     * @param target The guest address to start the mapping at. -     * @param memory The memory to be mapped. -     * @param size Size of the mapping. -     * @param state MemoryState tag to attach to the VMA. -     */ -    ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); - -    /** -     * Finds the first free memory region of the given size within -     * the user-addressable ASLR memory region. -     * -     * @param size The size of the desired region in bytes. -     * -     * @returns If successful, the base address of the free region with -     *          the given size. -     */ -    ResultVal<VAddr> FindFreeRegion(u64 size) const; - -    /** -     * Finds the first free address range that can hold a region of the desired size -     * -     * @param begin The starting address of the range. -     *              This is treated as an inclusive beginning address. -     * -     * @param end   The ending address of the range. -     *              This is treated as an exclusive ending address. -     * -     * @param size  The size of the free region to attempt to locate, -     *              in bytes. -     * -     * @returns If successful, the base address of the free region with -     *          the given size. -     * -     * @returns If unsuccessful, a result containing an error code. -     * -     * @pre The starting address must be less than the ending address. -     * @pre The size must not exceed the address range itself. -     */ -    ResultVal<VAddr> FindFreeRegion(VAddr begin, VAddr end, u64 size) const; - -    /** -     * Maps a memory-mapped IO region at a given address. -     * -     * @param target The guest address to start the mapping at. -     * @param paddr The physical address where the registers are present. -     * @param size Size of the mapping. -     * @param state MemoryState tag to attach to the VMA. -     * @param mmio_handler The handler that will implement read and write for this MMIO region. -     */ -    ResultVal<VMAHandle> MapMMIO(VAddr target, PAddr paddr, u64 size, MemoryState state, -                                 Common::MemoryHookPointer mmio_handler); - -    /// Unmaps a range of addresses, splitting VMAs as necessary. -    ResultCode UnmapRange(VAddr target, u64 size); - -    /// Changes the permissions of the given VMA. -    VMAHandle Reprotect(VMAHandle vma, VMAPermission new_perms); - -    /// Changes the permissions of a range of addresses, splitting VMAs as necessary. -    ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms); - -    ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); - -    /// Attempts to allocate a heap with the given size. -    /// -    /// @param size The size of the heap to allocate in bytes. -    /// -    /// @note If a heap is currently allocated, and this is called -    ///       with a size that is equal to the size of the current heap, -    ///       then this function will do nothing and return the current -    ///       heap's starting address, as there's no need to perform -    ///       any additional heap allocation work. -    /// -    /// @note If a heap is currently allocated, and this is called -    ///       with a size less than the current heap's size, then -    ///       this function will attempt to shrink the heap. -    /// -    /// @note If a heap is currently allocated, and this is called -    ///       with a size larger than the current heap's size, then -    ///       this function will attempt to extend the size of the heap. -    /// -    /// @returns A result indicating either success or failure. -    ///          <p> -    ///          If successful, this function will return a result -    ///          containing the starting address to the allocated heap. -    ///          <p> -    ///          If unsuccessful, this function will return a result -    ///          containing an error code. -    /// -    /// @pre The given size must lie within the allowable heap -    ///      memory region managed by this VMManager instance. -    ///      Failure to abide by this will result in ERR_OUT_OF_MEMORY -    ///      being returned as the result. -    /// -    ResultVal<VAddr> SetHeapSize(u64 size); - -    /// Maps memory at a given address. -    /// -    /// @param target The virtual address to map memory at. -    /// @param size   The amount of memory to map. -    /// -    /// @note The destination address must lie within the Map region. -    /// -    /// @note This function requires that SystemResourceSize be non-zero, -    ///       however, this is just because if it were not then the -    ///       resulting page tables could be exploited on hardware by -    ///       a malicious program. SystemResource usage does not need -    ///       to be explicitly checked or updated here. -    ResultCode MapPhysicalMemory(VAddr target, u64 size); - -    /// Unmaps memory at a given address. -    /// -    /// @param target The virtual address to unmap memory at. -    /// @param size   The amount of memory to unmap. -    /// -    /// @note The destination address must lie within the Map region. -    /// -    /// @note This function requires that SystemResourceSize be non-zero, -    ///       however, this is just because if it were not then the -    ///       resulting page tables could be exploited on hardware by -    ///       a malicious program. SystemResource usage does not need -    ///       to be explicitly checked or updated here. -    ResultCode UnmapPhysicalMemory(VAddr target, u64 size); - -    /// Maps a region of memory as code memory. -    /// -    /// @param dst_address The base address of the region to create the aliasing memory region. -    /// @param src_address The base address of the region to be aliased. -    /// @param size        The total amount of memory to map in bytes. -    /// -    /// @pre Both memory regions lie within the actual addressable address space. -    /// -    /// @post After this function finishes execution, assuming success, then the address range -    ///       [dst_address, dst_address+size) will alias the memory region, -    ///       [src_address, src_address+size). -    ///       <p> -    ///       What this also entails is as follows: -    ///          1. The aliased region gains the Locked memory attribute. -    ///          2. The aliased region becomes read-only. -    ///          3. The aliasing region becomes read-only. -    ///          4. The aliasing region is created with a memory state of MemoryState::CodeModule. -    /// -    ResultCode MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); - -    /// Unmaps a region of memory designated as code module memory. -    /// -    /// @param dst_address The base address of the memory region aliasing the source memory region. -    /// @param src_address The base address of the memory region being aliased. -    /// @param size        The size of the memory region to unmap in bytes. -    /// -    /// @pre Both memory ranges lie within the actual addressable address space. -    /// -    /// @pre The memory region being unmapped has been previously been mapped -    ///      by a call to MapCodeMemory. -    /// -    /// @post After execution of the function, if successful. the aliasing memory region -    ///       will be unmapped and the aliased region will have various traits about it -    ///       restored to what they were prior to the original mapping call preceding -    ///       this function call. -    ///       <p> -    ///       What this also entails is as follows: -    ///           1. The state of the memory region will now indicate a general heap region. -    ///           2. All memory attributes for the memory region are cleared. -    ///           3. Memory permissions for the region are restored to user read/write. -    /// -    ResultCode UnmapCodeMemory(VAddr dst_address, VAddr src_address, u64 size); - -    /// Queries the memory manager for information about the given address. -    /// -    /// @param address The address to query the memory manager about for information. -    /// -    /// @return A MemoryInfo instance containing information about the given address. -    /// -    MemoryInfo QueryMemory(VAddr address) const; - -    /// Sets an attribute across the given address range. -    /// -    /// @param address   The starting address -    /// @param size      The size of the range to set the attribute on. -    /// @param mask      The attribute mask -    /// @param attribute The attribute to set across the given address range -    /// -    /// @returns RESULT_SUCCESS if successful -    /// @returns ERR_INVALID_ADDRESS_STATE if the attribute could not be set. -    /// -    ResultCode SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask, -                                  MemoryAttribute attribute); - -    /** -     * Scans all VMAs and updates the page table range of any that use the given vector as backing -     * memory. This should be called after any operation that causes reallocation of the vector. -     */ -    void RefreshMemoryBlockMappings(const PhysicalMemory* block); - -    /// Dumps the address space layout to the log, for debugging -    void LogLayout() const; - -    /// Gets the total memory usage, used by svcGetInfo -    u64 GetTotalPhysicalMemoryAvailable() const; - -    /// Gets the address space base address -    VAddr GetAddressSpaceBaseAddress() const; - -    /// Gets the address space end address -    VAddr GetAddressSpaceEndAddress() const; - -    /// Gets the total address space address size in bytes -    u64 GetAddressSpaceSize() const; - -    /// Gets the address space width in bits. -    u64 GetAddressSpaceWidth() const; - -    /// Determines whether or not the given address range lies within the address space. -    bool IsWithinAddressSpace(VAddr address, u64 size) const; - -    /// Gets the base address of the ASLR region. -    VAddr GetASLRRegionBaseAddress() const; - -    /// Gets the end address of the ASLR region. -    VAddr GetASLRRegionEndAddress() const; - -    /// Gets the size of the ASLR region -    u64 GetASLRRegionSize() const; - -    /// Determines whether or not the specified address range is within the ASLR region. -    bool IsWithinASLRRegion(VAddr address, u64 size) const; - -    /// Gets the base address of the code region. -    VAddr GetCodeRegionBaseAddress() const; - -    /// Gets the end address of the code region. -    VAddr GetCodeRegionEndAddress() const; - -    /// Gets the total size of the code region in bytes. -    u64 GetCodeRegionSize() const; - -    /// Determines whether or not the specified range is within the code region. -    bool IsWithinCodeRegion(VAddr address, u64 size) const; - -    /// Gets the base address of the heap region. -    VAddr GetHeapRegionBaseAddress() const; - -    /// Gets the end address of the heap region; -    VAddr GetHeapRegionEndAddress() const; - -    /// Gets the total size of the heap region in bytes. -    u64 GetHeapRegionSize() const; - -    /// Gets the total size of the current heap in bytes. -    /// -    /// @note This is the current allocated heap size, not the size -    ///       of the region it's allowed to exist within. -    /// -    u64 GetCurrentHeapSize() const; - -    /// Determines whether or not the specified range is within the heap region. -    bool IsWithinHeapRegion(VAddr address, u64 size) const; - -    /// Gets the base address of the map region. -    VAddr GetMapRegionBaseAddress() const; - -    /// Gets the end address of the map region. -    VAddr GetMapRegionEndAddress() const; - -    /// Gets the total size of the map region in bytes. -    u64 GetMapRegionSize() const; - -    /// Determines whether or not the specified range is within the map region. -    bool IsWithinMapRegion(VAddr address, u64 size) const; - -    /// Gets the base address of the stack region. -    VAddr GetStackRegionBaseAddress() const; - -    /// Gets the end address of the stack region. -    VAddr GetStackRegionEndAddress() const; - -    /// Gets the total size of the stack region in bytes. -    u64 GetStackRegionSize() const; - -    /// Determines whether or not the given address range is within the stack region -    bool IsWithinStackRegion(VAddr address, u64 size) const; - -    /// Gets the base address of the TLS IO region. -    VAddr GetTLSIORegionBaseAddress() const; - -    /// Gets the end address of the TLS IO region. -    VAddr GetTLSIORegionEndAddress() const; - -    /// Gets the total size of the TLS IO region in bytes. -    u64 GetTLSIORegionSize() const; - -    /// Determines if the given address range is within the TLS IO region. -    bool IsWithinTLSIORegion(VAddr address, u64 size) const; - -    /// Each VMManager has its own page table, which is set as the main one when the owning process -    /// is scheduled. -    Common::PageTable page_table{Memory::PAGE_BITS}; - -    using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; - -    /// Checks if an address range adheres to the specified states provided. -    /// -    /// @param address         The starting address of the address range. -    /// @param size            The size of the address range. -    /// @param state_mask      The memory state mask. -    /// @param state           The state to compare the individual VMA states against, -    ///                        which is done in the form of: (vma.state & state_mask) != state. -    /// @param permission_mask The memory permissions mask. -    /// @param permissions     The permission to compare the individual VMA permissions against, -    ///                        which is done in the form of: -    ///                        (vma.permission & permission_mask) != permission. -    /// @param attribute_mask  The memory attribute mask. -    /// @param attribute       The memory attributes to compare the individual VMA attributes -    ///                        against, which is done in the form of: -    ///                        (vma.attributes & attribute_mask) != attribute. -    /// @param ignore_mask     The memory attributes to ignore during the check. -    /// -    /// @returns If successful, returns a tuple containing the memory attributes -    ///          (with ignored bits specified by ignore_mask unset), memory permissions, and -    ///          memory state across the memory range. -    /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. -    /// -    CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, -                                 VMAPermission permission_mask, VMAPermission permissions, -                                 MemoryAttribute attribute_mask, MemoryAttribute attribute, -                                 MemoryAttribute ignore_mask) const; - -private: -    using VMAIter = VMAMap::iterator; - -    /// Converts a VMAHandle to a mutable VMAIter. -    VMAIter StripIterConstness(const VMAHandle& iter); - -    /// Unmaps the given VMA. -    VMAIter Unmap(VMAIter vma); - -    /** -     * Carves a VMA of a specific size at the specified address by splitting Free VMAs while doing -     * the appropriate error checking. -     */ -    ResultVal<VMAIter> CarveVMA(VAddr base, u64 size); - -    /** -     * Splits the edges of the given range of non-Free VMAs so that there is a VMA split at each -     * end of the range. -     */ -    ResultVal<VMAIter> CarveVMARange(VAddr base, u64 size); - -    /** -     * Splits a VMA in two, at the specified offset. -     * @returns the right side of the split, with the original iterator becoming the left side. -     */ -    VMAIter SplitVMA(VMAIter vma, u64 offset_in_vma); - -    /** -     * Checks for and merges the specified VMA with adjacent ones if possible. -     * @returns the merged VMA or the original if no merging was possible. -     */ -    VMAIter MergeAdjacent(VMAIter vma); - -    /** -     * Merges two adjacent VMAs. -     */ -    void MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryArea& right); - -    /// Updates the pages corresponding to this VMA so they match the VMA's attributes. -    void UpdatePageTableForVMA(const VirtualMemoryArea& vma); - -    /// Initializes memory region ranges to adhere to a given address space type. -    void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type); - -    /// Clears the underlying map and page table. -    void Clear(); - -    /// Clears out the VMA map, unmapping any previously mapped ranges. -    void ClearVMAMap(); - -    /// Clears out the page table -    void ClearPageTable(); - -    /// Gets the amount of memory currently mapped (state != Unmapped) in a range. -    ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; - -    /// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range. -    ResultVal<std::size_t> SizeOfUnmappablePhysicalMemoryInRange(VAddr address, -                                                                 std::size_t size) const; - -    /** -     * A map covering the entirety of the managed address space, keyed by the `base` field of each -     * VMA. It must always be modified by splitting or merging VMAs, so that the invariant -     * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be -     * merged when possible so that no two similar and adjacent regions exist that have not been -     * merged. -     */ -    VMAMap vma_map; - -    u32 address_space_width = 0; -    VAddr address_space_base = 0; -    VAddr address_space_end = 0; - -    VAddr aslr_region_base = 0; -    VAddr aslr_region_end = 0; - -    VAddr code_region_base = 0; -    VAddr code_region_end = 0; - -    VAddr heap_region_base = 0; -    VAddr heap_region_end = 0; - -    VAddr map_region_base = 0; -    VAddr map_region_end = 0; - -    VAddr stack_region_base = 0; -    VAddr stack_region_end = 0; - -    VAddr tls_io_region_base = 0; -    VAddr tls_io_region_end = 0; - -    // Memory used to back the allocations in the regular heap. A single vector is used to cover -    // the entire virtual address space extents that bound the allocations, including any holes. -    // 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<PhysicalMemory> heap_memory; - -    // The end of the currently allocated heap. This is not an inclusive -    // end of the range. This is essentially 'base_address + current_size'. -    VAddr heap_end = 0; - -    // The current amount of memory mapped via MapPhysicalMemory. -    // This is used here (and in Nintendo's kernel) only for debugging, and does not impact -    // any behavior. -    u64 physical_memory_mapped = 0; - -    Core::System& system; -}; -} // namespace Kernel diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 557608e76..3ece2cf3c 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -903,7 +903,7 @@ private:      void PopOutData(Kernel::HLERequestContext& ctx) {          LOG_DEBUG(Service_AM, "called"); -        const auto storage = applet->GetBroker().PopNormalDataToGame(); +        auto storage = applet->GetBroker().PopNormalDataToGame();          if (storage == nullptr) {              LOG_ERROR(Service_AM,                        "storage is a nullptr. There is no data in the current normal channel"); @@ -934,7 +934,7 @@ private:      void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {          LOG_DEBUG(Service_AM, "called"); -        const auto storage = applet->GetBroker().PopInteractiveDataToGame(); +        auto storage = applet->GetBroker().PopInteractiveDataToGame();          if (storage == nullptr) {              LOG_ERROR(Service_AM,                        "storage is a nullptr. There is no data in the current interactive channel"); diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 4fb2cbc4b..106e89743 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -210,7 +210,7 @@ private:      /// This is the event handle used to check if the audio buffer was released      Kernel::EventPair buffer_event; -    Memory::Memory& main_memory; +    Core::Memory::Memory& main_memory;  };  AudOutU::AudOutU(Core::System& system_) : ServiceFramework("audout:u"), system{system_} { diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 175cabf45..d8359abaa 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -92,11 +92,16 @@ private:      }      void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { -        LOG_WARNING(Service_Audio, "(STUBBED) called"); +        LOG_DEBUG(Service_Audio, "(STUBBED) called"); + +        auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer()); + +        if (result.Succeeded()) { +            ctx.WriteBuffer(result.Unwrap()); +        } -        ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));          IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(RESULT_SUCCESS); +        rb.Push(result.Code());      }      void Start(Kernel::HLERequestContext& ctx) { @@ -252,8 +257,6 @@ private:      }      void GetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx}; -          const auto device_name_buffer = ctx.ReadBuffer();          const std::string name = Common::StringFromBuffer(device_name_buffer); diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index df00ae625..86f36915a 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -76,7 +76,6 @@ private:      }      void ImportTicket(Kernel::HLERequestContext& ctx) { -        IPC::RequestParser rp{ctx};          const auto ticket = ctx.ReadBuffer();          const auto cert = ctx.ReadBuffer(1); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index c1e32b28c..c55d900e2 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -107,6 +107,7 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {      switch (controller_type) {      case NPadControllerType::None:          UNREACHABLE(); +        break;      case NPadControllerType::Handheld:          controller.joy_styles.handheld.Assign(1);          controller.device_type.handheld.Assign(1); @@ -363,6 +364,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*          switch (controller_type) {          case NPadControllerType::None:              UNREACHABLE(); +            break;          case NPadControllerType::Handheld:              handheld_entry.connection_status.raw = 0;              handheld_entry.connection_status.IsWired.Assign(1); @@ -500,7 +502,7 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)  void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,                                          const std::vector<Vibration>& vibrations) { -    LOG_WARNING(Service_HID, "(STUBBED) called"); +    LOG_DEBUG(Service_HID, "(STUBBED) called");      if (!can_controllers_vibrate) {          return; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index d6ed5f304..d6031a987 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -14,6 +14,7 @@  #include "core/hle/ipc_helpers.h"  #include "core/hle/kernel/client_port.h"  #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/readable_event.h"  #include "core/hle/kernel/shared_memory.h"  #include "core/hle/kernel/writable_event.h" @@ -53,9 +54,7 @@ IAppletResource::IAppletResource(Core::System& system)      RegisterHandlers(functions);      auto& kernel = system.Kernel(); -    shared_mem = Kernel::SharedMemory::Create( -        kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, -        Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); +    shared_mem = SharedFrom(&kernel.GetHidSharedMem());      MakeController<Controller_DebugPad>(HidController::DebugPad);      MakeController<Controller_Touchscreen>(HidController::Touchscreen); diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 5e79e2c1a..36ed6f7da 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -6,6 +6,7 @@  #include "core/core.h"  #include "core/core_timing.h"  #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/shared_memory.h"  #include "core/hle/service/hid/irs.h" @@ -38,9 +39,8 @@ IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) {      RegisterHandlers(functions);      auto& kernel = system.Kernel(); -    shared_mem = Kernel::SharedMemory::Create( -        kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite, -        Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory"); + +    shared_mem = SharedFrom(&kernel.GetIrsSharedMem());  }  void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 647943020..0cde7a557 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -8,14 +8,21 @@  #include "common/alignment.h"  #include "common/hex_util.h" +#include "common/scope_exit.h"  #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/memory/page_table.h" +#include "core/hle/kernel/memory/system_control.h"  #include "core/hle/kernel/process.h"  #include "core/hle/service/ldr/ldr.h"  #include "core/hle/service/service.h"  #include "core/loader/nro.h" +#include "core/memory.h"  namespace Service::LDR { +constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2}; +  constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};  constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};  constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53}; @@ -29,7 +36,61 @@ constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};  constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};  constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; -constexpr u64 MAXIMUM_LOADED_RO = 0x40; +constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; +constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; + +struct NRRHeader { +    u32_le magic; +    INSERT_PADDING_BYTES(12); +    u64_le title_id_mask; +    u64_le title_id_pattern; +    INSERT_PADDING_BYTES(16); +    std::array<u8, 0x100> modulus; +    std::array<u8, 0x100> signature_1; +    std::array<u8, 0x100> signature_2; +    u64_le title_id; +    u32_le size; +    INSERT_PADDING_BYTES(4); +    u32_le hash_offset; +    u32_le hash_count; +    INSERT_PADDING_BYTES(8); +}; +static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); + +struct NROHeader { +    INSERT_PADDING_WORDS(1); +    u32_le mod_offset; +    INSERT_PADDING_WORDS(2); +    u32_le magic; +    u32_le version; +    u32_le nro_size; +    u32_le flags; +    u32_le text_offset; +    u32_le text_size; +    u32_le ro_offset; +    u32_le ro_size; +    u32_le rw_offset; +    u32_le rw_size; +    u32_le bss_size; +    INSERT_PADDING_WORDS(1); +    std::array<u8, 0x20> build_id; +    INSERT_PADDING_BYTES(0x20); +}; +static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); + +using SHA256Hash = std::array<u8, 0x20>; + +struct NROInfo { +    SHA256Hash hash{}; +    VAddr nro_address{}; +    std::size_t nro_size{}; +    VAddr bss_address{}; +    std::size_t bss_size{}; +    std::size_t text_size{}; +    std::size_t ro_size{}; +    std::size_t data_size{}; +    VAddr src_addr{}; +};  class DebugMonitor final : public ServiceFramework<DebugMonitor> {  public: @@ -84,7 +145,7 @@ public:              {0, &RelocatableObject::LoadNro, "LoadNro"},              {1, &RelocatableObject::UnloadNro, "UnloadNro"},              {2, &RelocatableObject::LoadNrr, "LoadNrr"}, -            {3, &RelocatableObject::UnloadNrr, "UnloadNrr"}, +            {3, nullptr, "UnloadNrr"},              {4, &RelocatableObject::Initialize, "Initialize"},              {10, nullptr, "LoadNrrEx"},          }; @@ -190,46 +251,125 @@ public:          rb.Push(RESULT_SUCCESS);      } -    void UnloadNrr(Kernel::HLERequestContext& ctx) { -        if (!initialized) { -            LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!"); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ERROR_NOT_INITIALIZED); -            return; +    bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start, +                              std::size_t size) const { +        constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize}; +        const auto start_info{page_table.QueryInfo(start - 1)}; + +        if (start_info.state != Kernel::Memory::MemoryState::Free) { +            return {};          } -        struct Parameters { -            u64_le process_id; -            u64_le nrr_address; -        }; +        if (start_info.GetAddress() > (start - padding_size)) { +            return {}; +        } -        IPC::RequestParser rp{ctx}; -        const auto [process_id, nrr_address] = rp.PopRaw<Parameters>(); +        const auto end_info{page_table.QueryInfo(start + size)}; -        LOG_DEBUG(Service_LDR, "called with process_id={:016X}, nrr_addr={:016X}", process_id, -                  nrr_address); +        if (end_info.state != Kernel::Memory::MemoryState::Free) { +            return {}; +        } -        if (!Common::Is4KBAligned(nrr_address)) { -            LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", -                      nrr_address); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ERROR_INVALID_ALIGNMENT); -            return; +        return (start + size + padding_size) <= (end_info.GetAddress() + end_info.GetSize()); +    } + +    VAddr GetRandomMapRegion(const Kernel::Memory::PageTable& page_table, std::size_t size) const { +        VAddr addr{}; +        const std::size_t end_pages{(page_table.GetAliasCodeRegionSize() - size) >> +                                    Kernel::Memory::PageBits}; +        do { +            addr = page_table.GetAliasCodeRegionStart() + +                   (Kernel::Memory::SystemControl::GenerateRandomRange(0, end_pages) +                    << Kernel::Memory::PageBits); +        } while (!page_table.IsInsideAddressSpace(addr, size) || +                 page_table.IsInsideHeapRegion(addr, size) || +                 page_table.IsInsideAliasRegion(addr, size)); +        return addr; +    } + +    ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress, +                                          u64 size) const { +        for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { +            auto& page_table{process->PageTable()}; +            const VAddr addr{GetRandomMapRegion(page_table, size)}; +            const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; + +            if (result == Kernel::ERR_INVALID_ADDRESS_STATE) { +                continue; +            } + +            CASCADE_CODE(result); + +            if (ValidateRegionForMap(page_table, addr, size)) { +                return MakeResult<VAddr>(addr); +            }          } -        const auto iter = nrr.find(nrr_address); -        if (iter == nrr.end()) { -            LOG_ERROR(Service_LDR, -                      "Attempting to unload NRR which has not been loaded! (addr={:016X})", -                      nrr_address); -            IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ERROR_INVALID_NRR_ADDRESS); -            return; +        return ERROR_INSUFFICIENT_ADDRESS_SPACE; +    } + +    ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size, +                            VAddr bss_addr, std::size_t bss_size, std::size_t size) const { + +        for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { +            auto& page_table{process->PageTable()}; +            VAddr addr{}; + +            CASCADE_RESULT(addr, MapProcessCodeMemory(process, nro_addr, nro_size)); + +            if (bss_size) { +                auto block_guard = detail::ScopeExit([&] { +                    page_table.UnmapProcessCodeMemory(addr + nro_size, bss_addr, bss_size); +                    page_table.UnmapProcessCodeMemory(addr, nro_addr, nro_size); +                }); + +                const ResultCode result{ +                    page_table.MapProcessCodeMemory(addr + nro_size, bss_addr, bss_size)}; + +                if (result == Kernel::ERR_INVALID_ADDRESS_STATE) { +                    continue; +                } + +                if (result.IsError()) { +                    return result; +                } + +                block_guard.Cancel(); +            } + +            if (ValidateRegionForMap(page_table, addr, size)) { +                return MakeResult<VAddr>(addr); +            }          } -        nrr.erase(iter); -        IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(RESULT_SUCCESS); +        return ERROR_INSUFFICIENT_ADDRESS_SPACE; +    } + +    ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, +                       VAddr start) const { +        const VAddr text_start{start + nro_header.text_offset}; +        const VAddr ro_start{start + nro_header.ro_offset}; +        const VAddr data_start{start + nro_header.rw_offset}; +        const VAddr bss_start{data_start + nro_header.rw_size}; +        const VAddr bss_end_addr{ +            Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; + +        auto CopyCode{[&](VAddr src_addr, VAddr dst_addr, u64 size) { +            std::vector<u8> source_data(size); +            system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); +            system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); +        }}; +        CopyCode(nro_addr + nro_header.text_offset, text_start, nro_header.text_size); +        CopyCode(nro_addr + nro_header.ro_offset, ro_start, nro_header.ro_size); +        CopyCode(nro_addr + nro_header.rw_offset, data_start, nro_header.rw_size); + +        CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( +            text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); +        CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( +            ro_start, data_start - ro_start, Kernel::Memory::MemoryPermission::Read)); + +        return process->PageTable().SetCodeMemoryPermission( +            data_start, bss_end_addr - data_start, Kernel::Memory::MemoryPermission::ReadAndWrite);      }      void LoadNro(Kernel::HLERequestContext& ctx) { @@ -317,9 +457,9 @@ public:              return;          } -        NROHeader header; +        // Load and validate the NRO header +        NROHeader header{};          std::memcpy(&header, nro_data.data(), sizeof(NROHeader)); -          if (!IsValidNRO(header, nro_size, bss_size)) {              LOG_ERROR(Service_LDR, "NRO was invalid!");              IPC::ResponseBuilder rb{ctx, 2}; @@ -327,62 +467,48 @@ public:              return;          } -        // Load NRO as new executable module -        auto* process = system.CurrentProcess(); -        auto& vm_manager = process->VMManager(); -        auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size); - -        if (!map_address.Succeeded() || -            *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) { - -            LOG_ERROR(Service_LDR, -                      "General error while allocation memory or no available memory to allocate!"); +        // Map memory for the NRO +        const auto map_result{MapNro(system.CurrentProcess(), nro_address, nro_size, bss_address, +                                     bss_size, nro_size + bss_size)}; +        if (map_result.Failed()) {              IPC::ResponseBuilder rb{ctx, 2}; -            rb.Push(ERROR_INVALID_MEMORY_STATE); -            return; +            rb.Push(map_result.Code());          } -        // Mark text and read-only region as ModuleCode -        ASSERT(vm_manager -                   .MirrorMemory(*map_address, nro_address, header.text_size + header.ro_size, -                                 Kernel::MemoryState::ModuleCode) -                   .IsSuccess()); -        // Mark read/write region as ModuleCodeData, which is necessary if this region is used for -        // TransferMemory (e.g. Final Fantasy VIII Remastered does this) -        ASSERT(vm_manager -                   .MirrorMemory(*map_address + header.rw_offset, nro_address + header.rw_offset, -                                 header.rw_size, Kernel::MemoryState::ModuleCodeData) -                   .IsSuccess()); -        // Revoke permissions from the old memory region -        ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None) -                   .IsSuccess()); - -        if (bss_size > 0) { -            // Mark BSS region as ModuleCodeData, which is necessary if this region is used for -            // TransferMemory (e.g. Final Fantasy VIII Remastered does this) -            ASSERT(vm_manager -                       .MirrorMemory(*map_address + nro_size, bss_address, bss_size, -                                     Kernel::MemoryState::ModuleCodeData) -                       .IsSuccess()); -            ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None) -                       .IsSuccess()); +        // Load the NRO into the mapped memory +        if (const auto result{LoadNro(system.CurrentProcess(), header, nro_address, *map_result)}; +            result.IsError()) { +            IPC::ResponseBuilder rb{ctx, 2}; +            rb.Push(map_result.Code());          } -        vm_manager.ReprotectRange(*map_address, header.text_size, -                                  Kernel::VMAPermission::ReadExecute); -        vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size, -                                  Kernel::VMAPermission::Read); -        vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, -                                  Kernel::VMAPermission::ReadWrite); +        // Track the loaded NRO +        nro.insert_or_assign(*map_result, NROInfo{hash, *map_result, nro_size, bss_address, +                                                  bss_size, header.text_size, header.ro_size, +                                                  header.rw_size, nro_address}); +        // Invalidate JIT caches for the newly mapped process code          system.InvalidateCpuInstructionCaches(); -        nro.insert_or_assign(*map_address, -                             NROInfo{hash, nro_address, nro_size, bss_address, bss_size}); -          IPC::ResponseBuilder rb{ctx, 4};          rb.Push(RESULT_SUCCESS); -        rb.Push(*map_address); +        rb.Push(*map_result); +    } + +    ResultCode UnmapNro(const NROInfo& info) { +        // Each region must be unmapped separately to validate memory state +        auto& page_table{system.CurrentProcess()->PageTable()}; +        CASCADE_CODE(page_table.UnmapProcessCodeMemory(info.nro_address + info.text_size + +                                                           info.ro_size + info.data_size, +                                                       info.bss_address, info.bss_size)); +        CASCADE_CODE(page_table.UnmapProcessCodeMemory( +            info.nro_address + info.text_size + info.ro_size, +            info.src_addr + info.text_size + info.ro_size, info.data_size)); +        CASCADE_CODE(page_table.UnmapProcessCodeMemory( +            info.nro_address + info.text_size, info.src_addr + info.text_size, info.ro_size)); +        CASCADE_CODE( +            page_table.UnmapProcessCodeMemory(info.nro_address, info.src_addr, info.text_size)); +        return RESULT_SUCCESS;      }      void UnloadNro(Kernel::HLERequestContext& ctx) { @@ -422,30 +548,15 @@ public:              return;          } -        auto& vm_manager = system.CurrentProcess()->VMManager(); -        const auto& nro_info = iter->second; - -        // Unmap the mirrored memory -        ASSERT( -            vm_manager.UnmapRange(nro_address, nro_info.nro_size + nro_info.bss_size).IsSuccess()); - -        // Reprotect the source memory -        ASSERT(vm_manager -                   .ReprotectRange(nro_info.nro_address, nro_info.nro_size, -                                   Kernel::VMAPermission::ReadWrite) -                   .IsSuccess()); -        if (nro_info.bss_size > 0) { -            ASSERT(vm_manager -                       .ReprotectRange(nro_info.bss_address, nro_info.bss_size, -                                       Kernel::VMAPermission::ReadWrite) -                       .IsSuccess()); -        } +        const auto result{UnmapNro(iter->second)};          system.InvalidateCpuInstructionCaches();          nro.erase(iter); +          IPC::ResponseBuilder rb{ctx, 2}; -        rb.Push(RESULT_SUCCESS); + +        rb.Push(result);      }      void Initialize(Kernel::HLERequestContext& ctx) { @@ -458,56 +569,7 @@ public:      }  private: -    using SHA256Hash = std::array<u8, 0x20>; - -    struct NROHeader { -        INSERT_PADDING_WORDS(1); -        u32_le mod_offset; -        INSERT_PADDING_WORDS(2); -        u32_le magic; -        u32_le version; -        u32_le nro_size; -        u32_le flags; -        u32_le text_offset; -        u32_le text_size; -        u32_le ro_offset; -        u32_le ro_size; -        u32_le rw_offset; -        u32_le rw_size; -        u32_le bss_size; -        INSERT_PADDING_WORDS(1); -        std::array<u8, 0x20> build_id; -        INSERT_PADDING_BYTES(0x20); -    }; -    static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); - -    struct NRRHeader { -        u32_le magic; -        INSERT_PADDING_BYTES(12); -        u64_le title_id_mask; -        u64_le title_id_pattern; -        INSERT_PADDING_BYTES(16); -        std::array<u8, 0x100> modulus; -        std::array<u8, 0x100> signature_1; -        std::array<u8, 0x100> signature_2; -        u64_le title_id; -        u32_le size; -        INSERT_PADDING_BYTES(4); -        u32_le hash_offset; -        u32_le hash_count; -        INSERT_PADDING_BYTES(8); -    }; -    static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); - -    struct NROInfo { -        SHA256Hash hash; -        VAddr nro_address; -        u64 nro_size; -        VAddr bss_address; -        u64 bss_size; -    }; - -    bool initialized = false; +    bool initialized{};      std::map<VAddr, NROInfo> nro;      std::map<VAddr, std::vector<SHA256Hash>> nrr; diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 346c8f899..dec96b771 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp @@ -17,7 +17,7 @@ namespace Service::LM {  class ILogger final : public ServiceFramework<ILogger> {  public: -    explicit ILogger(Manager& manager_, Memory::Memory& memory_) +    explicit ILogger(Manager& manager_, Core::Memory::Memory& memory_)          : ServiceFramework("ILogger"), manager{manager_}, memory{memory_} {          static const FunctionInfo functions[] = {              {0, &ILogger::Log, "Log"}, @@ -75,12 +75,12 @@ private:      }      Manager& manager; -    Memory::Memory& memory; +    Core::Memory::Memory& memory;  };  class LM final : public ServiceFramework<LM> {  public: -    explicit LM(Manager& manager_, Memory::Memory& memory_) +    explicit LM(Manager& manager_, Core::Memory::Memory& memory_)          : ServiceFramework{"lm"}, manager{manager_}, memory{memory_} {          // clang-format off          static const FunctionInfo functions[] = { @@ -101,7 +101,7 @@ private:      }      Manager& manager; -    Memory::Memory& memory; +    Core::Memory::Memory& memory;  };  void InstallInterfaces(Core::System& system) { diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 8da4e52c5..ab1746d28 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp @@ -19,6 +19,7 @@  #include "core/file_sys/romfs.h"  #include "core/file_sys/system_archive/system_archive.h"  #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/kernel.h"  #include "core/hle/kernel/physical_memory.h"  #include "core/hle/kernel/shared_memory.h"  #include "core/hle/service/filesystem/filesystem.h" @@ -265,16 +266,13 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {  void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {      // Map backing memory for the font data      LOG_DEBUG(Service_NS, "called"); -    system.CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, -                                                        SHARED_FONT_MEM_SIZE, -                                                        Kernel::MemoryState::Shared);      // Create shared font memory object      auto& kernel = system.Kernel(); -    impl->shared_font_mem = Kernel::SharedMemory::Create( -        kernel, system.CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, -        Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, -        "PL_U:shared_font_mem"); +    impl->shared_font_mem = SharedFrom(&kernel.GetFontSharedMem()); + +    std::memcpy(impl->shared_font_mem->GetPointer(), impl->shared_font->data(), +                impl->shared_font->size());      IPC::ResponseBuilder rb{ctx, 2, 1};      rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h index 3f505c37c..c993bdf79 100644 --- a/src/core/hle/service/time/standard_network_system_clock_core.h +++ b/src/core/hle/service/time/standard_network_system_clock_core.h @@ -23,7 +23,7 @@ public:          standard_network_clock_sufficient_accuracy = value;      } -    bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) { +    bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) const {          SystemClockContext context{};          if (GetClockContext(system, context) != RESULT_SUCCESS) {              return {}; diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h index 84af3d105..d80a2385f 100644 --- a/src/core/hle/service/time/steady_clock_core.h +++ b/src/core/hle/service/time/steady_clock_core.h @@ -16,6 +16,7 @@ namespace Service::Time::Clock {  class SteadyClockCore {  public:      SteadyClockCore() = default; +    virtual ~SteadyClockCore() = default;      const Common::UUID& GetClockSourceId() const {          return clock_source_id; diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h index 6260de6c3..2b0fa7e75 100644 --- a/src/core/hle/service/time/system_clock_context_update_callback.h +++ b/src/core/hle/service/time/system_clock_context_update_callback.h @@ -20,7 +20,7 @@ namespace Service::Time::Clock {  class SystemClockContextUpdateCallback {  public:      SystemClockContextUpdateCallback(); -    ~SystemClockContextUpdateCallback(); +    virtual ~SystemClockContextUpdateCallback();      bool NeedUpdate(const SystemClockContext& value) const; diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp index 1a3ab8cfa..d31d4e2ca 100644 --- a/src/core/hle/service/time/system_clock_core.cpp +++ b/src/core/hle/service/time/system_clock_core.cpp @@ -9,7 +9,7 @@  namespace Service::Time::Clock {  SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core) -    : steady_clock_core{steady_clock_core}, is_initialized{} { +    : steady_clock_core{steady_clock_core} {      context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();  } diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h index 54407a6c5..608dd3b2e 100644 --- a/src/core/hle/service/time/system_clock_core.h +++ b/src/core/hle/service/time/system_clock_core.h @@ -22,7 +22,7 @@ class SystemClockContextUpdateCallback;  class SystemClockCore {  public:      explicit SystemClockCore(SteadyClockCore& steady_clock_core); -    ~SystemClockCore(); +    virtual ~SystemClockCore();      SteadyClockCore& GetSteadyClockCore() const {          return steady_clock_core; diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp index fdaef233f..999ec1e51 100644 --- a/src/core/hle/service/time/time_sharedmemory.cpp +++ b/src/core/hle/service/time/time_sharedmemory.cpp @@ -6,6 +6,7 @@  #include "core/core_timing.h"  #include "core/core_timing_util.h"  #include "core/hardware_properties.h" +#include "core/hle/kernel/kernel.h"  #include "core/hle/service/time/clock_types.h"  #include "core/hle/service/time/steady_clock_core.h"  #include "core/hle/service/time/time_sharedmemory.h" @@ -15,9 +16,7 @@ namespace Service::Time {  static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};  SharedMemory::SharedMemory(Core::System& system) : system(system) { -    shared_memory_holder = Kernel::SharedMemory::Create( -        system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, -        Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory"); +    shared_memory_holder = SharedFrom(&system.Kernel().GetTimeSharedMem());      std::memset(shared_memory_holder->GetPointer(), 0, SHARED_MEMORY_SIZE);  } diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp index c8159bcd5..69152d0ac 100644 --- a/src/core/hle/service/time/time_zone_manager.cpp +++ b/src/core/hle/service/time/time_zone_manager.cpp @@ -518,8 +518,8 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi      constexpr s32 time_zone_max_leaps{50};      constexpr s32 time_zone_max_chars{50};      if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps && -          0 < header.type_count && header.type_count < time_zone_rule.ttis.size() && -          0 <= header.time_count && header.time_count < time_zone_rule.ats.size() && +          0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) && +          0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) &&            0 <= header.char_count && header.char_count < time_zone_max_chars &&            (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) &&            (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) { diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 53559e8b1..134e83412 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -14,6 +14,7 @@  #include "core/file_sys/romfs_factory.h"  #include "core/gdbstub/gdbstub.h"  #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h"  #include "core/hle/service/filesystem/filesystem.h"  #include "core/loader/deconstructed_rom_directory.h" @@ -129,27 +130,47 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect      }      metadata.Print(); -    if (process.LoadFromMetadata(metadata).IsError()) { -        return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; +    const auto static_modules = {"rtld",    "main",    "subsdk0", "subsdk1", "subsdk2", "subsdk3", +                                 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}; + +    // Use the NSO module loader to figure out the code layout +    std::size_t code_size{}; +    for (const auto& module : static_modules) { +        const FileSys::VirtualFile module_file{dir->GetFile(module)}; +        if (!module_file) { +            continue; +        } + +        const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; +        const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( +            process, *module_file, code_size, should_pass_arguments, false)}; +        if (!tentative_next_load_addr) { +            return {ResultStatus::ErrorLoadingNSO, {}}; +        } + +        code_size = *tentative_next_load_addr;      } -    const FileSys::PatchManager pm(metadata.GetTitleID()); +    // Setup the process code layout +    if (process.LoadFromMetadata(metadata, code_size).IsError()) { +        return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; +    }      // Load NSO modules      modules.clear(); -    const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); -    VAddr next_load_addr = base_address; -    for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", -                               "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { -        const FileSys::VirtualFile module_file = dir->GetFile(module); -        if (module_file == nullptr) { +    const VAddr base_address{process.PageTable().GetCodeRegionStart()}; +    VAddr next_load_addr{base_address}; +    const FileSys::PatchManager pm{metadata.GetTitleID()}; +    for (const auto& module : static_modules) { +        const FileSys::VirtualFile module_file{dir->GetFile(module)}; +        if (!module_file) {              continue;          } -        const VAddr load_addr = next_load_addr; -        const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; -        const auto tentative_next_load_addr = -            AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm); +        const VAddr load_addr{next_load_addr}; +        const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; +        const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( +            process, *module_file, load_addr, should_pass_arguments, true, pm)};          if (!tentative_next_load_addr) {              return {ResultStatus::ErrorLoadingNSO, {}};          } diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 8908e5328..8f7615115 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -10,8 +10,8 @@  #include "common/file_util.h"  #include "common/logging/log.h"  #include "core/hle/kernel/code_set.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h" -#include "core/hle/kernel/vm_manager.h"  #include "core/loader/elf.h"  #include "core/memory.h" @@ -393,15 +393,20 @@ AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {          return {ResultStatus::ErrorIncorrectELFFileSize, {}};      } -    const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); +    const VAddr base_address = process.PageTable().GetCodeRegionStart();      ElfReader elf_reader(&buffer[0]);      Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);      const VAddr entry_point = codeset.entrypoint; +    // Setup the process code layout +    if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) { +        return {ResultStatus::ErrorNotInitialized, {}}; +    } +      process.LoadModule(std::move(codeset), entry_point);      is_loaded = true; -    return {ResultStatus::Success, LoadParameters{48, Memory::DEFAULT_STACK_SIZE}}; +    return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}};  }  } // namespace Loader diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 092103abe..40fa03ad1 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -7,14 +7,16 @@  #include "core/file_sys/program_metadata.h"  #include "core/gdbstub/gdbstub.h"  #include "core/hle/kernel/code_set.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h"  #include "core/loader/kip.h" +#include "core/memory.h"  namespace Loader {  namespace {  constexpr u32 PageAlignSize(u32 size) { -    return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; +    return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK;  }  } // Anonymous namespace @@ -68,7 +70,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {                          kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(),                          kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, kip->GetKernelCapabilities()); -    const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); +    const VAddr base_address = process.PageTable().GetCodeRegionStart();      Kernel::CodeSet codeset;      Kernel::PhysicalMemory program_image; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 175898b91..906544bc9 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -16,8 +16,8 @@  #include "core/file_sys/vfs_offset.h"  #include "core/gdbstub/gdbstub.h"  #include "core/hle/kernel/code_set.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h" -#include "core/hle/kernel/vm_manager.h"  #include "core/hle/service/filesystem/filesystem.h"  #include "core/loader/nro.h"  #include "core/loader/nso.h" @@ -127,11 +127,11 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {  }  static constexpr u32 PageAlignSize(u32 size) { -    return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; +    return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK;  }  static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data, -                        const std::string& name, VAddr load_base) { +                        const std::string& name) {      if (data.size() < sizeof(NroHeader)) {          return {};      } @@ -187,19 +187,25 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,      codeset.DataSegment().size += bss_size;      program_image.resize(static_cast<u32>(program_image.size()) + bss_size); +    // Setup the process code layout +    if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size()) +            .IsError()) { +        return false; +    } +      // Load codeset for current process      codeset.memory = std::move(program_image); -    process.LoadModule(std::move(codeset), load_base); +    process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart());      // Register module with GDBStub -    GDBStub::RegisterModule(name, load_base, load_base); +    GDBStub::RegisterModule(name, process.PageTable().GetCodeRegionStart(), +                            process.PageTable().GetCodeRegionEnd());      return true;  } -bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& file, -                            VAddr load_base) { -    return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base); +bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& file) { +    return LoadNroImpl(process, file.ReadAllBytes(), file.GetName());  }  AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { @@ -207,10 +213,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {          return {ResultStatus::ErrorAlreadyLoaded, {}};      } -    // Load NRO -    const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); - -    if (!LoadNro(process, *file, base_address)) { +    if (!LoadNro(process, *file)) {          return {ResultStatus::ErrorLoadingNRO, {}};      } @@ -221,7 +224,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {      is_loaded = true;      return {ResultStatus::Success, -            LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}}; +            LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}};  }  ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 71811bc29..4593d48fb 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h @@ -47,7 +47,7 @@ public:      bool IsRomFSUpdatable() const override;  private: -    bool LoadNro(Kernel::Process& process, const FileSys::VfsFile& file, VAddr load_base); +    bool LoadNro(Kernel::Process& process, const FileSys::VfsFile& file);      std::vector<u8> icon_data;      std::unique_ptr<FileSys::NACP> nacp; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 044067a5b..575330a86 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -16,8 +16,8 @@  #include "core/file_sys/patch_manager.h"  #include "core/gdbstub/gdbstub.h"  #include "core/hle/kernel/code_set.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h" -#include "core/hle/kernel/vm_manager.h"  #include "core/loader/nso.h"  #include "core/memory.h"  #include "core/settings.h" @@ -37,7 +37,7 @@ static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");  std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,                                    const NSOSegmentHeader& header) { -    const std::vector<u8> uncompressed_data = +    std::vector<u8> uncompressed_data =          Common::Compression::DecompressDataLZ4(compressed_data, header.size);      ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size, @@ -47,7 +47,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,  }  constexpr u32 PageAlignSize(u32 size) { -    return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; +    return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK;  }  } // Anonymous namespace @@ -73,7 +73,7 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {  std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,                                                 const FileSys::VfsFile& file, VAddr load_base, -                                               bool should_pass_arguments, +                                               bool should_pass_arguments, bool load_into_process,                                                 std::optional<FileSys::PatchManager> pm) {      if (file.GetSize() < sizeof(NSOHeader)) {          return {}; @@ -97,21 +97,17 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,          if (nso_header.IsSegmentCompressed(i)) {              data = DecompressSegment(data, nso_header.segments[i]);          } -        program_image.resize(nso_header.segments[i].location + -                             PageAlignSize(static_cast<u32>(data.size()))); +        program_image.resize(nso_header.segments[i].location + static_cast<u32>(data.size()));          std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(),                      data.size());          codeset.segments[i].addr = nso_header.segments[i].location;          codeset.segments[i].offset = nso_header.segments[i].location; -        codeset.segments[i].size = PageAlignSize(static_cast<u32>(data.size())); +        codeset.segments[i].size = nso_header.segments[i].size;      } -    if (should_pass_arguments) { -        std::vector<u8> arg_data{Settings::values.program_args.begin(), -                                 Settings::values.program_args.end()}; -        if (arg_data.empty()) { -            arg_data.resize(NSO_ARGUMENT_DEFAULT_SIZE); -        } +    if (should_pass_arguments && !Settings::values.program_args.empty()) { +        const auto arg_data{Settings::values.program_args}; +          codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;          NSOArgumentHeader args_header{              NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}}; @@ -123,24 +119,15 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,                      arg_data.size());      } -    // MOD header pointer is at .text offset + 4 -    u32 module_offset; -    std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); - -    // Read MOD header -    MODHeader mod_header{}; -    // Default .bss to size in segment header if MOD0 section doesn't exist -    u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; -    std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader)); -    const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; -    if (has_mod_header) { -        // 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.DataSegment().size += bss_size; -    const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; +    codeset.DataSegment().size += nso_header.segments[2].bss_size; +    const u32 image_size{ +        PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)};      program_image.resize(image_size); +    for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { +        codeset.segments[i].size = PageAlignSize(codeset.segments[i].size); +    } +      // Apply patches if necessary      if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {          std::vector<u8> pi_header; @@ -154,6 +141,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,          std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data());      } +    // If we aren't actually loading (i.e. just computing the process code layout), we are done +    if (!load_into_process) { +        return load_base + image_size; +    } +      // Apply cheats if they exist and the program has a valid title ID      if (pm) {          auto& system = Core::System::GetInstance(); @@ -182,8 +174,8 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {      modules.clear();      // Load module -    const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); -    if (!LoadModule(process, *file, base_address, true)) { +    const VAddr base_address = process.PageTable().GetCodeRegionStart(); +    if (!LoadModule(process, *file, base_address, true, true)) {          return {ResultStatus::ErrorLoadingNSO, {}};      } @@ -192,7 +184,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {      is_loaded = true;      return {ResultStatus::Success, -            LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}}; +            LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}};  }  ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) { diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index d2d600cd9..b210830f0 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h @@ -56,8 +56,6 @@ static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");  static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");  constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000; -// NOTE: Official software default argument state is unverified. -constexpr u64 NSO_ARGUMENT_DEFAULT_SIZE = 1;  struct NSOArgumentHeader {      u32_le allocated_size; @@ -84,6 +82,7 @@ public:      static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file,                                             VAddr load_base, bool should_pass_arguments, +                                           bool load_into_process,                                             std::optional<FileSys::PatchManager> pm = {});      LoadResult Load(Kernel::Process& process) override; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 6061d37ae..9d87045a0 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -14,13 +14,14 @@  #include "common/swap.h"  #include "core/arm/arm_interface.h"  #include "core/core.h" +#include "core/device_memory.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/physical_memory.h"  #include "core/hle/kernel/process.h" -#include "core/hle/kernel/vm_manager.h"  #include "core/memory.h"  #include "video_core/gpu.h" -namespace Memory { +namespace Core::Memory {  // Implementation class used to keep the specifics of the memory subsystem hidden  // from outside classes. This also allows modification to the internals of the memory @@ -29,9 +30,9 @@ struct Memory::Impl {      explicit Impl(Core::System& system_) : system{system_} {}      void SetCurrentPageTable(Kernel::Process& process) { -        current_page_table = &process.VMManager().page_table; +        current_page_table = &process.PageTable().PageTableImpl(); -        const std::size_t address_space_width = process.VMManager().GetAddressSpaceWidth(); +        const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth();          system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width);          system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width); @@ -39,12 +40,7 @@ struct Memory::Impl {          system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);      } -    void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, -                         Kernel::PhysicalMemory& memory, VAddr offset) { -        MapMemoryRegion(page_table, base, size, memory.data() + offset); -    } - -    void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) { +    void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {          ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);          ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);          MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory); @@ -52,46 +48,27 @@ struct Memory::Impl {      void MapIoRegion(Common::PageTable& page_table, VAddr base, u64 size,                       Common::MemoryHookPointer mmio_handler) { -        ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size); -        ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); -        MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, -                 Common::PageType::Special); - -        const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); -        const Common::SpecialRegion region{Common::SpecialRegion::Type::IODevice, -                                           std::move(mmio_handler)}; -        page_table.special_regions.add( -            std::make_pair(interval, std::set<Common::SpecialRegion>{region})); +        UNIMPLEMENTED();      }      void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {          ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);          ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base); -        MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, -                 Common::PageType::Unmapped); - -        const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); -        page_table.special_regions.erase(interval); +        MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);      }      void AddDebugHook(Common::PageTable& page_table, VAddr base, u64 size,                        Common::MemoryHookPointer hook) { -        const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); -        const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; -        page_table.special_regions.add( -            std::make_pair(interval, std::set<Common::SpecialRegion>{region})); +        UNIMPLEMENTED();      }      void RemoveDebugHook(Common::PageTable& page_table, VAddr base, u64 size,                           Common::MemoryHookPointer hook) { -        const auto interval = boost::icl::discrete_interval<VAddr>::closed(base, base + size - 1); -        const Common::SpecialRegion region{Common::SpecialRegion::Type::DebugHook, std::move(hook)}; -        page_table.special_regions.subtract( -            std::make_pair(interval, std::set<Common::SpecialRegion>{region})); +        UNIMPLEMENTED();      }      bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const { -        const auto& page_table = process.VMManager().page_table; +        const auto& page_table = process.PageTable().PageTableImpl();          const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS];          if (page_pointer != nullptr) { @@ -113,55 +90,28 @@ struct Memory::Impl {          return IsValidVirtualAddress(*system.CurrentProcess(), vaddr);      } -    /** -     * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) -     * using a VMA from the current process -     */ -    u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { -        const auto& vm_manager = process.VMManager(); +    u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const { +        const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]}; -        const auto it = vm_manager.FindVMA(vaddr); -        DEBUG_ASSERT(vm_manager.IsValidHandle(it)); - -        u8* direct_pointer = nullptr; -        const auto& vma = it->second; -        switch (vma.type) { -        case Kernel::VMAType::AllocatedMemoryBlock: -            direct_pointer = vma.backing_block->data() + vma.offset; -            break; -        case Kernel::VMAType::BackingMemory: -            direct_pointer = vma.backing_memory; -            break; -        case Kernel::VMAType::Free: -            return nullptr; -        default: -            UNREACHABLE(); +        if (!paddr) { +            return {};          } -        return direct_pointer + (vaddr - vma.base); +        return system.DeviceMemory().GetPointer(paddr) + vaddr;      } -    /** -     * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) -     * using a VMA from the current process. -     */ -    u8* GetPointerFromVMA(VAddr vaddr) { -        return GetPointerFromVMA(*system.CurrentProcess(), vaddr); -    } - -    u8* GetPointer(const VAddr vaddr) { -        u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; -        if (page_pointer != nullptr) { +    u8* GetPointer(const VAddr vaddr) const { +        u8* const page_pointer{current_page_table->pointers[vaddr >> PAGE_BITS]}; +        if (page_pointer) {              return page_pointer + vaddr;          }          if (current_page_table->attributes[vaddr >> PAGE_BITS] ==              Common::PageType::RasterizerCachedMemory) { -            return GetPointerFromVMA(vaddr); +            return GetPointerFromRasterizerCachedMemory(vaddr);          } -        LOG_ERROR(HW_Memory, "Unknown GetPointer @ 0x{:016X}", vaddr); -        return nullptr; +        return {};      }      u8 Read8(const VAddr addr) { @@ -169,15 +119,33 @@ struct Memory::Impl {      }      u16 Read16(const VAddr addr) { -        return Read<u16_le>(addr); +        if ((addr & 1) == 0) { +            return Read<u16_le>(addr); +        } else { +            const u8 a{Read<u8>(addr)}; +            const u8 b{Read<u8>(addr + sizeof(u8))}; +            return (static_cast<u16>(b) << 8) | a; +        }      }      u32 Read32(const VAddr addr) { -        return Read<u32_le>(addr); +        if ((addr & 3) == 0) { +            return Read<u32_le>(addr); +        } else { +            const u16 a{Read16(addr)}; +            const u16 b{Read16(addr + sizeof(u16))}; +            return (static_cast<u32>(b) << 16) | a; +        }      }      u64 Read64(const VAddr addr) { -        return Read<u64_le>(addr); +        if ((addr & 7) == 0) { +            return Read<u64_le>(addr); +        } else { +            const u32 a{Read32(addr)}; +            const u32 b{Read32(addr + sizeof(u32))}; +            return (static_cast<u64>(b) << 32) | a; +        }      }      void Write8(const VAddr addr, const u8 data) { @@ -185,15 +153,30 @@ struct Memory::Impl {      }      void Write16(const VAddr addr, const u16 data) { -        Write<u16_le>(addr, data); +        if ((addr & 1) == 0) { +            Write<u16_le>(addr, data); +        } else { +            Write<u8>(addr, static_cast<u8>(data)); +            Write<u8>(addr + sizeof(u8), static_cast<u8>(data >> 8)); +        }      }      void Write32(const VAddr addr, const u32 data) { -        Write<u32_le>(addr, data); +        if ((addr & 3) == 0) { +            Write<u32_le>(addr, data); +        } else { +            Write16(addr, static_cast<u16>(data)); +            Write16(addr + sizeof(u16), static_cast<u16>(data >> 16)); +        }      }      void Write64(const VAddr addr, const u64 data) { -        Write<u64_le>(addr, data); +        if ((addr & 7) == 0) { +            Write<u64_le>(addr, data); +        } else { +            Write32(addr, static_cast<u32>(data)); +            Write32(addr + sizeof(u32), static_cast<u32>(data >> 32)); +        }      }      std::string ReadCString(VAddr vaddr, std::size_t max_length) { @@ -213,7 +196,7 @@ struct Memory::Impl {      void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,                     const std::size_t size) { -        const auto& page_table = process.VMManager().page_table; +        const auto& page_table = process.PageTable().PageTableImpl();          std::size_t remaining_size = size;          std::size_t page_index = src_addr >> PAGE_BITS; @@ -241,7 +224,7 @@ struct Memory::Impl {                  break;              }              case Common::PageType::RasterizerCachedMemory: { -                const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); +                const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};                  system.GPU().FlushRegion(current_vaddr, copy_amount);                  std::memcpy(dest_buffer, host_ptr, copy_amount);                  break; @@ -259,7 +242,7 @@ struct Memory::Impl {      void ReadBlockUnsafe(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer,                           const std::size_t size) { -        const auto& page_table = process.VMManager().page_table; +        const auto& page_table = process.PageTable().PageTableImpl();          std::size_t remaining_size = size;          std::size_t page_index = src_addr >> PAGE_BITS; @@ -287,7 +270,7 @@ struct Memory::Impl {                  break;              }              case Common::PageType::RasterizerCachedMemory: { -                const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); +                const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};                  std::memcpy(dest_buffer, host_ptr, copy_amount);                  break;              } @@ -312,7 +295,7 @@ struct Memory::Impl {      void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer,                      const std::size_t size) { -        const auto& page_table = process.VMManager().page_table; +        const auto& page_table = process.PageTable().PageTableImpl();          std::size_t remaining_size = size;          std::size_t page_index = dest_addr >> PAGE_BITS;          std::size_t page_offset = dest_addr & PAGE_MASK; @@ -338,7 +321,7 @@ struct Memory::Impl {                  break;              }              case Common::PageType::RasterizerCachedMemory: { -                u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); +                u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};                  system.GPU().InvalidateRegion(current_vaddr, copy_amount);                  std::memcpy(host_ptr, src_buffer, copy_amount);                  break; @@ -356,7 +339,7 @@ struct Memory::Impl {      void WriteBlockUnsafe(const Kernel::Process& process, const VAddr dest_addr,                            const void* src_buffer, const std::size_t size) { -        const auto& page_table = process.VMManager().page_table; +        const auto& page_table = process.PageTable().PageTableImpl();          std::size_t remaining_size = size;          std::size_t page_index = dest_addr >> PAGE_BITS;          std::size_t page_offset = dest_addr & PAGE_MASK; @@ -382,7 +365,7 @@ struct Memory::Impl {                  break;              }              case Common::PageType::RasterizerCachedMemory: { -                u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); +                u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};                  std::memcpy(host_ptr, src_buffer, copy_amount);                  break;              } @@ -406,7 +389,7 @@ struct Memory::Impl {      }      void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { -        const auto& page_table = process.VMManager().page_table; +        const auto& page_table = process.PageTable().PageTableImpl();          std::size_t remaining_size = size;          std::size_t page_index = dest_addr >> PAGE_BITS;          std::size_t page_offset = dest_addr & PAGE_MASK; @@ -432,7 +415,7 @@ struct Memory::Impl {                  break;              }              case Common::PageType::RasterizerCachedMemory: { -                u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); +                u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};                  system.GPU().InvalidateRegion(current_vaddr, copy_amount);                  std::memset(host_ptr, 0, copy_amount);                  break; @@ -453,7 +436,7 @@ struct Memory::Impl {      void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,                     const std::size_t size) { -        const auto& page_table = process.VMManager().page_table; +        const auto& page_table = process.PageTable().PageTableImpl();          std::size_t remaining_size = size;          std::size_t page_index = src_addr >> PAGE_BITS;          std::size_t page_offset = src_addr & PAGE_MASK; @@ -479,7 +462,7 @@ struct Memory::Impl {                  break;              }              case Common::PageType::RasterizerCachedMemory: { -                const u8* const host_ptr = GetPointerFromVMA(process, current_vaddr); +                const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(current_vaddr)};                  system.GPU().FlushRegion(current_vaddr, copy_amount);                  WriteBlock(process, dest_addr, host_ptr, copy_amount);                  break; @@ -512,7 +495,7 @@ struct Memory::Impl {          u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;          for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) { -            Common::PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; +            Common::PageType& page_type{current_page_table->attributes[vaddr >> PAGE_BITS]};              if (cached) {                  // Switch page type to cached if now cached @@ -544,7 +527,7 @@ struct Memory::Impl {                      // that this area is already unmarked as cached.                      break;                  case Common::PageType::RasterizerCachedMemory: { -                    u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); +                    u8* pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};                      if (pointer == nullptr) {                          // It's possible that this function has been called while updating the                          // pagetable after unmapping a VMA. In that case the underlying VMA will no @@ -573,9 +556,9 @@ struct Memory::Impl {       * @param memory     The memory to map.       * @param type       The page type to map the memory as.       */ -    void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* memory, +    void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,                    Common::PageType type) { -        LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, +        LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE,                    (base + size) * PAGE_SIZE);          // During boot, current_page_table might not be set yet, in which case we need not flush @@ -593,19 +576,26 @@ struct Memory::Impl {          ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}",                     base + page_table.pointers.size()); -        std::fill(page_table.attributes.begin() + base, page_table.attributes.begin() + end, type); +        if (!target) { +            while (base != end) { +                page_table.pointers[base] = nullptr; +                page_table.attributes[base] = type; +                page_table.backing_addr[base] = 0; -        if (memory == nullptr) { -            std::fill(page_table.pointers.begin() + base, page_table.pointers.begin() + end, -                      memory); +                base += 1; +            }          } else {              while (base != end) { -                page_table.pointers[base] = memory - (base << PAGE_BITS); +                page_table.pointers[base] = +                    system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS); +                page_table.attributes[base] = type; +                page_table.backing_addr[base] = target - (base << PAGE_BITS); +                  ASSERT_MSG(page_table.pointers[base],                             "memory mapping base yield a nullptr within the table");                  base += 1; -                memory += PAGE_SIZE; +                target += PAGE_SIZE;              }          }      } @@ -640,7 +630,7 @@ struct Memory::Impl {              ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);              break;          case Common::PageType::RasterizerCachedMemory: { -            const u8* const host_ptr = GetPointerFromVMA(vaddr); +            const u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};              system.GPU().FlushRegion(vaddr, sizeof(T));              T value;              std::memcpy(&value, host_ptr, sizeof(T)); @@ -682,7 +672,7 @@ struct Memory::Impl {              ASSERT_MSG(false, "Mapped memory page without a pointer @ {:016X}", vaddr);              break;          case Common::PageType::RasterizerCachedMemory: { -            u8* const host_ptr{GetPointerFromVMA(vaddr)}; +            u8* const host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)};              system.GPU().InvalidateRegion(vaddr, sizeof(T));              std::memcpy(host_ptr, &data, sizeof(T));              break; @@ -703,12 +693,7 @@ void Memory::SetCurrentPageTable(Kernel::Process& process) {      impl->SetCurrentPageTable(process);  } -void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, -                             Kernel::PhysicalMemory& memory, VAddr offset) { -    impl->MapMemoryRegion(page_table, base, size, memory, offset); -} - -void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target) { +void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {      impl->MapMemoryRegion(page_table, base, size, target);  } @@ -845,4 +830,4 @@ bool IsKernelVirtualAddress(const VAddr vaddr) {      return KERNEL_REGION_VADDR <= vaddr && vaddr < KERNEL_REGION_END;  } -} // namespace Memory +} // namespace Core::Memory diff --git a/src/core/memory.h b/src/core/memory.h index b92d678a4..9292f3b0a 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -23,7 +23,7 @@ class PhysicalMemory;  class Process;  } // namespace Kernel -namespace Memory { +namespace Core::Memory {  /**   * Page size used by the ARM architecture. This is the smallest granularity with which memory can @@ -67,19 +67,6 @@ public:      void SetCurrentPageTable(Kernel::Process& process);      /** -     * Maps an physical buffer onto a region of the emulated process address space. -     * -     * @param page_table The page table of the emulated process. -     * @param base       The address to start mapping at. Must be page-aligned. -     * @param size       The amount of bytes to map. Must be page-aligned. -     * @param memory     Physical buffer with the memory backing the mapping. Must be of length -     *                   at least `size + offset`. -     * @param offset     The offset within the physical memory. Must be page-aligned. -     */ -    void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, -                         Kernel::PhysicalMemory& memory, VAddr offset); - -    /**       * Maps an allocated buffer onto a region of the emulated process address space.       *       * @param page_table The page table of the emulated process. @@ -88,7 +75,7 @@ public:       * @param target     Buffer with the memory backing the mapping. Must be of length at least       *                   `size`.       */ -    void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, u8* target); +    void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target);      /**       * Maps a region of the emulated process address space as a IO region. @@ -503,4 +490,4 @@ private:  /// Determines if the given VAddr is a kernel address  bool IsKernelVirtualAddress(VAddr vaddr); -} // namespace Memory +} // namespace Core::Memory diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 4472500d2..b139e8465 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -10,13 +10,15 @@  #include "core/core_timing.h"  #include "core/core_timing_util.h"  #include "core/hardware_properties.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h"  #include "core/hle/service/hid/controllers/npad.h"  #include "core/hle/service/hid/hid.h"  #include "core/hle/service/sm/sm.h" +#include "core/memory.h"  #include "core/memory/cheat_engine.h" -namespace Memory { +namespace Core::Memory {  constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 12);  constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; @@ -194,11 +196,12 @@ void CheatEngine::Initialize() {      metadata.process_id = system.CurrentProcess()->GetProcessID();      metadata.title_id = system.CurrentProcess()->GetTitleID(); -    const auto& vm_manager = system.CurrentProcess()->VMManager(); -    metadata.heap_extents = {vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionSize()}; -    metadata.address_space_extents = {vm_manager.GetAddressSpaceBaseAddress(), -                                      vm_manager.GetAddressSpaceSize()}; -    metadata.alias_extents = {vm_manager.GetMapRegionBaseAddress(), vm_manager.GetMapRegionSize()}; +    const auto& page_table = system.CurrentProcess()->PageTable(); +    metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()}; +    metadata.address_space_extents = {page_table.GetAddressSpaceStart(), +                                      page_table.GetAddressSpaceSize()}; +    metadata.alias_extents = {page_table.GetAliasCodeRegionStart(), +                              page_table.GetAliasCodeRegionSize()};      is_pending_reload.exchange(true);  } @@ -230,4 +233,4 @@ void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {      core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);  } -} // namespace Memory +} // namespace Core::Memory diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index 3d6b2298a..2649423f8 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -20,7 +20,7 @@ class CoreTiming;  struct EventType;  } // namespace Core::Timing -namespace Memory { +namespace Core::Memory {  class StandardVmCallbacks : public DmntCheatVm::Callbacks {  public: @@ -84,4 +84,4 @@ private:      Core::System& system;  }; -} // namespace Memory +} // namespace Core::Memory diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h index bf68fa0fe..5e60733dc 100644 --- a/src/core/memory/dmnt_cheat_types.h +++ b/src/core/memory/dmnt_cheat_types.h @@ -26,7 +26,7 @@  #include "common/common_types.h" -namespace Memory { +namespace Core::Memory {  struct MemoryRegionExtents {      u64 base{}; @@ -55,4 +55,4 @@ struct CheatEntry {      CheatDefinition definition{};  }; -} // namespace Memory +} // namespace Core::Memory diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index 5bb26a36f..fb9f36bfd 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp @@ -27,7 +27,7 @@  #include "core/memory/dmnt_cheat_types.h"  #include "core/memory/dmnt_cheat_vm.h" -namespace Memory { +namespace Core::Memory {  DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {} @@ -1210,4 +1210,4 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {      }  } -} // namespace Memory +} // namespace Core::Memory diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h index c36212cf1..8351fd798 100644 --- a/src/core/memory/dmnt_cheat_vm.h +++ b/src/core/memory/dmnt_cheat_vm.h @@ -30,7 +30,7 @@  #include "common/common_types.h"  #include "core/memory/dmnt_cheat_types.h" -namespace Memory { +namespace Core::Memory {  enum class CheatVmOpcodeType : u32 {      StoreStatic = 0, @@ -318,4 +318,4 @@ private:                                        MemoryAccessType mem_type, u64 rel_address);  }; -}; // namespace Memory +}; // namespace Core::Memory diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 85ac81ef7..558cbe6d7 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -16,9 +16,11 @@  #include "core/arm/arm_interface.h"  #include "core/core.h"  #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/memory/page_table.h"  #include "core/hle/kernel/process.h"  #include "core/hle/result.h"  #include "core/hle/service/lm/manager.h" +#include "core/memory.h"  #include "core/reporter.h"  #include "core/settings.h" @@ -108,14 +110,13 @@ json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64  json GetProcessorStateDataAuto(Core::System& system) {      const auto* process{system.CurrentProcess()}; -    const auto& vm_manager{process->VMManager()};      auto& arm{system.CurrentArmInterface()};      Core::ARM_Interface::ThreadContext64 context{};      arm.SaveContext(context);      return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", -                                 vm_manager.GetCodeRegionBaseAddress(), context.sp, context.pc, +                                 process->PageTable().GetCodeRegionStart(), context.sp, context.pc,                                   context.pstate, context.cpu_registers);  } @@ -147,7 +148,8 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s  }  template <bool read_value, typename DescriptorType> -json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memory::Memory& memory) { +json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, +                                Core::Memory::Memory& memory) {      auto buffer_out = json::array();      for (const auto& desc : buffer) {          auto entry = json{ @@ -167,7 +169,7 @@ json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer, Memor      return buffer_out;  } -json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Memory::Memory& memory) { +json GetHLERequestContextData(Kernel::HLERequestContext& ctx, Core::Memory::Memory& memory) {      json out;      auto cmd_buf = json::array(); diff --git a/src/core/settings.h b/src/core/settings.h index 79ec01731..c73d1c596 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -464,6 +464,7 @@ struct Values {      bool dump_nso;      bool reporting_services;      bool quest_flag; +    bool disable_cpu_opt;      // BCAT      std::string bcat_backend; diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 1e060f009..b2c6c537e 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp @@ -16,7 +16,7 @@ namespace {  constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); -u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) { +u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {      switch (width) {      case 1:          return memory.Read8(addr); @@ -32,7 +32,7 @@ u64 MemoryReadWidth(Memory::Memory& memory, u32 width, VAddr addr) {      }  } -void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value) { +void MemoryWriteWidth(Core::Memory::Memory& memory, u32 width, VAddr addr, u64 value) {      switch (width) {      case 1:          memory.Write8(addr, static_cast<u8>(value)); @@ -53,7 +53,7 @@ void MemoryWriteWidth(Memory::Memory& memory, u32 width, VAddr addr, u64 value)  } // Anonymous namespace -Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_) +Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_)      : core_timing{core_timing_}, memory{memory_} {      event = Core::Timing::CreateEvent(          "MemoryFreezer::FrameCallback", diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index 916339c6c..62fc6aa6c 100644 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h @@ -16,7 +16,7 @@ class CoreTiming;  struct EventType;  } // namespace Core::Timing -namespace Memory { +namespace Core::Memory {  class Memory;  } @@ -38,7 +38,7 @@ public:          u64 value;      }; -    explicit Freezer(Core::Timing::CoreTiming& core_timing_, Memory::Memory& memory_); +    explicit Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_);      ~Freezer();      // Enables or disables the entire memory freezer. @@ -82,7 +82,7 @@ private:      std::shared_ptr<Core::Timing::EventType> event;      Core::Timing::CoreTiming& core_timing; -    Memory::Memory& memory; +    Core::Memory::Memory& memory;  };  } // namespace Tools  | 
