Merge pull request #2690 from SciresM/physmem_fixes
Implement MapPhysicalMemory/UnmapPhysicalMemory
This commit is contained in:
		@@ -129,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) {
 | 
			
		||||
@@ -172,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());
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -168,8 +168,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.
 | 
			
		||||
@@ -196,15 +212,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 {
 | 
			
		||||
@@ -298,12 +314,16 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -736,16 +736,16 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
 | 
			
		||||
        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);
 | 
			
		||||
@@ -763,12 +763,12 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -829,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:
 | 
			
		||||
@@ -843,12 +848,12 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
 | 
			
		||||
            *result = 0;
 | 
			
		||||
            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:
 | 
			
		||||
@@ -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
 | 
			
		||||
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 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"},
 | 
			
		||||
    {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"},
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,8 @@
 | 
			
		||||
#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"
 | 
			
		||||
@@ -48,10 +50,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;
 | 
			
		||||
    }
 | 
			
		||||
@@ -99,7 +105,7 @@ bool VMManager::IsValidHandle(VMAHandle handle) const {
 | 
			
		||||
ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
 | 
			
		||||
                                                          std::shared_ptr<std::vector<u8>> block,
 | 
			
		||||
                                                          std::size_t offset, u64 size,
 | 
			
		||||
                                                          MemoryState state) {
 | 
			
		||||
                                                          MemoryState state, VMAPermission perm) {
 | 
			
		||||
    ASSERT(block != nullptr);
 | 
			
		||||
    ASSERT(offset + size <= block->size());
 | 
			
		||||
 | 
			
		||||
@@ -109,7 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
 | 
			
		||||
    ASSERT(final_vma.size == size);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
@@ -288,6 +294,166 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
 | 
			
		||||
    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) {
 | 
			
		||||
    constexpr auto ignore_attribute = MemoryAttribute::LockedForIPC | MemoryAttribute::DeviceMapped;
 | 
			
		||||
    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
 | 
			
		||||
    Reprotect(new_vma, vma->second.permissions);
 | 
			
		||||
    // Remove permissions from old region
 | 
			
		||||
    Reprotect(vma, VMAPermission::None);
 | 
			
		||||
    ReprotectRange(src_addr, size, VMAPermission::None);
 | 
			
		||||
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -568,14 +734,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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -584,6 +750,38 @@ 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)) {
 | 
			
		||||
        // 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) {
 | 
			
		||||
    switch (vma.type) {
 | 
			
		||||
    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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
    LOG_WARNING(Kernel, "(STUBBED) called");
 | 
			
		||||
    return 0xF8000000;
 | 
			
		||||
 
 | 
			
		||||
@@ -349,7 +349,8 @@ public:
 | 
			
		||||
     * @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);
 | 
			
		||||
                                        std::size_t offset, u64 size, MemoryState state,
 | 
			
		||||
                                        VMAPermission perm = VMAPermission::ReadWrite);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Maps an unmanaged host memory pointer at a given address.
 | 
			
		||||
@@ -450,6 +451,34 @@ public:
 | 
			
		||||
    ///
 | 
			
		||||
    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.
 | 
			
		||||
    ///
 | 
			
		||||
    /// @param dst_address The base address of the region to create the aliasing memory region.
 | 
			
		||||
@@ -657,6 +686,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);
 | 
			
		||||
 | 
			
		||||
@@ -701,6 +735,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
 | 
			
		||||
@@ -742,6 +783,11 @@ private:
 | 
			
		||||
    // 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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user