202 lines
8.0 KiB
C++
202 lines
8.0 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
|
|
|
#include "memory.h"
|
|
#include "types/KProcess.h"
|
|
|
|
namespace skyline::kernel {
|
|
ChunkDescriptor *MemoryManager::GetChunk(u64 address) {
|
|
auto chunk = std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
|
|
return address < chunk.address;
|
|
});
|
|
|
|
if (chunk-- != chunkList.begin()) {
|
|
if ((chunk->address + chunk->size) > address)
|
|
return chunk.base();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
BlockDescriptor *MemoryManager::GetBlock(u64 address, ChunkDescriptor *chunk) {
|
|
if (!chunk)
|
|
chunk = GetChunk(address);
|
|
|
|
if (chunk) {
|
|
auto block = std::upper_bound(chunk->blockList.begin(), chunk->blockList.end(), address, [](const u64 address, const BlockDescriptor &block) -> bool {
|
|
return address < block.address;
|
|
});
|
|
|
|
if (block-- != chunk->blockList.begin()) {
|
|
if ((block->address + block->size) > address)
|
|
return block.base();
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void MemoryManager::InsertChunk(const ChunkDescriptor &chunk) {
|
|
auto upperChunk = std::upper_bound(chunkList.begin(), chunkList.end(), chunk.address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
|
|
return address < chunk.address;
|
|
});
|
|
|
|
if (upperChunk != chunkList.begin()) {
|
|
auto lowerChunk = std::prev(upperChunk);
|
|
|
|
if (lowerChunk->address + lowerChunk->size > chunk.address)
|
|
throw exception("InsertChunk: Descriptors are colliding: 0x{:X} - 0x{:X} and 0x{:X} - 0x{:X}", lowerChunk->address, lowerChunk->address + lowerChunk->size, chunk.address, chunk.address + chunk.size);
|
|
}
|
|
|
|
chunkList.insert(upperChunk, chunk);
|
|
}
|
|
|
|
void MemoryManager::DeleteChunk(u64 address) {
|
|
for (auto chunk = chunkList.begin(), end = chunkList.end(); chunk != end;) {
|
|
if (chunk->address <= address && (chunk->address + chunk->size) > address)
|
|
chunk = chunkList.erase(chunk);
|
|
else
|
|
++chunk;
|
|
}
|
|
}
|
|
|
|
void MemoryManager::ResizeChunk(ChunkDescriptor *chunk, size_t size) {
|
|
if (chunk->blockList.size() == 1) {
|
|
chunk->blockList.begin()->size = size;
|
|
} else if (size > chunk->size) {
|
|
auto begin = chunk->blockList.begin();
|
|
auto end = std::prev(chunk->blockList.end());
|
|
|
|
BlockDescriptor block{
|
|
.address = (end->address + end->size),
|
|
.size = (chunk->address + size) - (end->address + end->size),
|
|
.permission = begin->permission,
|
|
.attributes = begin->attributes,
|
|
};
|
|
|
|
chunk->blockList.push_back(block);
|
|
} else if (size < chunk->size) {
|
|
auto endAddress = chunk->address + size;
|
|
|
|
for (auto block = chunk->blockList.begin(), end = chunk->blockList.end(); block != end;) {
|
|
if (block->address > endAddress)
|
|
block = chunk->blockList.erase(block);
|
|
else
|
|
++block;
|
|
}
|
|
|
|
auto end = std::prev(chunk->blockList.end());
|
|
end->size = endAddress - end->address;
|
|
}
|
|
|
|
chunk->size = size;
|
|
}
|
|
|
|
void MemoryManager::InsertBlock(ChunkDescriptor *chunk, const BlockDescriptor block) {
|
|
if (chunk->address + chunk->size < block.address + block.size)
|
|
throw exception("InsertBlock: Inserting block past chunk end is not allowed");
|
|
|
|
for (auto iter = chunk->blockList.begin(); iter != chunk->blockList.end(); iter++) {
|
|
if (iter->address <= block.address) {
|
|
if ((iter->address + iter->size) > block.address) {
|
|
if (iter->address == block.address && iter->size == block.size) {
|
|
iter->attributes = block.attributes;
|
|
iter->permission = block.permission;
|
|
} else {
|
|
auto endBlock = *iter;
|
|
endBlock.address = (block.address + block.size);
|
|
endBlock.size = (iter->address + iter->size) - endBlock.address;
|
|
|
|
iter->size = iter->address - block.address;
|
|
chunk->blockList.insert(std::next(iter), {block, endBlock});
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw exception("InsertBlock: Block offset not present within current block list");
|
|
}
|
|
|
|
void MemoryManager::InitializeRegions(u64 address, u64 size, const memory::AddressSpaceType type) {
|
|
switch (type) {
|
|
case memory::AddressSpaceType::AddressSpace32Bit:
|
|
throw exception("32-bit address spaces are not supported");
|
|
|
|
case memory::AddressSpaceType::AddressSpace36Bit: {
|
|
base.address = constant::BaseAddress;
|
|
base.size = 0xFF8000000;
|
|
code.address = base.address;
|
|
code.size = 0x78000000;
|
|
if (code.address > address || (code.size - (address - code.address)) < size)
|
|
throw exception("Code mapping larger than 36-bit code region");
|
|
alias.address = code.address + code.size;
|
|
alias.size = 0x180000000;
|
|
stack.address = alias.address;
|
|
stack.size = alias.size;
|
|
heap.address = alias.address + alias.size;
|
|
heap.size = 0x180000000;
|
|
tlsIo.address = code.address;
|
|
tlsIo.size = 0;
|
|
break;
|
|
}
|
|
|
|
case memory::AddressSpaceType::AddressSpace39Bit: {
|
|
base.address = constant::BaseAddress;
|
|
base.size = 0x7FF8000000;
|
|
code.address = util::AlignDown(address, 0x200000);
|
|
code.size = util::AlignUp(address + size, 0x200000) - code.address;
|
|
alias.address = code.address + code.size;
|
|
alias.size = 0x1000000000;
|
|
heap.address = alias.address + alias.size;
|
|
heap.size = 0x180000000;
|
|
stack.address = heap.address + heap.size;
|
|
stack.size = 0x80000000;
|
|
tlsIo.address = stack.address + stack.size;
|
|
tlsIo.size = 0x1000000000;
|
|
break;
|
|
}
|
|
}
|
|
|
|
state.logger->Debug("Region Map:\nCode Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nAlias Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nHeap Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nStack Region: 0x{:X} - 0x{:X} (Size: 0x{:X})\nTLS/IO Region: 0x{:X} - 0x{:X} (Size: 0x{:X})", code.address, code.address + code.size, code.size, alias.address, alias.address + alias.size, alias.size, heap.address, heap
|
|
.address + heap.size, heap.size, stack.address, stack.address + stack.size, stack.size, tlsIo.address, tlsIo.address + tlsIo.size, tlsIo.size);
|
|
}
|
|
|
|
MemoryManager::MemoryManager(const DeviceState &state) : state(state) {}
|
|
|
|
std::optional<DescriptorPack> MemoryManager::Get(u64 address) {
|
|
auto chunk = GetChunk(address);
|
|
|
|
if (chunk)
|
|
return DescriptorPack{*GetBlock(address, chunk), *chunk};
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
memory::Region MemoryManager::GetRegion(memory::Regions region) {
|
|
switch (region) {
|
|
case memory::Regions::Base:
|
|
return base;
|
|
case memory::Regions::Code:
|
|
return code;
|
|
case memory::Regions::Alias:
|
|
return alias;
|
|
case memory::Regions::Heap:
|
|
return heap;
|
|
case memory::Regions::Stack:
|
|
return stack;
|
|
case memory::Regions::TlsIo:
|
|
return tlsIo;
|
|
}
|
|
}
|
|
|
|
size_t MemoryManager::GetProgramSize() {
|
|
size_t size = 0;
|
|
|
|
for (const auto &chunk : chunkList)
|
|
size += chunk.size;
|
|
|
|
return size;
|
|
}
|
|
}
|