summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/code_set.h3
-rw-r--r--src/core/hle/kernel/kernel.cpp14
-rw-r--r--src/core/hle/kernel/kernel.h5
-rw-r--r--src/core/hle/kernel/physical_memory.h19
-rw-r--r--src/core/hle/kernel/process.cpp184
-rw-r--r--src/core/hle/kernel/process.h65
-rw-r--r--src/core/hle/kernel/shared_memory.cpp6
-rw-r--r--src/core/hle/kernel/shared_memory.h13
-rw-r--r--src/core/hle/kernel/svc.cpp166
-rw-r--r--src/core/hle/kernel/svc_wrap.h5
-rw-r--r--src/core/hle/kernel/thread.cpp10
-rw-r--r--src/core/hle/kernel/thread.h16
-rw-r--r--src/core/hle/kernel/transfer_memory.cpp2
-rw-r--r--src/core/hle/kernel/transfer_memory.h3
-rw-r--r--src/core/hle/kernel/vm_manager.cpp401
-rw-r--r--src/core/hle/kernel/vm_manager.h108
16 files changed, 788 insertions, 232 deletions
diff --git a/src/core/hle/kernel/code_set.h b/src/core/hle/kernel/code_set.h
index 879957dcb..d8ad54030 100644
--- a/src/core/hle/kernel/code_set.h
+++ b/src/core/hle/kernel/code_set.h
@@ -8,6 +8,7 @@
#include <vector>
#include "common/common_types.h"
+#include "core/hle/kernel/physical_memory.h"
namespace Kernel {
@@ -77,7 +78,7 @@ struct CodeSet final {
}
/// The overall data that backs this code set.
- std::vector<u8> memory;
+ Kernel::PhysicalMemory memory;
/// The segments that comprise this code set.
std::array<Segment, 3> segments;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 757e5f21f..799e5e0d8 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -99,7 +99,8 @@ struct KernelCore::Impl {
void Shutdown() {
next_object_id = 0;
- next_process_id = Process::ProcessIDMin;
+ next_kernel_process_id = Process::InitialKIPIDMin;
+ next_user_process_id = Process::ProcessIDMin;
next_thread_id = 1;
process_list.clear();
@@ -132,7 +133,8 @@ struct KernelCore::Impl {
}
std::atomic<u32> next_object_id{0};
- std::atomic<u64> next_process_id{Process::ProcessIDMin};
+ std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
+ std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
std::atomic<u64> next_thread_id{1};
// Lists all processes that exist in the current session.
@@ -226,8 +228,12 @@ u64 KernelCore::CreateNewThreadID() {
return impl->next_thread_id++;
}
-u64 KernelCore::CreateNewProcessID() {
- return impl->next_process_id++;
+u64 KernelCore::CreateNewKernelProcessID() {
+ return impl->next_kernel_process_id++;
+}
+
+u64 KernelCore::CreateNewUserProcessID() {
+ return impl->next_user_process_id++;
}
Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 6b8738599..0cc44ee76 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -96,7 +96,10 @@ private:
u32 CreateNewObjectID();
/// Creates a new process ID, incrementing the internal process ID counter;
- u64 CreateNewProcessID();
+ u64 CreateNewKernelProcessID();
+
+ /// Creates a new process ID, incrementing the internal process ID counter;
+ u64 CreateNewUserProcessID();
/// Creates a new thread ID, incrementing the internal thread ID counter.
u64 CreateNewThreadID();
diff --git a/src/core/hle/kernel/physical_memory.h b/src/core/hle/kernel/physical_memory.h
new file mode 100644
index 000000000..090565310
--- /dev/null
+++ b/src/core/hle/kernel/physical_memory.h
@@ -0,0 +1,19 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/alignment.h"
+
+namespace Kernel {
+
+// This encapsulation serves 2 purposes:
+// - First, to encapsulate host physical memory under a single type and set an
+// standard for managing it.
+// - Second to ensure all host backing memory used is aligned to 256 bytes due
+// to strict alignment restrictions on GPU memory.
+
+using PhysicalMemory = std::vector<u8, Common::AlignmentAllocator<u8, 256>>;
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 2b81a8d4f..e80a12ac3 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <bitset>
#include <memory>
#include <random>
#include "common/alignment.h"
@@ -48,7 +49,58 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
}
} // Anonymous namespace
-SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
+// Represents a page used for thread-local storage.
+//
+// Each TLS page contains slots that may be used by processes and threads.
+// Every process and thread is created with a slot in some arbitrary page
+// (whichever page happens to have an available slot).
+class TLSPage {
+public:
+ static constexpr std::size_t num_slot_entries = Memory::PAGE_SIZE / Memory::TLS_ENTRY_SIZE;
+
+ explicit TLSPage(VAddr address) : base_address{address} {}
+
+ bool HasAvailableSlots() const {
+ return !is_slot_used.all();
+ }
+
+ VAddr GetBaseAddress() const {
+ return base_address;
+ }
+
+ std::optional<VAddr> ReserveSlot() {
+ for (std::size_t i = 0; i < is_slot_used.size(); i++) {
+ if (is_slot_used[i]) {
+ continue;
+ }
+
+ is_slot_used[i] = true;
+ return base_address + (i * Memory::TLS_ENTRY_SIZE);
+ }
+
+ return std::nullopt;
+ }
+
+ void ReleaseSlot(VAddr address) {
+ // Ensure that all given addresses are consistent with how TLS pages
+ // are intended to be used when releasing slots.
+ ASSERT(IsWithinPage(address));
+ ASSERT((address % Memory::TLS_ENTRY_SIZE) == 0);
+
+ const std::size_t index = (address - base_address) / 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;
+ }
+
+ VAddr base_address;
+ std::bitset<num_slot_entries> is_slot_used;
+};
+
+SharedPtr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) {
auto& kernel = system.Kernel();
SharedPtr<Process> process(new Process(system));
@@ -56,7 +108,8 @@ SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
process->resource_limit = kernel.GetSystemResourceLimit();
process->status = ProcessStatus::Created;
process->program_id = 0;
- process->process_id = kernel.CreateNewProcessID();
+ process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
+ : kernel.CreateNewUserProcessID();
process->capabilities.InitializeForMetadatalessProcess();
std::mt19937 rng(Settings::values.rng_seed.value_or(0));
@@ -76,20 +129,17 @@ u64 Process::GetTotalPhysicalMemoryAvailable() const {
return vm_manager.GetTotalPhysicalMemoryAvailable();
}
-u64 Process::GetTotalPhysicalMemoryAvailableWithoutMmHeap() const {
- // TODO: Subtract the personal heap size from this when the
- // personal heap is implemented.
- return GetTotalPhysicalMemoryAvailable();
+u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
+ return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize();
}
u64 Process::GetTotalPhysicalMemoryUsed() const {
- return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
+ return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size +
+ GetSystemResourceUsage();
}
-u64 Process::GetTotalPhysicalMemoryUsedWithoutMmHeap() const {
- // TODO: Subtract the personal heap size from this when the
- // personal heap is implemented.
- return GetTotalPhysicalMemoryUsed();
+u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
+ return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
}
void Process::RegisterThread(const Thread* thread) {
@@ -119,6 +169,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
program_id = metadata.GetTitleID();
ideal_core = metadata.GetMainThreadCore();
is_64bit_process = metadata.Is64BitProgram();
+ system_resource_size = metadata.GetSystemResourceSize();
vm_manager.Reset(metadata.GetAddressSpaceType());
@@ -133,19 +184,11 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
}
void Process::Run(s32 main_thread_priority, u64 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
- // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
- // of the user address space.
- const VAddr mapping_address = vm_manager.GetTLSIORegionEndAddress() - main_thread_stack_size;
- vm_manager
- .MapMemoryBlock(mapping_address, std::make_shared<std::vector<u8>>(main_thread_stack_size),
- 0, main_thread_stack_size, MemoryState::Stack)
- .Unwrap();
+ AllocateMainThreadStack(stack_size);
+ tls_region_address = CreateTLSRegion();
vm_manager.LogLayout();
+
ChangeStatus(ProcessStatus::Running);
SetupMainThread(*this, kernel, main_thread_priority);
@@ -175,69 +218,66 @@ void Process::PrepareForTermination() {
stop_threads(system.Scheduler(2).GetThreadList());
stop_threads(system.Scheduler(3).GetThreadList());
+ FreeTLSRegion(tls_region_address);
+ tls_region_address = 0;
+
ChangeStatus(ProcessStatus::Exited);
}
/**
- * Finds a free location for the TLS section of a thread.
- * @param tls_slots The TLS page array of the thread's owner process.
- * Returns a tuple of (page, slot, alloc_needed) where:
- * page: The index of the first allocated TLS page that has free slots.
- * slot: The index of the first free slot in the indicated page.
- * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
+ * Attempts to find a TLS page that contains a free slot for
+ * use by a thread.
+ *
+ * @returns If a page with an available slot is found, then an iterator
+ * pointing to the page is returned. Otherwise the end iterator
+ * is returned instead.
*/
-static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot(
- const std::vector<std::bitset<8>>& tls_slots) {
- // Iterate over all the allocated pages, and try to find one where not all slots are used.
- for (std::size_t page = 0; page < tls_slots.size(); ++page) {
- const auto& page_tls_slots = tls_slots[page];
- if (!page_tls_slots.all()) {
- // We found a page with at least one free slot, find which slot it is
- for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
- if (!page_tls_slots.test(slot)) {
- return std::make_tuple(page, slot, false);
- }
- }
- }
- }
-
- return std::make_tuple(0, 0, true);
+static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
+ return std::find_if(tls_pages.begin(), tls_pages.end(),
+ [](const auto& page) { return page.HasAvailableSlots(); });
}
-VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) {
- auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots);
- const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress();
+VAddr Process::CreateTLSRegion() {
+ auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages);
- if (needs_allocation) {
- tls_slots.emplace_back(0); // The page is completely available at the start
- available_page = tls_slots.size() - 1;
- available_slot = 0; // Use the first slot in the new page
+ 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());
- // Allocate some memory from the end of the linear heap for this region.
- auto& tls_memory = thread.GetTLSMemory();
- tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0);
+ 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());
- vm_manager.RefreshMemoryBlockMappings(tls_memory.get());
+ tls_pages.emplace_back(*region_address);
- vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0,
- Memory::PAGE_SIZE, MemoryState::ThreadLocal);
- }
+ const auto reserve_result = tls_pages.back().ReserveSlot();
+ ASSERT(reserve_result.has_value());
- tls_slots[available_page].set(available_slot);
+ return *reserve_result;
+ }
- return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE;
+ return *tls_page_iter->ReserveSlot();
}
-void Process::FreeTLSSlot(VAddr tls_address) {
- const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress();
- const VAddr tls_page = tls_base / Memory::PAGE_SIZE;
- const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
+void Process::FreeTLSRegion(VAddr tls_address) {
+ const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE);
+ auto iter =
+ std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
+ return page.GetBaseAddress() == aligned_address;
+ });
- tls_slots[tls_page].reset(tls_slot);
+ // Something has gone very wrong if we're freeing a region
+ // with no actual page available.
+ ASSERT(iter != tls_pages.cend());
+
+ iter->ReleaseSlot(tls_address);
}
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
- const auto memory = std::make_shared<std::vector<u8>>(std::move(module_.memory));
+ const auto memory = std::make_shared<PhysicalMemory>(std::move(module_.memory));
const auto MapSegment = [&](const CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
@@ -280,4 +320,16 @@ void Process::ChangeStatus(ProcessStatus new_status) {
WakeupAllWaitingThreads();
}
+void Process::AllocateMainThreadStack(u64 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();
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 29e016983..c2df451f3 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -5,7 +5,6 @@
#pragma once
#include <array>
-#include <bitset>
#include <cstddef>
#include <list>
#include <string>
@@ -32,6 +31,7 @@ namespace Kernel {
class KernelCore;
class ResourceLimit;
class Thread;
+class TLSPage;
struct CodeSet;
@@ -73,9 +73,15 @@ public:
ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
};
+ // Used to determine how process IDs are assigned.
+ enum class ProcessType {
+ KernelInternal,
+ Userland,
+ };
+
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
- static SharedPtr<Process> Create(Core::System& system, std::string name);
+ static SharedPtr<Process> Create(Core::System& system, std::string name, ProcessType type);
std::string GetTypeName() const override {
return "Process";
@@ -129,6 +135,11 @@ public:
return mutex;
}
+ /// Gets the address to the process' dedicated TLS region.
+ VAddr GetTLSRegionAddress() const {
+ return tls_region_address;
+ }
+
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -162,8 +173,24 @@ public:
return capabilities.GetPriorityMask();
}
- u32 IsVirtualMemoryEnabled() const {
- return is_virtual_address_memory_enabled;
+ /// Gets the amount of secure memory to allocate for memory management.
+ u32 GetSystemResourceSize() const {
+ return system_resource_size;
+ }
+
+ /// Gets the amount of secure memory currently in use for memory management.
+ u32 GetSystemResourceUsage() const {
+ // On hardware, this returns the amount of system resource memory that has
+ // been used by the kernel. This is problematic for Yuzu to emulate, because
+ // system resource memory is used for page tables -- and yuzu doesn't really
+ // have a way to calculate how much memory is required for page tables for
+ // the current process at any given time.
+ // TODO: Is this even worth implementing? Games may retrieve this value via
+ // an SDK function that gets used + available system resource size for debug
+ // or diagnostic purposes. However, it seems unlikely that a game would make
+ // decisions based on how much system memory is dedicated to its page tables.
+ // Is returning a value other than zero wise?
+ return 0;
}
/// Whether this process is an AArch64 or AArch32 process.
@@ -190,15 +217,15 @@ public:
u64 GetTotalPhysicalMemoryAvailable() const;
/// Retrieves the total physical memory available to this process in bytes,
- /// without the size of the personal heap added to it.
- u64 GetTotalPhysicalMemoryAvailableWithoutMmHeap() const;
+ /// without the size of the personal system resource heap added to it.
+ u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const;
/// Retrieves the total physical memory used by this process in bytes.
u64 GetTotalPhysicalMemoryUsed() const;
/// Retrieves the total physical memory used by this process in bytes,
- /// without the size of the personal heap added to it.
- u64 GetTotalPhysicalMemoryUsedWithoutMmHeap() const;
+ /// without the size of the personal system resource heap added to it.
+ u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
/// Gets the list of all threads created with this process as their owner.
const std::list<const Thread*>& GetThreadList() const {
@@ -254,10 +281,10 @@ public:
// Thread-local storage management
// Marks the next available region as used and returns the address of the slot.
- VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
+ [[nodiscard]] VAddr CreateTLSRegion();
// Frees a used TLS slot identified by the given address
- void FreeTLSSlot(VAddr tls_address);
+ void FreeTLSRegion(VAddr tls_address);
private:
explicit Process(Core::System& system);
@@ -274,6 +301,9 @@ private:
/// a process signal.
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;
@@ -284,7 +314,7 @@ private:
u64 code_memory_size = 0;
/// Current status of the process
- ProcessStatus status;
+ ProcessStatus status{};
/// The ID of this process
u64 process_id = 0;
@@ -292,19 +322,23 @@ private:
/// Title ID corresponding to the process
u64 program_id = 0;
+ /// Specifies additional memory to be reserved for the process's memory management by the
+ /// system. When this is non-zero, secure memory is allocated and used for page table allocation
+ /// instead of using the normal global page tables/memory block management.
+ u32 system_resource_size = 0;
+
/// Resource limit descriptor for this process
SharedPtr<ResourceLimit> resource_limit;
/// The ideal CPU core for this process, threads are scheduled on this core by default.
u8 ideal_core = 0;
- u32 is_virtual_address_memory_enabled = 0;
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
/// holds the TLS for a specific thread. This vector contains which parts are in use for each
/// page as a bitmask.
/// This vector will grow as more pages are allocated for new threads.
- std::vector<std::bitset<8>> tls_slots;
+ std::vector<TLSPage> tls_pages;
/// Contains the parsed process capability descriptors.
ProcessCapabilities capabilities;
@@ -332,8 +366,11 @@ private:
/// variable related facilities.
Mutex mutex;
+ /// Address indicating the location of the process' dedicated TLS region.
+ VAddr tls_region_address = 0;
+
/// Random values for svcGetInfo RandomEntropy
- std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
+ std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
/// List of threads that are running with this process as their owner.
std::list<const Thread*> thread_list;
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index f15c5ee36..a815c4eea 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -28,7 +28,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
shared_memory->other_permissions = other_permissions;
if (address == 0) {
- shared_memory->backing_block = std::make_shared<std::vector<u8>>(size);
+ shared_memory->backing_block = std::make_shared<Kernel::PhysicalMemory>(size);
shared_memory->backing_block_offset = 0;
// Refresh the address mappings for the current process.
@@ -59,8 +59,8 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_
}
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
- KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size,
- MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
+ KernelCore& kernel, std::shared_ptr<Kernel::PhysicalMemory> heap_block, std::size_t offset,
+ u64 size, MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
shared_memory->owner_process = nullptr;
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index c2b6155e1..01ca6dcd2 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -10,6 +10,7 @@
#include "common/common_types.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"
@@ -62,12 +63,10 @@ public:
* block.
* @param name Optional object name, used for debugging purposes.
*/
- static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
- std::shared_ptr<std::vector<u8>> heap_block,
- std::size_t offset, u64 size,
- MemoryPermission permissions,
- MemoryPermission other_permissions,
- std::string name = "Unknown Applet");
+ static SharedPtr<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");
std::string GetTypeName() const override {
return "SharedMemory";
@@ -135,7 +134,7 @@ private:
~SharedMemory() override;
/// Backing memory for this shared memory block.
- std::shared_ptr<std::vector<u8>> backing_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.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index f9c606bc5..1fd1a732a 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -38,6 +38,7 @@
#include "core/hle/result.h"
#include "core/hle/service/service.h"
#include "core/memory.h"
+#include "core/reporter.h"
namespace Kernel {
namespace {
@@ -97,9 +98,9 @@ ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_add
return ERR_INVALID_ADDRESS_STATE;
}
- if (!vm_manager.IsWithinNewMapRegion(dst_addr, size)) {
+ if (!vm_manager.IsWithinStackRegion(dst_addr, size)) {
LOG_ERROR(Kernel_SVC,
- "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
+ "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}",
dst_addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
@@ -317,7 +318,14 @@ static ResultCode UnmapMemory(Core::System& system, VAddr dst_addr, VAddr src_ad
return result;
}
- return vm_manager.UnmapRange(dst_addr, size);
+ 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;
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -594,6 +602,7 @@ struct BreakReason {
static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
bool has_dumped_buffer{};
+ std::vector<u8> debug_buffer;
const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
if (sz == 0 || addr == 0 || has_dumped_buffer) {
@@ -605,7 +614,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
} else {
// We don't know what's in here so we'll hexdump it
- std::vector<u8> debug_buffer(sz);
+ debug_buffer.resize(sz);
Memory::ReadBlock(addr, debug_buffer.data(), sz);
std::string hexdump;
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
@@ -664,6 +673,10 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
break;
}
+ system.GetReporter().SaveSvcBreakReport(
+ static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1,
+ info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
+
if (!break_reason.signal_debugger) {
LOG_CRITICAL(
Debug_Emulated,
@@ -720,19 +733,19 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
// 2.0.0+
ASLRRegionBaseAddr = 12,
ASLRRegionSize = 13,
- NewMapRegionBaseAddr = 14,
- NewMapRegionSize = 15,
+ StackRegionBaseAddr = 14,
+ StackRegionSize = 15,
// 3.0.0+
- IsVirtualAddressMemoryEnabled = 16,
- PersonalMmHeapUsage = 17,
+ SystemResourceSize = 16,
+ SystemResourceUsage = 17,
TitleId = 18,
// 4.0.0+
PrivilegedProcessId = 19,
// 5.0.0+
UserExceptionContextAddr = 20,
// 6.0.0+
- TotalPhysicalMemoryAvailableWithoutMmHeap = 21,
- TotalPhysicalMemoryUsedWithoutMmHeap = 22,
+ TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
+ TotalPhysicalMemoryUsedWithoutSystemResource = 22,
};
const auto info_id_type = static_cast<GetInfoType>(info_id);
@@ -746,16 +759,16 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
case GetInfoType::HeapRegionSize:
case GetInfoType::ASLRRegionBaseAddr:
case GetInfoType::ASLRRegionSize:
- case GetInfoType::NewMapRegionBaseAddr:
- case GetInfoType::NewMapRegionSize:
+ case GetInfoType::StackRegionBaseAddr:
+ case GetInfoType::StackRegionSize:
case GetInfoType::TotalPhysicalMemoryAvailable:
case GetInfoType::TotalPhysicalMemoryUsed:
- case GetInfoType::IsVirtualAddressMemoryEnabled:
- case GetInfoType::PersonalMmHeapUsage:
+ case GetInfoType::SystemResourceSize:
+ case GetInfoType::SystemResourceUsage:
case GetInfoType::TitleId:
case GetInfoType::UserExceptionContextAddr:
- case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
- case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: {
+ case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
+ case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: {
if (info_sub_id != 0) {
return ERR_INVALID_ENUM_VALUE;
}
@@ -800,12 +813,12 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
*result = process->VMManager().GetASLRRegionSize();
return RESULT_SUCCESS;
- case GetInfoType::NewMapRegionBaseAddr:
- *result = process->VMManager().GetNewMapRegionBaseAddress();
+ case GetInfoType::StackRegionBaseAddr:
+ *result = process->VMManager().GetStackRegionBaseAddress();
return RESULT_SUCCESS;
- case GetInfoType::NewMapRegionSize:
- *result = process->VMManager().GetNewMapRegionSize();
+ case GetInfoType::StackRegionSize:
+ *result = process->VMManager().GetStackRegionSize();
return RESULT_SUCCESS;
case GetInfoType::TotalPhysicalMemoryAvailable:
@@ -816,8 +829,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
*result = process->GetTotalPhysicalMemoryUsed();
return RESULT_SUCCESS;
- case GetInfoType::IsVirtualAddressMemoryEnabled:
- *result = process->IsVirtualMemoryEnabled();
+ case GetInfoType::SystemResourceSize:
+ *result = process->GetSystemResourceSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::SystemResourceUsage:
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage");
+ *result = process->GetSystemResourceUsage();
return RESULT_SUCCESS;
case GetInfoType::TitleId:
@@ -825,17 +843,15 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
return RESULT_SUCCESS;
case GetInfoType::UserExceptionContextAddr:
- LOG_WARNING(Kernel_SVC,
- "(STUBBED) Attempted to query user exception context address, returned 0");
- *result = 0;
+ *result = process->GetTLSRegionAddress();
return RESULT_SUCCESS;
- case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
- *result = process->GetTotalPhysicalMemoryAvailable();
+ case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
+ *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
return RESULT_SUCCESS;
- case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap:
- *result = process->GetTotalPhysicalMemoryUsedWithoutMmHeap();
+ case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource:
+ *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
return RESULT_SUCCESS;
default:
@@ -940,6 +956,86 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
}
}
+/// Maps memory at a desired address
+static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+ LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
+
+ if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is zero");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!(addr < addr + size)) {
+ LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ Process* const current_process = system.Kernel().CurrentProcess();
+ auto& vm_manager = current_process->VMManager();
+
+ 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");
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return vm_manager.MapPhysicalMemory(addr, size);
+}
+
+/// Unmaps memory previously mapped via MapPhysicalMemory
+static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size) {
+ LOG_DEBUG(Kernel_SVC, "called, addr=0x{:016X}, size=0x{:X}", addr, size);
+
+ if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, 0x{:016X}", addr);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:X}", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is zero");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!(addr < addr + size)) {
+ LOG_ERROR(Kernel_SVC, "Size causes 64-bit overflow of address");
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ Process* const current_process = system.Kernel().CurrentProcess();
+ auto& vm_manager = current_process->VMManager();
+
+ 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");
+ return ERR_INVALID_MEMORY_RANGE;
+ }
+
+ return vm_manager.UnmapPhysicalMemory(addr, size);
+}
+
/// Sets the thread activity
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
@@ -1641,8 +1737,8 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
// Wait for an address (via Address Arbiter)
static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value,
s64 timeout) {
- LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}",
- address, type, value, timeout);
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address,
+ type, value, timeout);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
@@ -1664,8 +1760,8 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
// Signals to an address (via Address Arbiter)
static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value,
s32 num_to_wake) {
- LOG_WARNING(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
- address, type, value, num_to_wake);
+ LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}",
+ address, type, value, num_to_wake);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
@@ -2297,8 +2393,8 @@ static const FunctionDef SVC_Table[] = {
{0x29, SvcWrap<GetInfo>, "GetInfo"},
{0x2A, nullptr, "FlushEntireDataCache"},
{0x2B, nullptr, "FlushDataCache"},
- {0x2C, nullptr, "MapPhysicalMemory"},
- {0x2D, nullptr, "UnmapPhysicalMemory"},
+ {0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"},
+ {0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
{0x2E, nullptr, "GetFutureThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 865473c6f..c2d8d0dc3 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -32,6 +32,11 @@ void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, Param(system, 0)).raw);
}
+template <ResultCode func(Core::System&, u64, u64)>
+void SvcWrap(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw);
+}
+
template <ResultCode func(Core::System&, u32)>
void SvcWrap(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c73a40977..ec529e7f2 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -65,7 +65,7 @@ void Thread::Stop() {
owner_process->UnregisterThread(this);
// Mark the TLS slot in the thread's page as free.
- owner_process->FreeTLSSlot(tls_address);
+ owner_process->FreeTLSRegion(tls_address);
}
void Thread::WakeAfterDelay(s64 nanoseconds) {
@@ -76,13 +76,13 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
// This function might be called from any thread so we have to be cautious and use the
// thread-safe version of ScheduleEvent.
const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
- Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe(
+ Core::System::GetInstance().CoreTiming().ScheduleEvent(
cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
void Thread::CancelWakeupTimer() {
- Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe(
- kernel.ThreadWakeupCallbackEventType(), callback_handle);
+ Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
+ callback_handle);
}
static std::optional<s32> GetNextProcessorId(u64 mask) {
@@ -205,9 +205,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->name = std::move(name);
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = &owner_process;
+ thread->tls_address = thread->owner_process->CreateTLSRegion();
thread->scheduler = &system.Scheduler(processor_id);
thread->scheduler->AddThread(thread);
- thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
thread->owner_process->RegisterThread(thread.get());
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index b4b9cda7c..07e989637 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -5,7 +5,6 @@
#pragma once
#include <functional>
-#include <memory>
#include <string>
#include <vector>
@@ -78,9 +77,6 @@ enum class ThreadActivity : u32 {
class Thread final : public WaitObject {
public:
- using TLSMemory = std::vector<u8>;
- using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
-
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
using ThreadContext = Core::ARM_Interface::ThreadContext;
@@ -169,14 +165,6 @@ public:
return thread_id;
}
- TLSMemoryPtr& GetTLSMemory() {
- return tls_memory;
- }
-
- const TLSMemoryPtr& GetTLSMemory() const {
- return tls_memory;
- }
-
/// Resumes a thread from waiting
void ResumeFromWait();
@@ -463,11 +451,9 @@ private:
u32 ideal_core{0xFFFFFFFF};
u64 affinity_mask{0x1};
- TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
+ ThreadActivity activity = ThreadActivity::Normal;
std::string name;
-
- ThreadActivity activity = ThreadActivity::Normal;
};
/**
diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp
index 26c4e5e67..1113c815e 100644
--- a/src/core/hle/kernel/transfer_memory.cpp
+++ b/src/core/hle/kernel/transfer_memory.cpp
@@ -47,7 +47,7 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p
return ERR_INVALID_STATE;
}
- backing_block = std::make_shared<std::vector<u8>>(size);
+ backing_block = std::make_shared<PhysicalMemory>(size);
const auto map_state = owner_permissions == MemoryPermission::None
? MemoryState::TransferMemoryIsolated
diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h
index a140b1e2b..6be9dc094 100644
--- a/src/core/hle/kernel/transfer_memory.h
+++ b/src/core/hle/kernel/transfer_memory.h
@@ -8,6 +8,7 @@
#include <vector>
#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/physical_memory.h"
union ResultCode;
@@ -82,7 +83,7 @@ private:
~TransferMemory() override;
/// Memory block backing this instance.
- std::shared_ptr<std::vector<u8>> backing_block;
+ std::shared_ptr<PhysicalMemory> backing_block;
/// The base address for the memory managed by this instance.
VAddr base_address = 0;
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 6d6980aba..c7af87073 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -5,13 +5,15 @@
#include <algorithm>
#include <iterator>
#include <utility>
+#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/memory_hook.h"
-#include "core/arm/arm_interface.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"
#include "core/memory_setup.h"
@@ -49,10 +51,14 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
type != next.type) {
return false;
}
- if (type == VMAType::AllocatedMemoryBlock &&
- (backing_block != next.backing_block || offset + size != next.offset)) {
+ 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;
}
@@ -68,9 +74,7 @@ VMManager::VMManager(Core::System& system) : system{system} {
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
}
-VMManager::~VMManager() {
- Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
-}
+VMManager::~VMManager() = default;
void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
Clear();
@@ -100,9 +104,9 @@ bool VMManager::IsValidHandle(VMAHandle handle) const {
}
ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
- std::shared_ptr<std::vector<u8>> block,
+ std::shared_ptr<PhysicalMemory> block,
std::size_t offset, u64 size,
- MemoryState state) {
+ MemoryState state, VMAPermission perm) {
ASSERT(block != nullptr);
ASSERT(offset + size <= block->size());
@@ -111,17 +115,8 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
- system.ArmInterface(0).MapBackingMemory(target, size, block->data() + offset,
- VMAPermission::ReadWriteExecute);
- system.ArmInterface(1).MapBackingMemory(target, size, block->data() + offset,
- VMAPermission::ReadWriteExecute);
- system.ArmInterface(2).MapBackingMemory(target, size, block->data() + offset,
- VMAPermission::ReadWriteExecute);
- system.ArmInterface(3).MapBackingMemory(target, size, block->data() + offset,
- VMAPermission::ReadWriteExecute);
-
final_vma.type = VMAType::AllocatedMemoryBlock;
- final_vma.permissions = VMAPermission::ReadWrite;
+ final_vma.permissions = perm;
final_vma.state = state;
final_vma.backing_block = std::move(block);
final_vma.offset = offset;
@@ -139,11 +134,6 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
VirtualMemoryArea& final_vma = vma_handle->second;
ASSERT(final_vma.size == size);
- system.ArmInterface(0).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
- system.ArmInterface(1).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
- system.ArmInterface(2).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
- system.ArmInterface(3).MapBackingMemory(target, size, memory, VMAPermission::ReadWriteExecute);
-
final_vma.type = VMAType::BackingMemory;
final_vma.permissions = VMAPermission::ReadWrite;
final_vma.state = state;
@@ -154,22 +144,33 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
}
ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
- // Find the first Free VMA.
- const VAddr base = GetASLRRegionBaseAddress();
- const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
- if (vma.second.type != VMAType::Free)
- return false;
+ return FindFreeRegion(GetASLRRegionBaseAddress(), GetASLRRegionEndAddress(), size);
+}
- const VAddr vma_end = vma.second.base + vma.second.size;
- return vma_end > base && vma_end >= base + size;
- });
+ResultVal<VAddr> VMManager::FindFreeRegion(VAddr begin, VAddr end, u64 size) const {
+ ASSERT(begin < end);
+ ASSERT(size <= end - begin);
- if (vma_handle == vma_map.end()) {
+ 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 ResultCode(-1);
}
- const VAddr target = std::max(base, vma_handle->second.base);
+ const VAddr target = std::max(begin, vma_handle->second.base);
return MakeResult<VAddr>(target);
}
@@ -221,11 +222,6 @@ ResultCode VMManager::UnmapRange(VAddr target, u64 size) {
ASSERT(FindVMA(target)->second.size >= size);
- system.ArmInterface(0).UnmapMemory(target, size);
- system.ArmInterface(1).UnmapMemory(target, size);
- system.ArmInterface(2).UnmapMemory(target, size);
- system.ArmInterface(3).UnmapMemory(target, size);
-
return RESULT_SUCCESS;
}
@@ -265,7 +261,7 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
if (heap_memory == nullptr) {
// Initialize heap
- heap_memory = std::make_shared<std::vector<u8>>(size);
+ heap_memory = std::make_shared<PhysicalMemory>(size);
heap_end = heap_region_base + size;
} else {
UnmapRange(heap_region_base, GetCurrentHeapSize());
@@ -299,6 +295,162 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
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(
@@ -438,7 +590,7 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
ASSERT_MSG(vma_offset + size <= vma->second.size,
"Shared memory exceeds bounds of mapped block");
- const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_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,
@@ -446,12 +598,12 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
// Protect mirror with permissions from old region
Reprotect(new_vma, vma->second.permissions);
// Remove permissions from old region
- Reprotect(vma, VMAPermission::None);
+ ReprotectRange(src_addr, size, VMAPermission::None);
return RESULT_SUCCESS;
}
-void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
+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) {
@@ -579,14 +731,14 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_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)) {
- iter->second.size += next_vma->second.size;
+ 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)) {
- prev_vma->second.size += iter->second.size;
+ MergeAdjacentVMA(prev_vma->second, iter->second);
vma_map.erase(iter);
iter = prev_vma;
}
@@ -595,6 +747,44 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
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)) {
+ const auto right_begin = right.backing_block->begin() + right.offset;
+ const auto right_end = right_begin + right.size;
+
+ // 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->insert(left.backing_block->end(), right_begin, right_end);
+ } else {
+ // Slow case: make a new memory block for left and right.
+ const auto left_begin = left.backing_block->begin() + left.offset;
+ const auto left_end = left_begin + left.size;
+ const auto left_size = static_cast<std::size_t>(std::distance(left_begin, left_end));
+ const auto right_size = static_cast<std::size_t>(std::distance(right_begin, right_end));
+
+ auto new_memory = std::make_shared<PhysicalMemory>();
+ new_memory->reserve(left_size + right_size);
+ new_memory->insert(new_memory->end(), left_begin, left_end);
+ new_memory->insert(new_memory->end(), right_begin, right_end);
+
+ 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) {
switch (vma.type) {
case VMAType::Free:
@@ -616,9 +806,11 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) {
u64 map_region_size = 0;
u64 heap_region_size = 0;
- u64 new_map_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:
@@ -634,6 +826,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
map_region_size = 0;
heap_region_size = 0x80000000;
}
+ stack_and_tls_io_end = 0x40000000;
break;
case FileSys::ProgramAddressSpaceType::Is36Bit:
address_space_width = 36;
@@ -643,6 +836,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
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;
@@ -652,7 +846,7 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
aslr_region_end = aslr_region_base + 0x7FF8000000;
map_region_size = 0x1000000000;
heap_region_size = 0x180000000;
- new_map_region_size = 0x80000000;
+ stack_region_size = 0x80000000;
tls_io_region_size = 0x1000000000;
break;
default:
@@ -660,6 +854,8 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
return;
}
+ const u64 stack_and_tls_io_begin = aslr_region_base;
+
address_space_base = 0;
address_space_end = 1ULL << address_space_width;
@@ -670,15 +866,20 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
heap_region_end = heap_region_base + heap_region_size;
heap_end = heap_region_base;
- new_map_region_base = heap_region_end;
- new_map_region_end = new_map_region_base + new_map_region_size;
+ stack_region_base = heap_region_end;
+ stack_region_end = stack_region_base + stack_region_size;
- tls_io_region_base = new_map_region_end;
+ tls_io_region_base = stack_region_end;
tls_io_region_end = tls_io_region_base + tls_io_region_size;
- if (new_map_region_size == 0) {
- new_map_region_base = address_space_base;
- new_map_region_end = address_space_end;
+ 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;
}
}
@@ -758,6 +959,84 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo
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;
@@ -870,21 +1149,21 @@ bool VMManager::IsWithinMapRegion(VAddr address, u64 size) const {
return IsInsideAddressRange(address, size, GetMapRegionBaseAddress(), GetMapRegionEndAddress());
}
-VAddr VMManager::GetNewMapRegionBaseAddress() const {
- return new_map_region_base;
+VAddr VMManager::GetStackRegionBaseAddress() const {
+ return stack_region_base;
}
-VAddr VMManager::GetNewMapRegionEndAddress() const {
- return new_map_region_end;
+VAddr VMManager::GetStackRegionEndAddress() const {
+ return stack_region_end;
}
-u64 VMManager::GetNewMapRegionSize() const {
- return new_map_region_end - new_map_region_base;
+u64 VMManager::GetStackRegionSize() const {
+ return stack_region_end - stack_region_base;
}
-bool VMManager::IsWithinNewMapRegion(VAddr address, u64 size) const {
- return IsInsideAddressRange(address, size, GetNewMapRegionBaseAddress(),
- GetNewMapRegionEndAddress());
+bool VMManager::IsWithinStackRegion(VAddr address, u64 size) const {
+ return IsInsideAddressRange(address, size, GetStackRegionBaseAddress(),
+ GetStackRegionEndAddress());
}
VAddr VMManager::GetTLSIORegionBaseAddress() const {
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index dfbf7a894..850a7ebc3 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -11,6 +11,7 @@
#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"
@@ -290,7 +291,7 @@ struct VirtualMemoryArea {
// Settings for type = AllocatedMemoryBlock
/// Memory block backing this VMA.
- std::shared_ptr<std::vector<u8>> backing_block = nullptr;
+ std::shared_ptr<PhysicalMemory> backing_block = nullptr;
/// Offset into the backing_memory the mapping starts from.
std::size_t offset = 0;
@@ -348,8 +349,9 @@ public:
* @param size Size of the mapping.
* @param state MemoryState tag to attach to the VMA.
*/
- ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block,
- std::size_t offset, u64 size, MemoryState state);
+ 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.
@@ -362,14 +364,39 @@ public:
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
/**
- * Finds the first free address that can hold a region of the desired size.
+ * Finds the first free memory region of the given size within
+ * the user-addressable ASLR memory region.
*
- * @param size Size of the desired region.
- * @return The found free address.
+ * @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.
@@ -425,6 +452,34 @@ public:
///
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.
@@ -493,7 +548,7 @@ public:
* 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 std::vector<u8>* block);
+ void RefreshMemoryBlockMappings(const PhysicalMemory* block);
/// Dumps the address space layout to the log, for debugging
void LogLayout() const;
@@ -571,17 +626,17 @@ public:
/// 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 new map region.
- VAddr GetNewMapRegionBaseAddress() const;
+ /// Gets the base address of the stack region.
+ VAddr GetStackRegionBaseAddress() const;
- /// Gets the end address of the new map region.
- VAddr GetNewMapRegionEndAddress() const;
+ /// Gets the end address of the stack region.
+ VAddr GetStackRegionEndAddress() const;
- /// Gets the total size of the new map region in bytes.
- u64 GetNewMapRegionSize() 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 new map region
- bool IsWithinNewMapRegion(VAddr address, u64 size) 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;
@@ -632,6 +687,11 @@ private:
*/
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);
@@ -676,6 +736,13 @@ private:
MemoryAttribute attribute_mask, MemoryAttribute attribute,
MemoryAttribute ignore_mask) const;
+ /// 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
@@ -701,8 +768,8 @@ private:
VAddr map_region_base = 0;
VAddr map_region_end = 0;
- VAddr new_map_region_base = 0;
- VAddr new_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;
@@ -711,12 +778,17 @@ private:
// 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<std::vector<u8>> heap_memory;
+ 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