From 13a8fde3ad2a4a37cf1bb8dcb367b4c8fc8b4d9b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Jul 2019 09:42:54 -0700 Subject: Implement MapPhysicalMemory/UnmapPhysicalMemory This implements svcMapPhysicalMemory/svcUnmapPhysicalMemory for Yuzu, which can be used to map memory at a desired address by games since 3.0.0. It also properly parses SystemResourceSize from NPDM, and makes information available via svcGetInfo. This is needed for games like Super Smash Bros. and Diablo 3 -- this PR's implementation does not run into the "ASCII reads" issue mentioned in the comments of #2626, which was caused by the following bugs in Yuzu's memory management that this PR also addresses: * Yuzu's memory coalescing does not properly merge blocks. This results in a polluted address space/svcQueryMemory results that would be impossible to replicate on hardware, which can lead to game code making the wrong assumptions about memory layout. * This implements better merging for AllocatedMemoryBlocks. * Yuzu's implementation of svcMirrorMemory unprotected the entire virtual memory range containing the range being mirrored. This could lead to games attempting to map data at that unprotected range/attempting to access that range after yuzu improperly unmapped it. * This PR fixes it by simply calling ReprotectRange instead of Reprotect. --- src/core/file_sys/program_metadata.cpp | 4 + src/core/file_sys/program_metadata.h | 4 +- src/core/hle/kernel/process.cpp | 1 + src/core/hle/kernel/process.h | 11 +- src/core/hle/kernel/svc.cpp | 110 +++++++++++- src/core/hle/kernel/svc_wrap.h | 5 + src/core/hle/kernel/vm_manager.cpp | 320 ++++++++++++++++++++++++++++++++- src/core/hle/kernel/vm_manager.h | 41 ++++- 8 files changed, 475 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index eb76174c5..7310b3602 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -94,6 +94,10 @@ u64 ProgramMetadata::GetFilesystemPermissions() const { return aci_file_access.permissions; } +u32 ProgramMetadata::GetSystemResourceSize() const { + return npdm_header.system_resource_size; +} + const ProgramMetadata::KernelCapabilityDescriptors& ProgramMetadata::GetKernelCapabilities() const { return aci_kernel_capabilities; } diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 43bf2820a..88ec97d85 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -58,6 +58,7 @@ public: u32 GetMainThreadStackSize() const; u64 GetTitleID() const; u64 GetFilesystemPermissions() const; + u32 GetSystemResourceSize() const; const KernelCapabilityDescriptors& GetKernelCapabilities() const; void Print() const; @@ -76,7 +77,8 @@ private: u8 reserved_3; u8 main_thread_priority; u8 main_thread_cpu; - std::array reserved_4; + std::array reserved_4; + u32_le system_resource_size; u32_le process_category; u32_le main_stack_size; std::array application_name; diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index f45ef05f6..51245cbb4 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -172,6 +172,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()); diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 83ea02bee..b0e795577 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -168,8 +168,9 @@ 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; } /// Whether this process is an AArch64 or AArch32 process. @@ -298,12 +299,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 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 diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 332573a95..abb374892 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -729,8 +729,8 @@ 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, @@ -756,8 +756,8 @@ 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: @@ -822,8 +822,22 @@ 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: + // 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? No game should ever use it, since + // the amount of remaining page table space should never be relevant except + // for diagnostics. Is returning a value other than zero wise? + LOG_WARNING(Kernel_SVC, + "(STUBBED) Attempted to query system resource usage, returned 0"); + *result = 0; return RESULT_SUCCESS; case GetInfoType::TitleId: @@ -946,6 +960,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; + } + + auto* const current_process = Core::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; + } + + auto* const current_process = Core::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); @@ -2303,8 +2397,8 @@ static const FunctionDef SVC_Table[] = { {0x29, SvcWrap, "GetInfo"}, {0x2A, nullptr, "FlushEntireDataCache"}, {0x2B, nullptr, "FlushDataCache"}, - {0x2C, nullptr, "MapPhysicalMemory"}, - {0x2D, nullptr, "UnmapPhysicalMemory"}, + {0x2C, SvcWrap, "MapPhysicalMemory"}, + {0x2D, SvcWrap, "UnmapPhysicalMemory"}, {0x2E, nullptr, "GetFutureThreadInfo"}, {0x2F, nullptr, "GetLastThreadInfo"}, {0x30, SvcWrap, "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 +void SvcWrap(Core::System& system) { + FuncReturn(system, func(system, Param(system, 0), Param(system, 1)).raw); +} + template void SvcWrap(Core::System& system) { FuncReturn(system, func(system, static_cast(Param(system, 0))).raw); diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 501544090..9385a8697 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -12,6 +12,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" @@ -49,9 +51,8 @@ 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)) { - return false; + if (type == VMAType::AllocatedMemoryBlock) { + return true; } if (type == VMAType::BackingMemory && backing_memory + size != next.backing_memory) { return false; @@ -100,7 +101,7 @@ bool VMManager::IsValidHandle(VMAHandle handle) const { ResultVal VMManager::MapMemoryBlock(VAddr target, std::shared_ptr> block, std::size_t offset, u64 size, - MemoryState state) { + MemoryState state, VMAPermission perm) { ASSERT(block != nullptr); ASSERT(offset + size <= block->size()); @@ -119,7 +120,7 @@ ResultVal VMManager::MapMemoryBlock(VAddr target, 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; @@ -308,6 +309,258 @@ ResultVal VMManager::SetHeapSize(u64 size) { return MakeResult(heap_region_base); } +ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { + const auto last_addr = target + size - 1; + VAddr cur_addr = target; + std::size_t mapped_size = 0; + + ResultCode result = RESULT_SUCCESS; + + // Check whether we've already mapped the desired memory. + { + auto vma = FindVMA(target); + ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); + + while (true) { + const auto vma_start = vma->second.base; + const auto vma_size = vma->second.size; + const auto state = vma->second.state; + + // Handle last block. + if (last_addr <= (vma_start + vma_size - 1)) { + if (state != MemoryState::Unmapped) { + mapped_size += last_addr - cur_addr + 1; + } + break; + } + + if (state != MemoryState::Unmapped) { + mapped_size += vma_start + vma_size - cur_addr; + } + cur_addr = vma_start + vma_size; + vma++; + ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); + } + + // If we already have the desired amount mapped, we're done. + if (mapped_size == size) { + return RESULT_SUCCESS; + } + } + + // Check that we can map the memory we want. + const auto res_limit = Core::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> mapped_regions; + + // Iterate, trying to map memory. + // Map initially with VMAPermission::None. + { + cur_addr = target; + + auto vma = FindVMA(target); + ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); + + while (true) { + const auto vma_start = vma->second.base; + const auto vma_size = vma->second.size; + const auto state = vma->second.state; + + // Handle last block. + if (last_addr <= (vma_start + vma_size - 1)) { + if (state == MemoryState::Unmapped) { + const auto map_res = MapMemoryBlock( + cur_addr, std::make_shared>(last_addr - cur_addr + 1, 0), 0, + last_addr - cur_addr + 1, MemoryState::Heap, VMAPermission::None); + result = map_res.Code(); + if (result.IsSuccess()) { + mapped_regions.push_back( + std::make_pair(cur_addr, last_addr - cur_addr + 1)); + } + } + break; + } + + if (state == MemoryState::Unmapped) { + const auto map_res = MapMemoryBlock( + cur_addr, std::make_shared>(vma_start + vma_size - cur_addr, 0), + 0, vma_start + vma_size - cur_addr, MemoryState::Heap, VMAPermission::None); + result = map_res.Code(); + if (result.IsSuccess()) { + mapped_regions.push_back( + std::make_pair(cur_addr, vma_start + vma_size - cur_addr)); + } else { + break; + } + } + cur_addr = vma_start + vma_size; + vma = FindVMA(cur_addr); + ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); + } + } + + // If we failed, unmap memory. + if (result.IsError()) { + for (const auto& it : mapped_regions) { + const auto unmap_res = UnmapRange(it.first, it.second); + ASSERT_MSG(unmap_res.IsSuccess(), "MapPhysicalMemory un-map on error"); + } + + return result; + } + + // We didn't fail, so reprotect all the memory to ReadWrite. + { + cur_addr = target; + + auto vma = FindVMA(target); + ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); + + while (true) { + const auto vma_start = vma->second.base; + const auto vma_size = vma->second.size; + const auto state = vma->second.state; + const auto perm = vma->second.permissions; + + // Handle last block. + if (last_addr <= (vma_start + vma_size - 1)) { + if (state == MemoryState::Heap && perm == VMAPermission::None) { + ASSERT_MSG( + ReprotectRange(cur_addr, last_addr - cur_addr + 1, VMAPermission::ReadWrite) + .IsSuccess(), + "MapPhysicalMemory reprotect"); + } + break; + } + + if (state == MemoryState::Heap && perm == VMAPermission::None) { + ASSERT_MSG(ReprotectRange(cur_addr, vma_start + vma_size - cur_addr, + VMAPermission::ReadWrite) + .IsSuccess(), + "MapPhysicalMemory reprotect"); + } + cur_addr = vma_start + vma_size; + vma = FindVMA(cur_addr); + ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); + } + } + + // Update amount of mapped physical memory. + physical_memory_mapped += size - mapped_size; + + return RESULT_SUCCESS; +} + +ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { + auto last_addr = target + size - 1; + VAddr cur_addr = target; + std::size_t mapped_size = 0; + + ResultCode result = RESULT_SUCCESS; + + // Check how much of the memory is currently mapped. + { + auto vma = FindVMA(target); + ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); + + while (true) { + const auto vma_start = vma->second.base; + const auto vma_size = vma->second.size; + const auto state = vma->second.state; + const auto attr = vma->second.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; + } + + // If this is the last block and it's mapped, update mapped size. + if (last_addr <= (vma_start + vma_size - 1)) { + if (state == MemoryState::Heap) { + mapped_size += last_addr - cur_addr + 1; + } + break; + } + + if (state == MemoryState::Heap) { + mapped_size += vma_start + vma_size - cur_addr; + } + cur_addr = vma_start + vma_size; + vma++; + ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); + } + + // If memory is already unmapped, we're done. + if (mapped_size == 0) { + return RESULT_SUCCESS; + } + } + + // Keep track of the memory regions we unmap. + std::vector> unmapped_regions; + + // Try to unmap regions. + { + cur_addr = target; + + auto vma = FindVMA(target); + ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); + + while (true) { + const auto vma_start = vma->second.base; + const auto vma_size = vma->second.size; + const auto state = vma->second.state; + const auto perm = vma->second.permissions; + + // Handle last block. + if (last_addr <= (vma_start + vma_size - 1)) { + if (state == MemoryState::Heap) { + result = UnmapRange(cur_addr, last_addr - cur_addr + 1); + if (result.IsSuccess()) { + unmapped_regions.push_back( + std::make_pair(cur_addr, last_addr - cur_addr + 1)); + } + } + break; + } + + if (state == MemoryState::Heap) { + result = UnmapRange(cur_addr, vma_start + vma_size - cur_addr); + if (result.IsSuccess()) { + unmapped_regions.push_back( + std::make_pair(cur_addr, vma_start + vma_size - cur_addr)); + } else { + break; + } + } + + cur_addr = vma_start + vma_size; + vma = FindVMA(cur_addr); + ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); + } + } + + // If we failed, re-map regions. + // TODO: Preserve memory contents? + if (result.IsError()) { + for (const auto& it : unmapped_regions) { + const auto remap_res = + MapMemoryBlock(it.first, std::make_shared>(it.second, 0), 0, + it.second, MemoryState::Heap, VMAPermission::None); + ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error"); + } + } + + 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( @@ -455,7 +708,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; } @@ -588,14 +841,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; } @@ -604,6 +857,57 @@ 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>(); + 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); + + // Update mappings for unicorn. + system.ArmInterface(0).UnmapMemory(left.base, left.size); + system.ArmInterface(1).UnmapMemory(left.base, left.size); + system.ArmInterface(2).UnmapMemory(left.base, left.size); + system.ArmInterface(3).UnmapMemory(left.base, left.size); + + system.ArmInterface(0).MapBackingMemory(left.base, left.size, + left.backing_block->data() + left.offset, + VMAPermission::ReadWriteExecute); + system.ArmInterface(1).MapBackingMemory(left.base, left.size, + left.backing_block->data() + left.offset, + VMAPermission::ReadWriteExecute); + system.ArmInterface(2).MapBackingMemory(left.base, left.size, + left.backing_block->data() + left.offset, + VMAPermission::ReadWriteExecute); + system.ArmInterface(3).MapBackingMemory(left.base, left.size, + left.backing_block->data() + left.offset, + VMAPermission::ReadWriteExecute); + } else { + // Just update the size. + left.size += right.size; + } +} + void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { switch (vma.type) { case VMAType::Free: diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 9fe6ac3f4..16f40ad00 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -349,7 +349,8 @@ public: * @param state MemoryState tag to attach to the VMA. */ ResultVal MapMemoryBlock(VAddr target, std::shared_ptr> 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 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 SystemResourceSize is 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 SystemResourceSize is 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); @@ -742,6 +776,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 -- cgit v1.2.3 From 1689784c198f6a7f3c389d4cd5edeccc74c1d9f3 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Jul 2019 11:48:11 -0700 Subject: address review commentary --- src/core/hle/kernel/process.cpp | 14 +++++--------- src/core/hle/kernel/process.h | 23 +++++++++++++++++++---- src/core/hle/kernel/svc.cpp | 32 ++++++++++++-------------------- src/core/hle/kernel/vm_manager.cpp | 5 ++++- src/core/hle/kernel/vm_manager.h | 4 ++-- 5 files changed, 42 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 51245cbb4..b0690be34 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -129,20 +129,16 @@ 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) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index b0e795577..3196014da 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -173,6 +173,21 @@ public: 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. bool Is64BitProcess() const { return is_64bit_process; @@ -197,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& GetThreadList() const { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index abb374892..85e4512f0 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -737,8 +737,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha // 5.0.0+ UserExceptionContextAddr = 20, // 6.0.0+ - TotalPhysicalMemoryAvailableWithoutMmHeap = 21, - TotalPhysicalMemoryUsedWithoutMmHeap = 22, + TotalPhysicalMemoryAvailableWithoutSystemResource = 21, + TotalPhysicalMemoryUsedWithoutSystemResource = 22, }; const auto info_id_type = static_cast(info_id); @@ -760,8 +760,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha 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; } @@ -827,17 +827,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha return RESULT_SUCCESS; case GetInfoType::SystemResourceUsage: - // 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? No game should ever use it, since - // the amount of remaining page table space should never be relevant except - // for diagnostics. Is returning a value other than zero wise? LOG_WARNING(Kernel_SVC, - "(STUBBED) Attempted to query system resource usage, returned 0"); - *result = 0; + "(STUBBED) Attempted to query system resource usage"); + *result = process->GetSystemResourceUsage(); return RESULT_SUCCESS; case GetInfoType::TitleId: @@ -850,12 +842,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: @@ -984,7 +976,7 @@ static ResultCode MapPhysicalMemory(Core::System& system, VAddr addr, u64 size) return ERR_INVALID_MEMORY_RANGE; } - auto* const current_process = Core::CurrentProcess(); + Process* const current_process = system.Kernel().CurrentProcess(); auto& vm_manager = current_process->VMManager(); if (current_process->GetSystemResourceSize() == 0) { @@ -1024,7 +1016,7 @@ static ResultCode UnmapPhysicalMemory(Core::System& system, VAddr addr, u64 size return ERR_INVALID_MEMORY_RANGE; } - auto* const current_process = Core::CurrentProcess(); + Process* const current_process = system.Kernel().CurrentProcess(); auto& vm_manager = current_process->VMManager(); if (current_process->GetSystemResourceSize() == 0) { diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 9385a8697..bda325e87 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -349,7 +349,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { } // Check that we can map the memory we want. - const auto res_limit = Core::CurrentProcess()->GetResourceLimit(); + 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)) { @@ -558,6 +558,9 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { } } + // Update mapped amount + physical_memory_mapped -= mapped_size; + return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 16f40ad00..8be03a6e4 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -458,7 +458,7 @@ public: /// /// @note The destination address must lie within the Map region. /// - /// @note This function requires SystemResourceSize is non-zero, + /// @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 @@ -472,7 +472,7 @@ public: /// /// @note The destination address must lie within the Map region. /// - /// @note This function requires SystemResourceSize is non-zero, + /// @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 -- cgit v1.2.3 From b901cd584e7c63ace5feb095279e1671fc605129 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Jul 2019 12:08:29 -0700 Subject: clang-format fixes --- src/core/hle/kernel/process.cpp | 3 ++- src/core/hle/kernel/svc.cpp | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b0690be34..db3ab14ce 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -134,7 +134,8 @@ u64 Process::GetTotalPhysicalMemoryAvailableWithoutSystemResource() const { } u64 Process::GetTotalPhysicalMemoryUsed() const { - return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size + GetSystemResourceUsage(); + return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size + + GetSystemResourceUsage(); } u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 85e4512f0..f9fe7dce0 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -827,8 +827,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha return RESULT_SUCCESS; case GetInfoType::SystemResourceUsage: - LOG_WARNING(Kernel_SVC, - "(STUBBED) Attempted to query system resource usage"); + LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); *result = process->GetSystemResourceUsage(); return RESULT_SUCCESS; -- cgit v1.2.3 From ce64a9fab9e6f015a4d2b332abcb7043914549d4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Jul 2019 12:55:30 -0700 Subject: physmem: add helpers, cleanup logic. --- src/core/hle/kernel/vm_manager.cpp | 325 ++++++++++++++++++------------------- src/core/hle/kernel/vm_manager.h | 16 ++ 2 files changed, 170 insertions(+), 171 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index bda325e87..775d170bf 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -310,42 +310,22 @@ ResultVal VMManager::SetHeapSize(u64 size) { } ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { - const auto last_addr = target + size - 1; + const auto end_addr = target + size; + const auto last_addr = end_addr - 1; VAddr cur_addr = target; - std::size_t mapped_size = 0; ResultCode result = RESULT_SUCCESS; - // Check whether we've already mapped the desired memory. - { - auto vma = FindVMA(target); - ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); - - while (true) { - const auto vma_start = vma->second.base; - const auto vma_size = vma->second.size; - const auto state = vma->second.state; - - // Handle last block. - if (last_addr <= (vma_start + vma_size - 1)) { - if (state != MemoryState::Unmapped) { - mapped_size += last_addr - cur_addr + 1; - } - break; - } - - if (state != MemoryState::Unmapped) { - mapped_size += vma_start + vma_size - cur_addr; - } - cur_addr = vma_start + vma_size; - vma++; - ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); - } + // 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 already have the desired amount mapped, we're done. - if (mapped_size == size) { - return RESULT_SUCCESS; - } + // 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. @@ -360,97 +340,54 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { std::vector> mapped_regions; // Iterate, trying to map memory. - // Map initially with VMAPermission::None. { cur_addr = target; - auto vma = FindVMA(target); - ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); + auto iter = FindVMA(target); + ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end"); while (true) { - const auto vma_start = vma->second.base; - const auto vma_size = vma->second.size; - const auto state = vma->second.state; - - // Handle last block. - if (last_addr <= (vma_start + vma_size - 1)) { - if (state == MemoryState::Unmapped) { - const auto map_res = MapMemoryBlock( - cur_addr, std::make_shared>(last_addr - cur_addr + 1, 0), 0, - last_addr - cur_addr + 1, MemoryState::Heap, VMAPermission::None); - result = map_res.Code(); - if (result.IsSuccess()) { - mapped_regions.push_back( - std::make_pair(cur_addr, last_addr - cur_addr + 1)); - } - } - break; - } - - if (state == MemoryState::Unmapped) { - const auto map_res = MapMemoryBlock( - cur_addr, std::make_shared>(vma_start + vma_size - cur_addr, 0), - 0, vma_start + vma_size - cur_addr, MemoryState::Heap, VMAPermission::None); + 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>(map_size, 0), 0, + map_size, MemoryState::Heap, VMAPermission::ReadWrite); result = map_res.Code(); - if (result.IsSuccess()) { - mapped_regions.push_back( - std::make_pair(cur_addr, vma_start + vma_size - cur_addr)); - } else { + if (result.IsError()) { break; } + + mapped_regions.emplace_back(cur_addr, map_size); } - cur_addr = vma_start + vma_size; - vma = FindVMA(cur_addr); - ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); + + // 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& it : mapped_regions) { - const auto unmap_res = UnmapRange(it.first, it.second); - ASSERT_MSG(unmap_res.IsSuccess(), "MapPhysicalMemory un-map on error"); + for (const auto [unmap_address, unmap_size] : mapped_regions) { + ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(), + "MapPhysicalMemory un-map on error"); } return result; } - // We didn't fail, so reprotect all the memory to ReadWrite. - { - cur_addr = target; - - auto vma = FindVMA(target); - ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); - - while (true) { - const auto vma_start = vma->second.base; - const auto vma_size = vma->second.size; - const auto state = vma->second.state; - const auto perm = vma->second.permissions; - - // Handle last block. - if (last_addr <= (vma_start + vma_size - 1)) { - if (state == MemoryState::Heap && perm == VMAPermission::None) { - ASSERT_MSG( - ReprotectRange(cur_addr, last_addr - cur_addr + 1, VMAPermission::ReadWrite) - .IsSuccess(), - "MapPhysicalMemory reprotect"); - } - break; - } - - if (state == MemoryState::Heap && perm == VMAPermission::None) { - ASSERT_MSG(ReprotectRange(cur_addr, vma_start + vma_size - cur_addr, - VMAPermission::ReadWrite) - .IsSuccess(), - "MapPhysicalMemory reprotect"); - } - cur_addr = vma_start + vma_size; - vma = FindVMA(cur_addr); - ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); - } - } - // Update amount of mapped physical memory. physical_memory_mapped += size - mapped_size; @@ -458,49 +395,22 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { } ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { - auto last_addr = target + size - 1; + const auto end_addr = target + size; + const auto last_addr = end_addr - 1; VAddr cur_addr = target; - std::size_t mapped_size = 0; ResultCode result = RESULT_SUCCESS; - // Check how much of the memory is currently mapped. - { - auto vma = FindVMA(target); - ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); - - while (true) { - const auto vma_start = vma->second.base; - const auto vma_size = vma->second.size; - const auto state = vma->second.state; - const auto attr = vma->second.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; - } - - // If this is the last block and it's mapped, update mapped size. - if (last_addr <= (vma_start + vma_size - 1)) { - if (state == MemoryState::Heap) { - mapped_size += last_addr - cur_addr + 1; - } - break; - } - - if (state == MemoryState::Heap) { - mapped_size += vma_start + vma_size - cur_addr; - } - cur_addr = vma_start + vma_size; - vma++; - ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); - } + // 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 memory is already unmapped, we're done. - if (mapped_size == 0) { - return RESULT_SUCCESS; - } + // 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. @@ -510,50 +420,45 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { { cur_addr = target; - auto vma = FindVMA(target); - ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); + auto iter = FindVMA(target); + ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end"); while (true) { - const auto vma_start = vma->second.base; - const auto vma_size = vma->second.size; - const auto state = vma->second.state; - const auto perm = vma->second.permissions; - - // Handle last block. - if (last_addr <= (vma_start + vma_size - 1)) { - if (state == MemoryState::Heap) { - result = UnmapRange(cur_addr, last_addr - cur_addr + 1); - if (result.IsSuccess()) { - unmapped_regions.push_back( - std::make_pair(cur_addr, last_addr - cur_addr + 1)); - } + 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; } - break; + + unmapped_regions.emplace_back(cur_addr, unmap_size); } - if (state == MemoryState::Heap) { - result = UnmapRange(cur_addr, vma_start + vma_size - cur_addr); - if (result.IsSuccess()) { - unmapped_regions.push_back( - std::make_pair(cur_addr, vma_start + vma_size - cur_addr)); - } else { - break; - } + // Break once we hit the end of the range. + if (last_addr <= vma_last) { + break; } - cur_addr = vma_start + vma_size; - vma = FindVMA(cur_addr); - ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); + // 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& it : unmapped_regions) { + for (const auto [map_address, map_size] : unmapped_regions) { const auto remap_res = - MapMemoryBlock(it.first, std::make_shared>(it.second, 0), 0, - it.second, MemoryState::Heap, VMAPermission::None); + MapMemoryBlock(map_address, std::make_shared>(map_size, 0), 0, + map_size, MemoryState::Heap, VMAPermission::None); ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error"); } } @@ -1085,6 +990,84 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask)); } +ResultVal 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 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; diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 8be03a6e4..5b27548aa 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -303,6 +303,15 @@ struct VirtualMemoryArea { PAddr paddr = 0; Common::MemoryHookPointer mmio_handler = nullptr; + /// If the address lies within this VMA, returns the size left before the + /// end of this VMA. If the given address doesn't lie within the VMA, then + /// an empty optional value is returned. + /// + /// For example, given a VMA 100 bytes long. If '10' was given as the + /// start address, then this would return 90. + /// + std::optional SizeRemainingFromAddress(VAddr address) const; + /// Tests if this area can be merged to the right with `next`. bool CanBeMergedWith(const VirtualMemoryArea& next) const; }; @@ -735,6 +744,13 @@ private: MemoryAttribute attribute_mask, MemoryAttribute attribute, MemoryAttribute ignore_mask) const; + /// Gets the amount of memory currently mapped (state != Unmapped) in a range. + ResultVal SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; + + /// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range. + ResultVal 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 -- cgit v1.2.3 From ca6f08e3b1df011075bf4564bbb61d4cdf9a7737 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Jul 2019 13:02:41 -0700 Subject: Remove unused member function declaration --- src/core/hle/kernel/vm_manager.h | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 5b27548aa..0aecb7499 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -303,15 +303,6 @@ struct VirtualMemoryArea { PAddr paddr = 0; Common::MemoryHookPointer mmio_handler = nullptr; - /// If the address lies within this VMA, returns the size left before the - /// end of this VMA. If the given address doesn't lie within the VMA, then - /// an empty optional value is returned. - /// - /// For example, given a VMA 100 bytes long. If '10' was given as the - /// start address, then this would return 90. - /// - std::optional SizeRemainingFromAddress(VAddr address) const; - /// Tests if this area can be merged to the right with `next`. bool CanBeMergedWith(const VirtualMemoryArea& next) const; }; -- cgit v1.2.3 From 697206092e8ac28c7dfe83eff0eea6613082740c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Jul 2019 22:19:27 -0700 Subject: Prevent merging of device mapped memory blocks. This sets the DeviceMapped attribute for GPU-mapped memory blocks, and prevents merging device mapped blocks. This prevents memory mapped from the gpu from having its backing address changed by block coalesce. --- src/core/hle/kernel/vm_manager.cpp | 5 +++++ src/video_core/memory_manager.cpp | 24 +++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 775d170bf..72a9d7717 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -51,6 +51,11 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { type != next.type) { return false; } + if ((attribute & MemoryAttribute::DeviceMapped) == MemoryAttribute::DeviceMapped) { + // TODO: Can device mapped memory be merged sanely? + // Not merging it may cause inaccuracies versus hardware when memory layout is queried. + return false; + } if (type == VMAType::AllocatedMemoryBlock) { return true; } diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 322453116..3a158fa26 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -5,6 +5,9 @@ #include "common/alignment.h" #include "common/assert.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 "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" @@ -49,6 +52,12 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); + ASSERT(Core::System::GetInstance() + .CurrentProcess() + ->VMManager() + .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, + Kernel::MemoryAttribute::DeviceMapped) + .IsSuccess()); return gpu_addr; } @@ -59,7 +68,12 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) const u64 aligned_size{Common::AlignUp(size, page_size)}; MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); - + ASSERT(Core::System::GetInstance() + .CurrentProcess() + ->VMManager() + .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, + Kernel::MemoryAttribute::DeviceMapped) + .IsSuccess()); return gpu_addr; } @@ -68,9 +82,17 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { const u64 aligned_size{Common::AlignUp(size, page_size)}; const CacheAddr cache_addr{ToCacheAddr(GetPointer(gpu_addr))}; + const auto cpu_addr = GpuToCpuAddress(gpu_addr); + ASSERT(cpu_addr); rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size); UnmapRange(gpu_addr, aligned_size); + ASSERT(Core::System::GetInstance() + .CurrentProcess() + ->VMManager() + .SetMemoryAttribute(cpu_addr.value(), size, Kernel::MemoryAttribute::DeviceMapped, + Kernel::MemoryAttribute::None) + .IsSuccess()); return gpu_addr; } -- cgit v1.2.3 From a1845d1dd3e6b03159c66e0c925688c98e373956 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 8 Jul 2019 23:17:44 -0700 Subject: prefer system reference over global accessor --- src/video_core/gpu.cpp | 2 +- src/video_core/memory_manager.cpp | 12 +++++------- src/video_core/memory_manager.h | 8 +++++++- 3 files changed, 13 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 52706505b..1b4975498 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -31,7 +31,7 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) { GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer) : renderer{renderer} { auto& rasterizer{renderer.Rasterizer()}; - memory_manager = std::make_unique(rasterizer); + memory_manager = std::make_unique(system, rasterizer); dma_pusher = std::make_unique(*this); maxwell_3d = std::make_unique(system, rasterizer, *memory_manager); fermi_2d = std::make_unique(rasterizer, *memory_manager); diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 3a158fa26..bffae940c 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -14,7 +14,8 @@ 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.attributes.begin(), page_table.attributes.end(), Common::PageType::Unmapped); @@ -52,8 +53,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, u64 size) { const GPUVAddr gpu_addr{FindFreeRegion(address_space_base, aligned_size)}; MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); - ASSERT(Core::System::GetInstance() - .CurrentProcess() + ASSERT(system.CurrentProcess() ->VMManager() .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, Kernel::MemoryAttribute::DeviceMapped) @@ -68,8 +68,7 @@ GPUVAddr MemoryManager::MapBufferEx(VAddr cpu_addr, GPUVAddr gpu_addr, u64 size) const u64 aligned_size{Common::AlignUp(size, page_size)}; MapBackingMemory(gpu_addr, Memory::GetPointer(cpu_addr), aligned_size, cpu_addr); - ASSERT(Core::System::GetInstance() - .CurrentProcess() + ASSERT(system.CurrentProcess() ->VMManager() .SetMemoryAttribute(cpu_addr, size, Kernel::MemoryAttribute::DeviceMapped, Kernel::MemoryAttribute::DeviceMapped) @@ -87,8 +86,7 @@ GPUVAddr MemoryManager::UnmapBuffer(GPUVAddr gpu_addr, u64 size) { rasterizer.FlushAndInvalidateRegion(cache_addr, aligned_size); UnmapRange(gpu_addr, aligned_size); - ASSERT(Core::System::GetInstance() - .CurrentProcess() + ASSERT(system.CurrentProcess() ->VMManager() .SetMemoryAttribute(cpu_addr.value(), size, Kernel::MemoryAttribute::DeviceMapped, Kernel::MemoryAttribute::None) diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 43a84bd52..aea010087 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -14,6 +14,10 @@ namespace VideoCore { class RasterizerInterface; } +namespace Core { +class System; +} + namespace Tegra { /** @@ -47,7 +51,7 @@ struct VirtualMemoryArea { class MemoryManager final { public: - explicit MemoryManager(VideoCore::RasterizerInterface& rasterizer); + explicit MemoryManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer); ~MemoryManager(); GPUVAddr AllocateSpace(u64 size, u64 align); @@ -173,6 +177,8 @@ private: Common::PageTable page_table{page_bits}; VMAMap vma_map; VideoCore::RasterizerInterface& rasterizer; + + Core::System& system; }; } // namespace Tegra -- cgit v1.2.3 From d4fc560c0539e5ba1d5cfcd03c92658699f20e5b Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 11 Jul 2019 15:12:33 -0700 Subject: Remove unicorn mappings/unmappings --- src/core/hle/kernel/vm_manager.cpp | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'src') diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 72a9d7717..02170d548 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -796,25 +796,6 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre // Page table update is needed, because backing memory changed. left.size += right.size; UpdatePageTableForVMA(left); - - // Update mappings for unicorn. - system.ArmInterface(0).UnmapMemory(left.base, left.size); - system.ArmInterface(1).UnmapMemory(left.base, left.size); - system.ArmInterface(2).UnmapMemory(left.base, left.size); - system.ArmInterface(3).UnmapMemory(left.base, left.size); - - system.ArmInterface(0).MapBackingMemory(left.base, left.size, - left.backing_block->data() + left.offset, - VMAPermission::ReadWriteExecute); - system.ArmInterface(1).MapBackingMemory(left.base, left.size, - left.backing_block->data() + left.offset, - VMAPermission::ReadWriteExecute); - system.ArmInterface(2).MapBackingMemory(left.base, left.size, - left.backing_block->data() + left.offset, - VMAPermission::ReadWriteExecute); - system.ArmInterface(3).MapBackingMemory(left.base, left.size, - left.backing_block->data() + left.offset, - VMAPermission::ReadWriteExecute); } else { // Just update the size. left.size += right.size; -- cgit v1.2.3