kernel/vm_manager: Tidy up heap allocation code

Another holdover from citra that can be tossed out is the notion of the
heap needing to be allocated in different addresses. On the switch, the
base address of the heap will always be managed by the memory allocator
in the kernel, so this doesn't need to be specified in the function's
interface itself.

The heap on the switch is always allocated with read/write permissions,
so we don't need to add specifying the memory permissions as part of the
heap allocation itself either.

This also corrects the error code returned from within the function.
If the size of the heap is larger than the entire heap region, then the
kernel will report an out of memory condition.
This commit is contained in:
Lioncash 2019-03-24 15:24:52 -04:00
parent 1665b70cc6
commit 586cab6172
3 changed files with 39 additions and 29 deletions

View file

@ -174,10 +174,8 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
return ERR_INVALID_SIZE;
}
auto& vm_manager = Core::CurrentProcess()->VMManager();
const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
const auto alloc_result =
vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
auto& vm_manager = Core::System::GetInstance().Kernel().CurrentProcess()->VMManager();
const auto alloc_result = vm_manager.HeapAllocate(heap_size);
if (alloc_result.Failed()) {
return alloc_result.Code();

View file

@ -256,39 +256,37 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
return RESULT_SUCCESS;
}
ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
if (!IsWithinHeapRegion(target, size)) {
return ERR_INVALID_ADDRESS;
ResultVal<VAddr> VMManager::HeapAllocate(u64 size) {
if (size > GetHeapRegionSize()) {
return ERR_OUT_OF_MEMORY;
}
if (heap_memory == nullptr) {
// Initialize heap
heap_memory = std::make_shared<std::vector<u8>>();
heap_start = heap_end = target;
heap_memory = std::make_shared<std::vector<u8>>(size);
heap_end = heap_region_base + size;
} else {
UnmapRange(heap_start, heap_end - heap_start);
UnmapRange(heap_region_base, GetCurrentHeapSize());
}
// If necessary, expand backing vector to cover new heap extents.
if (target < heap_start) {
heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
heap_start = target;
RefreshMemoryBlockMappings(heap_memory.get());
}
if (target + size > heap_end) {
heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
heap_end = target + size;
RefreshMemoryBlockMappings(heap_memory.get());
}
ASSERT(heap_end - heap_start == heap_memory->size());
if (size > GetCurrentHeapSize()) {
const u64 alloc_size = size - GetCurrentHeapSize();
CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
MemoryState::Heap));
Reprotect(vma, perms);
heap_memory->insert(heap_memory->end(), alloc_size, 0);
heap_end = heap_region_base + size;
RefreshMemoryBlockMappings(heap_memory.get());
}
ASSERT(GetCurrentHeapSize() == heap_memory->size());
const auto mapping_result =
MapMemoryBlock(heap_region_base, heap_memory, 0, size, MemoryState::Heap);
if (mapping_result.Failed()) {
return mapping_result.Code();
}
heap_used = size;
return MakeResult<VAddr>(heap_end - size);
return MakeResult<VAddr>(heap_region_base);
}
ResultCode VMManager::HeapFree(VAddr target, u64 size) {
@ -778,6 +776,10 @@ u64 VMManager::GetHeapRegionSize() const {
return heap_region_end - heap_region_base;
}
u64 VMManager::GetCurrentHeapSize() const {
return heap_end - heap_region_base;
}
bool VMManager::IsWithinHeapRegion(VAddr address, u64 size) const {
return IsInsideAddressRange(address, size, GetHeapRegionBaseAddress(),
GetHeapRegionEndAddress());

View file

@ -380,7 +380,7 @@ public:
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultVal<VAddr> HeapAllocate(u64 size);
ResultCode HeapFree(VAddr target, u64 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
@ -469,6 +469,13 @@ public:
/// Gets the total size of the heap region in bytes.
u64 GetHeapRegionSize() const;
/// Gets the total size of the current heap in bytes.
///
/// @note This is the current allocated heap size, not the size
/// of the region it's allowed to exist within.
///
u64 GetCurrentHeapSize() const;
/// Determines whether or not the specified range is within the heap region.
bool IsWithinHeapRegion(VAddr address, u64 size) const;
@ -628,9 +635,12 @@ private:
// This makes deallocation and reallocation of holes fast and keeps process memory contiguous
// in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
std::shared_ptr<std::vector<u8>> heap_memory;
// The left/right bounds of the address space covered by heap_memory.
VAddr heap_start = 0;
// The end of the currently allocated heap. This is not an inclusive
// end of the range. This is essentially 'base_address + current_size'.
VAddr heap_end = 0;
// Indicates how many bytes from the current heap are currently used.
u64 heap_used = 0;
};
} // namespace Kernel