mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #2690 from SciresM/physmem_fixes
Implement MapPhysicalMemory/UnmapPhysicalMemory
This commit is contained in:
commit
4882c058fd
|
@ -94,6 +94,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const {
|
||||||
return aci_file_access.permissions;
|
return aci_file_access.permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 ProgramMetadata::GetSystemResourceSize() const {
|
||||||
|
return npdm_header.system_resource_size;
|
||||||
|
}
|
||||||
|
|
||||||
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
|
const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const {
|
||||||
return aci_kernel_capabilities;
|
return aci_kernel_capabilities;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
u32 GetMainThreadStackSize() const;
|
u32 GetMainThreadStackSize() const;
|
||||||
u64 GetTitleID() const;
|
u64 GetTitleID() const;
|
||||||
u64 GetFilesystemPermissions() const;
|
u64 GetFilesystemPermissions() const;
|
||||||
|
u32 GetSystemResourceSize() const;
|
||||||
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
const KernelCapabilityDescriptors& GetKernelCapabilities() const;
|
||||||
|
|
||||||
void Print() const;
|
void Print() const;
|
||||||
|
@ -76,7 +77,8 @@ private:
|
||||||
u8 reserved_3;
|
u8 reserved_3;
|
||||||
u8 main_thread_priority;
|
u8 main_thread_priority;
|
||||||
u8 main_thread_cpu;
|
u8 main_thread_cpu;
|
||||||
std::array<u8, 8> reserved_4;
|
std::array<u8, 4> reserved_4;
|
||||||
|
u32_le system_resource_size;
|
||||||
u32_le process_category;
|
u32_le process_category;
|
||||||
u32_le main_stack_size;
|
u32_le main_stack_size;
|
||||||
std::array<u8, 0x10> application_name;
|
std::array<u8, 0x10> application_name;
|
||||||
|
|
|
@ -129,20 +129,17 @@ u64 Process::GetTotalPhysicalMemoryAvailable() const {
|
||||||
return vm_manager.GetTotalPhysicalMemoryAvailable();
|
return vm_manager.GetTotalPhysicalMemoryAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Process::GetTotalPhysicalMemoryAvailableWithoutMmHeap() const {
|
u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const {
|
||||||
// TODO: Subtract the personal heap size from this when the
|
return GetTotalPhysicalMemoryAvailable() - GetSystemResourceSize();
|
||||||
// personal heap is implemented.
|
|
||||||
return GetTotalPhysicalMemoryAvailable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Process::GetTotalPhysicalMemoryUsed() const {
|
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 {
|
u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
|
||||||
// TODO: Subtract the personal heap size from this when the
|
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
|
||||||
// personal heap is implemented.
|
|
||||||
return GetTotalPhysicalMemoryUsed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::RegisterThread(const Thread* thread) {
|
void Process::RegisterThread(const Thread* thread) {
|
||||||
|
@ -172,6 +169,7 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
|
||||||
program_id = metadata.GetTitleID();
|
program_id = metadata.GetTitleID();
|
||||||
ideal_core = metadata.GetMainThreadCore();
|
ideal_core = metadata.GetMainThreadCore();
|
||||||
is_64bit_process = metadata.Is64BitProgram();
|
is_64bit_process = metadata.Is64BitProgram();
|
||||||
|
system_resource_size = metadata.GetSystemResourceSize();
|
||||||
|
|
||||||
vm_manager.Reset(metadata.GetAddressSpaceType());
|
vm_manager.Reset(metadata.GetAddressSpaceType());
|
||||||
|
|
||||||
|
|
|
@ -168,8 +168,24 @@ public:
|
||||||
return capabilities.GetPriorityMask();
|
return capabilities.GetPriorityMask();
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 IsVirtualMemoryEnabled() const {
|
/// Gets the amount of secure memory to allocate for memory management.
|
||||||
return is_virtual_address_memory_enabled;
|
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.
|
/// Whether this process is an AArch64 or AArch32 process.
|
||||||
|
@ -196,15 +212,15 @@ public:
|
||||||
u64 GetTotalPhysicalMemoryAvailable() const;
|
u64 GetTotalPhysicalMemoryAvailable() const;
|
||||||
|
|
||||||
/// Retrieves the total physical memory available to this process in bytes,
|
/// Retrieves the total physical memory available to this process in bytes,
|
||||||
/// without the size of the personal heap added to it.
|
/// without the size of the personal system resource heap added to it.
|
||||||
u64 GetTotalPhysicalMemoryAvailableWithoutMmHeap() const;
|
u64 GetTotalPhysicalMemoryAvailableWithoutSystemResource() const;
|
||||||
|
|
||||||
/// Retrieves the total physical memory used by this process in bytes.
|
/// Retrieves the total physical memory used by this process in bytes.
|
||||||
u64 GetTotalPhysicalMemoryUsed() const;
|
u64 GetTotalPhysicalMemoryUsed() const;
|
||||||
|
|
||||||
/// Retrieves the total physical memory used by this process in bytes,
|
/// Retrieves the total physical memory used by this process in bytes,
|
||||||
/// without the size of the personal heap added to it.
|
/// without the size of the personal system resource heap added to it.
|
||||||
u64 GetTotalPhysicalMemoryUsedWithoutMmHeap() const;
|
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
|
||||||
|
|
||||||
/// Gets the list of all threads created with this process as their owner.
|
/// Gets the list of all threads created with this process as their owner.
|
||||||
const std::list<const Thread*>& GetThreadList() const {
|
const std::list<const Thread*>& GetThreadList() const {
|
||||||
|
@ -298,12 +314,16 @@ private:
|
||||||
/// Title ID corresponding to the process
|
/// Title ID corresponding to the process
|
||||||
u64 program_id = 0;
|
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
|
/// Resource limit descriptor for this process
|
||||||
SharedPtr<ResourceLimit> resource_limit;
|
SharedPtr<ResourceLimit> resource_limit;
|
||||||
|
|
||||||
/// The ideal CPU core for this process, threads are scheduled on this core by default.
|
/// The ideal CPU core for this process, threads are scheduled on this core by default.
|
||||||
u8 ideal_core = 0;
|
u8 ideal_core = 0;
|
||||||
u32 is_virtual_address_memory_enabled = 0;
|
|
||||||
|
|
||||||
/// The Thread Local Storage area is allocated as processes create threads,
|
/// 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
|
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
|
||||||
|
|
|
@ -736,16 +736,16 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||||
StackRegionBaseAddr = 14,
|
StackRegionBaseAddr = 14,
|
||||||
StackRegionSize = 15,
|
StackRegionSize = 15,
|
||||||
// 3.0.0+
|
// 3.0.0+
|
||||||
IsVirtualAddressMemoryEnabled = 16,
|
SystemResourceSize = 16,
|
||||||
PersonalMmHeapUsage = 17,
|
SystemResourceUsage = 17,
|
||||||
TitleId = 18,
|
TitleId = 18,
|
||||||
// 4.0.0+
|
// 4.0.0+
|
||||||
PrivilegedProcessId = 19,
|
PrivilegedProcessId = 19,
|
||||||
// 5.0.0+
|
// 5.0.0+
|
||||||
UserExceptionContextAddr = 20,
|
UserExceptionContextAddr = 20,
|
||||||
// 6.0.0+
|
// 6.0.0+
|
||||||
TotalPhysicalMemoryAvailableWithoutMmHeap = 21,
|
TotalPhysicalMemoryAvailableWithoutSystemResource = 21,
|
||||||
TotalPhysicalMemoryUsedWithoutMmHeap = 22,
|
TotalPhysicalMemoryUsedWithoutSystemResource = 22,
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
||||||
|
@ -763,12 +763,12 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||||
case GetInfoType::StackRegionSize:
|
case GetInfoType::StackRegionSize:
|
||||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||||
case GetInfoType::TotalPhysicalMemoryUsed:
|
case GetInfoType::TotalPhysicalMemoryUsed:
|
||||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
case GetInfoType::SystemResourceSize:
|
||||||
case GetInfoType::PersonalMmHeapUsage:
|
case GetInfoType::SystemResourceUsage:
|
||||||
case GetInfoType::TitleId:
|
case GetInfoType::TitleId:
|
||||||
case GetInfoType::UserExceptionContextAddr:
|
case GetInfoType::UserExceptionContextAddr:
|
||||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
|
case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
|
||||||
case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: {
|
case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: {
|
||||||
if (info_sub_id != 0) {
|
if (info_sub_id != 0) {
|
||||||
return ERR_INVALID_ENUM_VALUE;
|
return ERR_INVALID_ENUM_VALUE;
|
||||||
}
|
}
|
||||||
|
@ -829,8 +829,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||||
*result = process->GetTotalPhysicalMemoryUsed();
|
*result = process->GetTotalPhysicalMemoryUsed();
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
||||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
case GetInfoType::SystemResourceSize:
|
||||||
*result = process->IsVirtualMemoryEnabled();
|
*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;
|
return RESULT_SUCCESS;
|
||||||
|
|
||||||
case GetInfoType::TitleId:
|
case GetInfoType::TitleId:
|
||||||
|
@ -843,12 +848,12 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||||
*result = 0;
|
*result = 0;
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
||||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
|
case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource:
|
||||||
*result = process->GetTotalPhysicalMemoryAvailable();
|
*result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource();
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
||||||
case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap:
|
case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource:
|
||||||
*result = process->GetTotalPhysicalMemoryUsedWithoutMmHeap();
|
*result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -953,6 +958,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
|
/// Sets the thread activity
|
||||||
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
|
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) {
|
||||||
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
|
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
|
||||||
|
@ -2310,8 +2395,8 @@ static const FunctionDef SVC_Table[] = {
|
||||||
{0x29, SvcWrap<GetInfo>, "GetInfo"},
|
{0x29, SvcWrap<GetInfo>, "GetInfo"},
|
||||||
{0x2A, nullptr, "FlushEntireDataCache"},
|
{0x2A, nullptr, "FlushEntireDataCache"},
|
||||||
{0x2B, nullptr, "FlushDataCache"},
|
{0x2B, nullptr, "FlushDataCache"},
|
||||||
{0x2C, nullptr, "MapPhysicalMemory"},
|
{0x2C, SvcWrap<MapPhysicalMemory>, "MapPhysicalMemory"},
|
||||||
{0x2D, nullptr, "UnmapPhysicalMemory"},
|
{0x2D, SvcWrap<UnmapPhysicalMemory>, "UnmapPhysicalMemory"},
|
||||||
{0x2E, nullptr, "GetFutureThreadInfo"},
|
{0x2E, nullptr, "GetFutureThreadInfo"},
|
||||||
{0x2F, nullptr, "GetLastThreadInfo"},
|
{0x2F, nullptr, "GetLastThreadInfo"},
|
||||||
{0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
|
{0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
|
||||||
|
|
|
@ -32,6 +32,11 @@ void SvcWrap(Core::System& system) {
|
||||||
FuncReturn(system, func(system, Param(system, 0)).raw);
|
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)>
|
template <ResultCode func(Core::System&, u32)>
|
||||||
void SvcWrap(Core::System& system) {
|
void SvcWrap(Core::System& system) {
|
||||||
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
|
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0))).raw);
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/program_metadata.h"
|
#include "core/file_sys/program_metadata.h"
|
||||||
#include "core/hle/kernel/errors.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/hle/kernel/vm_manager.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/memory_setup.h"
|
#include "core/memory_setup.h"
|
||||||
|
@ -48,10 +50,14 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
|
||||||
type != next.type) {
|
type != next.type) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (type == VMAType::AllocatedMemoryBlock &&
|
if ((attribute & MemoryAttribute::DeviceMapped) == MemoryAttribute::DeviceMapped) {
|
||||||
(backing_block != next.backing_block || offset + size != next.offset)) {
|
// TODO: Can device mapped memory be merged sanely?
|
||||||
|
// Not merging it may cause inaccuracies versus hardware when memory layout is queried.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (type == VMAType::AllocatedMemoryBlock) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) {
|
if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -99,7 +105,7 @@ bool VMManager::IsValidHandle(VMAHandle handle) const {
|
||||||
ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
|
ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
|
||||||
std::shared_ptr<std::vector<u8>> block,
|
std::shared_ptr<std::vector<u8>> block,
|
||||||
std::size_t offset, u64 size,
|
std::size_t offset, u64 size,
|
||||||
MemoryState state) {
|
MemoryState state, VMAPermission perm) {
|
||||||
ASSERT(block != nullptr);
|
ASSERT(block != nullptr);
|
||||||
ASSERT(offset + size <= block->size());
|
ASSERT(offset + size <= block->size());
|
||||||
|
|
||||||
|
@ -109,7 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
|
||||||
ASSERT(final_vma.size == size);
|
ASSERT(final_vma.size == size);
|
||||||
|
|
||||||
final_vma.type = VMAType::AllocatedMemoryBlock;
|
final_vma.type = VMAType::AllocatedMemoryBlock;
|
||||||
final_vma.permissions = VMAPermission::ReadWrite;
|
final_vma.permissions = perm;
|
||||||
final_vma.state = state;
|
final_vma.state = state;
|
||||||
final_vma.backing_block = std::move(block);
|
final_vma.backing_block = std::move(block);
|
||||||
final_vma.offset = offset;
|
final_vma.offset = offset;
|
||||||
|
@ -288,6 +294,166 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
|
||||||
return MakeResult<VAddr>(heap_region_base);
|
return MakeResult<VAddr>(heap_region_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
|
||||||
|
const auto end_addr = target + size;
|
||||||
|
const auto last_addr = end_addr - 1;
|
||||||
|
VAddr cur_addr = target;
|
||||||
|
|
||||||
|
ResultCode result = RESULT_SUCCESS;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Iterate, trying to map memory.
|
||||||
|
{
|
||||||
|
cur_addr = target;
|
||||||
|
|
||||||
|
auto iter = FindVMA(target);
|
||||||
|
ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != 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<std::vector<u8>>(map_size, 0), 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_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != 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(),
|
||||||
|
"MapPhysicalMemory un-map on error");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update amount of mapped physical memory.
|
||||||
|
physical_memory_mapped += size - mapped_size;
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
|
||||||
|
const auto end_addr = target + size;
|
||||||
|
const auto last_addr = end_addr - 1;
|
||||||
|
VAddr cur_addr = target;
|
||||||
|
|
||||||
|
ResultCode result = RESULT_SUCCESS;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// Try to unmap regions.
|
||||||
|
{
|
||||||
|
cur_addr = target;
|
||||||
|
|
||||||
|
auto iter = FindVMA(target);
|
||||||
|
ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != 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_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != 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<std::vector<u8>>(map_size, 0), 0,
|
||||||
|
map_size, MemoryState::Heap, VMAPermission::None);
|
||||||
|
ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update mapped amount
|
||||||
|
physical_memory_mapped -= mapped_size;
|
||||||
|
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
|
ResultCode VMManager::MapCodeMemory(VAddr dst_address, VAddr src_address, u64 size) {
|
||||||
constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
|
constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
|
||||||
const auto src_check_result = CheckRangeState(
|
const auto src_check_result = CheckRangeState(
|
||||||
|
@ -435,7 +601,7 @@ ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, Mem
|
||||||
// Protect mirror with permissions from old region
|
// Protect mirror with permissions from old region
|
||||||
Reprotect(new_vma, vma->second.permissions);
|
Reprotect(new_vma, vma->second.permissions);
|
||||||
// Remove permissions from old region
|
// Remove permissions from old region
|
||||||
Reprotect(vma, VMAPermission::None);
|
ReprotectRange(src_addr, size, VMAPermission::None);
|
||||||
|
|
||||||
return RESULT_SUCCESS;
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -568,14 +734,14 @@ VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u64 offset_in_vma) {
|
||||||
VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
|
VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
|
||||||
const VMAIter next_vma = std::next(iter);
|
const VMAIter next_vma = std::next(iter);
|
||||||
if (next_vma != vma_map.end() && iter->second.CanBeMergedWith(next_vma->second)) {
|
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);
|
vma_map.erase(next_vma);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iter != vma_map.begin()) {
|
if (iter != vma_map.begin()) {
|
||||||
VMAIter prev_vma = std::prev(iter);
|
VMAIter prev_vma = std::prev(iter);
|
||||||
if (prev_vma->second.CanBeMergedWith(iter->second)) {
|
if (prev_vma->second.CanBeMergedWith(iter->second)) {
|
||||||
prev_vma->second.size += iter->second.size;
|
MergeAdjacentVMA(prev_vma->second, iter->second);
|
||||||
vma_map.erase(iter);
|
vma_map.erase(iter);
|
||||||
iter = prev_vma;
|
iter = prev_vma;
|
||||||
}
|
}
|
||||||
|
@ -584,6 +750,38 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) {
|
||||||
return 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)) {
|
||||||
|
// 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.backing_block->begin() + right.offset,
|
||||||
|
right.backing_block->begin() + right.offset + right.size);
|
||||||
|
} else {
|
||||||
|
// Slow case: make a new memory block for left and right.
|
||||||
|
auto new_memory = std::make_shared<std::vector<u8>>();
|
||||||
|
new_memory->insert(new_memory->end(), left.backing_block->begin() + left.offset,
|
||||||
|
left.backing_block->begin() + left.offset + left.size);
|
||||||
|
new_memory->insert(new_memory->end(), right.backing_block->begin() + right.offset,
|
||||||
|
right.backing_block->begin() + right.offset + right.size);
|
||||||
|
left.backing_block = 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) {
|
void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) {
|
||||||
switch (vma.type) {
|
switch (vma.type) {
|
||||||
case VMAType::Free:
|
case VMAType::Free:
|
||||||
|
@ -758,6 +956,84 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo
|
||||||
std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
|
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_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != 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_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != 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_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != 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_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeResult(mapped_size);
|
||||||
|
}
|
||||||
|
|
||||||
u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
|
u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
|
||||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||||
return 0xF8000000;
|
return 0xF8000000;
|
||||||
|
|
|
@ -349,7 +349,8 @@ public:
|
||||||
* @param state MemoryState tag to attach to the VMA.
|
* @param state MemoryState tag to attach to the VMA.
|
||||||
*/
|
*/
|
||||||
ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block,
|
ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block,
|
||||||
std::size_t offset, u64 size, MemoryState state);
|
std::size_t offset, u64 size, MemoryState state,
|
||||||
|
VMAPermission perm = VMAPermission::ReadWrite);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps an unmanaged host memory pointer at a given address.
|
* Maps an unmanaged host memory pointer at a given address.
|
||||||
|
@ -450,6 +451,34 @@ public:
|
||||||
///
|
///
|
||||||
ResultVal<VAddr> SetHeapSize(u64 size);
|
ResultVal<VAddr> SetHeapSize(u64 size);
|
||||||
|
|
||||||
|
/// Maps memory at a given address.
|
||||||
|
///
|
||||||
|
/// @param addr 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 addr 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.
|
/// Maps a region of memory as code memory.
|
||||||
///
|
///
|
||||||
/// @param dst_address The base address of the region to create the aliasing memory region.
|
/// @param dst_address The base address of the region to create the aliasing memory region.
|
||||||
|
@ -657,6 +686,11 @@ private:
|
||||||
*/
|
*/
|
||||||
VMAIter MergeAdjacent(VMAIter vma);
|
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.
|
/// Updates the pages corresponding to this VMA so they match the VMA's attributes.
|
||||||
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
|
void UpdatePageTableForVMA(const VirtualMemoryArea& vma);
|
||||||
|
|
||||||
|
@ -701,6 +735,13 @@ private:
|
||||||
MemoryAttribute attribute_mask, MemoryAttribute attribute,
|
MemoryAttribute attribute_mask, MemoryAttribute attribute,
|
||||||
MemoryAttribute ignore_mask) const;
|
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
|
* 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
|
* VMA. It must always be modified by splitting or merging VMAs, so that the invariant
|
||||||
|
@ -742,6 +783,11 @@ private:
|
||||||
// end of the range. This is essentially 'base_address + current_size'.
|
// end of the range. This is essentially 'base_address + current_size'.
|
||||||
VAddr heap_end = 0;
|
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;
|
Core::System& system;
|
||||||
};
|
};
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -31,7 +31,7 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
|
||||||
|
|
||||||
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} {
|
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} {
|
||||||
auto& rasterizer{renderer.Rasterizer()};
|
auto& rasterizer{renderer.Rasterizer()};
|
||||||
memory_manager = std::make_unique<Tegra::MemoryManager>(rasterizer);
|
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
|
||||||
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
|
||||||
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
|
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
|
||||||
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
|
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
|
||||||
|
|
|
@ -5,13 +5,17 @@
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/kernel/process.h"
|
||||||
|
#include "core/hle/kernel/vm_manager.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "video_core/memory_manager.h"
|
#include "video_core/memory_manager.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
MemoryManager::MemoryManager(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {
|
MemoryManager::MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||||
|
: rasterizer{rasterizer}, system{system} {
|
||||||
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
|
std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr);
|
||||||
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
|
std::fill(page_table.attributes.begin(), page_table.attributes.end(),
|
||||||
Common::PageType::Unmapped);
|
Common::PageType::Unmapped);
|
||||||
|
@ -49,6 +53,11 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) {
|
||||||
const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
|
const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)};
|
||||||
|
|
||||||
MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
|
MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
|
||||||
|
ASSERT(system.CurrentProcess()
|
||||||
|
->VMManager()
|
||||||
|
.SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped,
|
||||||
|
Kernel::MemoryAttribute::DeviceMapped)
|
||||||
|
.IsSuccess());
|
||||||
|
|
||||||
return gpu_addr;
|
return gpu_addr;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +68,11 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size)
|
||||||
const u64 aligned_size{Common::AlignUp(size, page_size)};
|
const u64 aligned_size{Common::AlignUp(size, page_size)};
|
||||||
|
|
||||||
MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
|
MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr);
|
||||||
|
ASSERT(system.CurrentProcess()
|
||||||
|
->VMManager()
|
||||||
|
.SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped,
|
||||||
|
Kernel::MemoryAttribute::DeviceMapped)
|
||||||
|
.IsSuccess());
|
||||||
return gpu_addr;
|
return gpu_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,9 +81,16 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) {
|
||||||
|
|
||||||
const u64 aligned_size{Common::AlignUp(size, page_size)};
|
const u64 aligned_size{Common::AlignUp(size, page_size)};
|
||||||
const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))};
|
const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))};
|
||||||
|
const auto cpu_addr = GpuToCpuAddress(gpu_addr);
|
||||||
|
ASSERT(cpu_addr);
|
||||||
|
|
||||||
rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size);
|
rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size);
|
||||||
UnmapRange(gpu_addr, aligned_size);
|
UnmapRange(gpu_addr, aligned_size);
|
||||||
|
ASSERT(system.CurrentProcess()
|
||||||
|
->VMManager()
|
||||||
|
.SetMemoryAttribute(cpu_addr.value(), size, Kernel::MemoryAttribute::DeviceMapped,
|
||||||
|
Kernel::MemoryAttribute::None)
|
||||||
|
.IsSuccess());
|
||||||
|
|
||||||
return gpu_addr;
|
return gpu_addr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,10 @@ namespace VideoCore {
|
||||||
class RasterizerInterface;
|
class RasterizerInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Tegra {
|
namespace Tegra {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,7 +51,7 @@ struct VirtualMemoryArea {
|
||||||
|
|
||||||
class MemoryManager final {
|
class MemoryManager final {
|
||||||
public:
|
public:
|
||||||
explicit MemoryManager(VideoCore::RasterizerInterface& rasterizer);
|
explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer);
|
||||||
~MemoryManager();
|
~MemoryManager();
|
||||||
|
|
||||||
GPUVAddr AllocateSpace(u64 size, u64 align);
|
GPUVAddr AllocateSpace(u64 size, u64 align);
|
||||||
|
@ -173,6 +177,8 @@ private:
|
||||||
Common::PageTable page_table{page_bits};
|
Common::PageTable page_table{page_bits};
|
||||||
VMAMap vma_map;
|
VMAMap vma_map;
|
||||||
VideoCore::RasterizerInterface& rasterizer;
|
VideoCore::RasterizerInterface& rasterizer;
|
||||||
|
|
||||||
|
Core::System& system;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Tegra
|
} // namespace Tegra
|
||||||
|
|
Loading…
Reference in New Issue