physmem: add helpers, cleanup logic.

This commit is contained in:
Michael Scire 2019-07-07 12:55:30 -07:00
parent b901cd584e
commit ce64a9fab9
2 changed files with 169 additions and 170 deletions

View file

@ -310,43 +310,23 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
} }
ResultCode VMManager::MapPhysicalMemory(VAddr target, 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; VAddr cur_addr = target;
std::size_t mapped_size = 0;
ResultCode result = RESULT_SUCCESS; ResultCode result = RESULT_SUCCESS;
// Check whether we've already mapped the desired memory. // Check how much memory we've already mapped.
{ const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size);
auto vma = FindVMA(target); if (mapped_size_result.Failed()) {
ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); return mapped_size_result.Code();
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) { // If we've already mapped the desired amount, return early.
mapped_size += vma_start + vma_size - cur_addr; const std::size_t mapped_size = *mapped_size_result;
}
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) { if (mapped_size == size) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
}
// Check that we can map the memory we want. // Check that we can map the memory we want.
const auto res_limit = system.CurrentProcess()->GetResourceLimit(); const auto res_limit = system.CurrentProcess()->GetResourceLimit();
@ -360,97 +340,54 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
std::vector<std::pair<u64, u64>> mapped_regions; std::vector<std::pair<u64, u64>> mapped_regions;
// Iterate, trying to map memory. // Iterate, trying to map memory.
// Map initially with VMAPermission::None.
{ {
cur_addr = target; cur_addr = target;
auto vma = FindVMA(target); auto iter = FindVMA(target);
ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
while (true) { while (true) {
const auto vma_start = vma->second.base; const auto& vma = iter->second;
const auto vma_size = vma->second.size; const auto vma_start = vma.base;
const auto state = vma->second.state; const auto vma_end = vma_start + vma.size;
const auto vma_last = vma_end - 1;
// Handle last block. // Map the memory block
if (last_addr <= (vma_start + vma_size - 1)) { const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
if (state == MemoryState::Unmapped) { if (vma.state == MemoryState::Unmapped) {
const auto map_res = MapMemoryBlock( const auto map_res =
cur_addr, std::make_shared<std::vector<u8>>(last_addr - cur_addr + 1, 0), 0, MapMemoryBlock(cur_addr, std::make_shared<std::vector<u8>>(map_size, 0), 0,
last_addr - cur_addr + 1, MemoryState::Heap, VMAPermission::None); map_size, MemoryState::Heap, VMAPermission::ReadWrite);
result = map_res.Code(); result = map_res.Code();
if (result.IsSuccess()) { if (result.IsError()) {
mapped_regions.push_back(
std::make_pair(cur_addr, last_addr - cur_addr + 1));
}
}
break; break;
} }
if (state == MemoryState::Unmapped) { mapped_regions.emplace_back(cur_addr, map_size);
const auto map_res = MapMemoryBlock( }
cur_addr, std::make_shared<std::vector<u8>>(vma_start + vma_size - cur_addr, 0),
0, vma_start + vma_size - cur_addr, MemoryState::Heap, VMAPermission::None); // Break once we hit the end of the range.
result = map_res.Code(); if (last_addr <= vma_last) {
if (result.IsSuccess()) {
mapped_regions.push_back(
std::make_pair(cur_addr, vma_start + vma_size - cur_addr));
} else {
break; break;
} }
}
cur_addr = vma_start + vma_size; // Advance to the next block.
vma = FindVMA(cur_addr); cur_addr = vma_end;
ASSERT_MSG(vma != vma_map.end(), "MapPhysicalMemory vma != end"); iter = FindVMA(cur_addr);
ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
} }
} }
// If we failed, unmap memory. // If we failed, unmap memory.
if (result.IsError()) { if (result.IsError()) {
for (const auto& it : mapped_regions) { for (const auto [unmap_address, unmap_size] : mapped_regions) {
const auto unmap_res = UnmapRange(it.first, it.second); ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(),
ASSERT_MSG(unmap_res.IsSuccess(), "MapPhysicalMemory un-map on error"); "MapPhysicalMemory un-map on error");
} }
return result; 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. // Update amount of mapped physical memory.
physical_memory_mapped += size - mapped_size; physical_memory_mapped += size - mapped_size;
@ -458,50 +395,23 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
} }
ResultCode VMManager::UnmapPhysicalMemory(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; VAddr cur_addr = target;
std::size_t mapped_size = 0;
ResultCode result = RESULT_SUCCESS; ResultCode result = RESULT_SUCCESS;
// Check how much of the memory is currently mapped. // Check how much memory is currently mapped.
{ const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size);
auto vma = FindVMA(target); if (mapped_size_result.Failed()) {
ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); return mapped_size_result.Code();
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 we've already unmapped all the memory, return early.
if (last_addr <= (vma_start + vma_size - 1)) { const std::size_t mapped_size = *mapped_size_result;
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) { if (mapped_size == 0) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
}
// Keep track of the memory regions we unmap. // Keep track of the memory regions we unmap.
std::vector<std::pair<u64, u64>> unmapped_regions; std::vector<std::pair<u64, u64>> unmapped_regions;
@ -510,50 +420,45 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
{ {
cur_addr = target; cur_addr = target;
auto vma = FindVMA(target); auto iter = FindVMA(target);
ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
while (true) { while (true) {
const auto vma_start = vma->second.base; const auto& vma = iter->second;
const auto vma_size = vma->second.size; const auto vma_start = vma.base;
const auto state = vma->second.state; const auto vma_end = vma_start + vma.size;
const auto perm = vma->second.permissions; const auto vma_last = vma_end - 1;
// Handle last block. // Unmap the memory block
if (last_addr <= (vma_start + vma_size - 1)) { const auto unmap_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
if (state == MemoryState::Heap) { if (vma.state == MemoryState::Heap) {
result = UnmapRange(cur_addr, last_addr - cur_addr + 1); result = UnmapRange(cur_addr, unmap_size);
if (result.IsSuccess()) { if (result.IsError()) {
unmapped_regions.push_back(
std::make_pair(cur_addr, last_addr - cur_addr + 1));
}
}
break; break;
} }
if (state == MemoryState::Heap) { unmapped_regions.emplace_back(cur_addr, unmap_size);
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; // Break once we hit the end of the range.
vma = FindVMA(cur_addr); if (last_addr <= vma_last) {
ASSERT_MSG(vma != vma_map.end(), "UnmapPhysicalMemory vma != end"); 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. // If we failed, re-map regions.
// TODO: Preserve memory contents? // TODO: Preserve memory contents?
if (result.IsError()) { if (result.IsError()) {
for (const auto& it : unmapped_regions) { for (const auto [map_address, map_size] : unmapped_regions) {
const auto remap_res = const auto remap_res =
MapMemoryBlock(it.first, std::make_shared<std::vector<u8>>(it.second, 0), 0, MapMemoryBlock(map_address, std::make_shared<std::vector<u8>>(map_size, 0), 0,
it.second, MemoryState::Heap, VMAPermission::None); map_size, MemoryState::Heap, VMAPermission::None);
ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error"); 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)); std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
} }
ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address,
std::size_t size) const {
const VAddr end_addr = address + size;
const VAddr last_addr = end_addr - 1;
std::size_t mapped_size = 0;
VAddr cur_addr = address;
auto iter = FindVMA(cur_addr);
ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
while (true) {
const auto& vma = iter->second;
const VAddr vma_start = vma.base;
const VAddr vma_end = vma_start + vma.size;
const VAddr vma_last = vma_end - 1;
// Add size if relevant.
if (vma.state != MemoryState::Unmapped) {
mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr);
}
// Break once we hit the end of the range.
if (last_addr <= vma_last) {
break;
}
// Advance to the next block.
cur_addr = vma_end;
iter = std::next(iter);
ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
}
return MakeResult(mapped_size);
}
ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr address,
std::size_t size) const {
const VAddr end_addr = address + size;
const VAddr last_addr = end_addr - 1;
std::size_t mapped_size = 0;
VAddr cur_addr = address;
auto iter = FindVMA(cur_addr);
ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
while (true) {
const auto& vma = iter->second;
const auto vma_start = vma.base;
const auto vma_end = vma_start + vma.size;
const auto vma_last = vma_end - 1;
const auto state = vma.state;
const auto attr = vma.attribute;
// Memory within region must be free or mapped heap.
if (!((state == MemoryState::Heap && attr == MemoryAttribute::None) ||
(state == MemoryState::Unmapped))) {
return ERR_INVALID_ADDRESS_STATE;
}
// Add size if relevant.
if (state != MemoryState::Unmapped) {
mapped_size += std::min(end_addr - cur_addr, vma_end - cur_addr);
}
// Break once we hit the end of the range.
if (last_addr <= vma_last) {
break;
}
// Advance to the next block.
cur_addr = vma_end;
iter = std::next(iter);
ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
}
return MakeResult(mapped_size);
}
u64 VMManager::GetTotalPhysicalMemoryAvailable() const { u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
LOG_WARNING(Kernel, "(STUBBED) called"); LOG_WARNING(Kernel, "(STUBBED) called");
return 0xF8000000; return 0xF8000000;

View file

@ -303,6 +303,15 @@ struct VirtualMemoryArea {
PAddr paddr = 0; PAddr paddr = 0;
Common::MemoryHookPointer mmio_handler = nullptr; 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<u64> SizeRemainingFromAddress(VAddr address) const;
/// Tests if this area can be merged to the right with `next`. /// Tests if this area can be merged to the right with `next`.
bool CanBeMergedWith(const VirtualMemoryArea& next) const; bool CanBeMergedWith(const VirtualMemoryArea& next) const;
}; };
@ -735,6 +744,13 @@ private:
MemoryAttribute attribute_mask, MemoryAttribute attribute, MemoryAttribute attribute_mask, MemoryAttribute attribute,
MemoryAttribute ignore_mask) const; MemoryAttribute ignore_mask) const;
/// Gets the amount of memory currently mapped (state != Unmapped) in a range.
ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const;
/// Gets the amount of memory unmappable by UnmapPhysicalMemory in a range.
ResultVal<std::size_t> SizeOfUnmappablePhysicalMemoryInRange(VAddr address,
std::size_t size) const;
/** /**
* A map covering the entirety of the managed address space, keyed by the `base` field of each * A map covering the entirety of the managed address space, keyed by the `base` field of each
* VMA. It must always be modified by splitting or merging VMAs, so that the invariant * VMA. It must always be modified by splitting or merging VMAs, so that the invariant