mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-06-08 12:01:39 +12:00
Sort of working OS allocator, except freeing is impossible
This commit is contained in:
parent
2128d5060b
commit
93f5ec7bb4
8 changed files with 186 additions and 40 deletions
|
@ -5,9 +5,9 @@
|
|||
|
||||
using namespace ELFIO;
|
||||
|
||||
std::optional<u32> Memory::loadELF(std::filesystem::path& path) {
|
||||
std::optional<u32> Memory::loadELF(std::ifstream& file) {
|
||||
elfio reader;
|
||||
if (!reader.load(path.string())) {
|
||||
if (!file.good() || !reader.load(file)) {
|
||||
printf("Woops failed to load ELF\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -39,8 +39,17 @@ std::optional<u32> Memory::loadELF(std::filesystem::path& path) {
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
u32 fcramAddr = vaddr - VirtualAddrs::ExecutableStart;
|
||||
if (memorySize & pageMask) {
|
||||
// Round up the size of the ELF segment to a page (4KB) boundary, as the OS can only alloc this way
|
||||
memorySize = (memorySize + pageSize - 1) & -pageSize;
|
||||
Helpers::warn("Rounding ELF segment size to %08X\n", memorySize);
|
||||
}
|
||||
|
||||
u32 fcramAddr = findPaddr(memorySize).value();
|
||||
std::memcpy(&fcram[fcramAddr], data, fileSize);
|
||||
|
||||
// Allocate the segment on the OS side
|
||||
allocateMemory(vaddr, fcramAddr, memorySize, true);
|
||||
}
|
||||
|
||||
return static_cast<u32>(reader.get_entry());
|
||||
|
|
|
@ -23,6 +23,7 @@ static constexpr bool isAligned(u32 value) {
|
|||
// Result ControlMemory(u32* outaddr, u32 addr0, u32 addr1, u32 size,
|
||||
// MemoryOperation operation, MemoryPermission permissions)
|
||||
// This has a weird ABI documented here https://www.3dbrew.org/wiki/Kernel_ABI
|
||||
// TODO: Does this need to write to outaddr?
|
||||
void Kernel::controlMemory() {
|
||||
u32 operation = regs[0]; // The base address is written here
|
||||
u32 addr0 = regs[1];
|
||||
|
@ -48,17 +49,22 @@ void Kernel::controlMemory() {
|
|||
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
|
||||
}
|
||||
|
||||
printf("ControlMemory(addr0 = %08X, addr1 = %08X, size = %X, operation = %X (%c%c%c)%s\n",
|
||||
printf("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n",
|
||||
addr0, addr1, size, operation, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', linear ? ", linear" : ""
|
||||
);
|
||||
|
||||
switch (operation & 0xFF) {
|
||||
case Operation::Commit:
|
||||
case Operation::Commit: {
|
||||
std::optional<u32> address = mem.allocateMemory(addr0, 0, size, linear);
|
||||
if (!address.has_value())
|
||||
Helpers::panic("ControlMemory: Failed to allocate memory");
|
||||
|
||||
regs[1] = address.value();
|
||||
break;
|
||||
}
|
||||
|
||||
default: Helpers::panic("ControlMemory: unknown operation %X\n", operation);
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[1] = 0xF3180131;
|
||||
}
|
|
@ -83,7 +83,7 @@ void Kernel::getResourceLimitCurrentValues() {
|
|||
s32 Kernel::getCurrentResourceValue(const KernelObject* limit, u32 resourceName) {
|
||||
const auto data = static_cast<ResourceLimits*>(limit->data);
|
||||
switch (resourceName) {
|
||||
case ResourceType::Commit: return data->currentCommit;
|
||||
case ResourceType::Commit: return mem.usedUserMemory;
|
||||
default: Helpers::panic("Attempted to get current value of unknown kernel resource: %d\n", resourceName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +1,116 @@
|
|||
#include "memory.hpp"
|
||||
#include <cassert>
|
||||
|
||||
Memory::Memory() {
|
||||
fcram = new uint8_t[128_MB]();
|
||||
fcram = new uint8_t[FCRAM_SIZE]();
|
||||
readTable.resize(totalPageCount, 0);
|
||||
writeTable.resize(totalPageCount, 0);
|
||||
}
|
||||
|
||||
constexpr u32 fcramPageCount = 128_MB / pageSize;
|
||||
void Memory::reset() {
|
||||
// Unallocate all memory
|
||||
usedFCRAMPages.reset();
|
||||
usedUserMemory = 0_MB;
|
||||
|
||||
// Map 63MB of FCRAM in the executable section as read/write
|
||||
// TODO: This should probably be read-only, but making it r/w shouldn't hurt?
|
||||
// Because if that were the case then writes would cause data aborts, so games wouldn't write to read-only memory
|
||||
u32 executablePageCount = VirtualAddrs::MaxExeSize / pageSize;
|
||||
u32 page = VirtualAddrs::ExecutableStart / pageSize;
|
||||
for (u32 i = 0; i < executablePageCount; i++) {
|
||||
const auto pointer = (uintptr_t)&fcram[i * pageSize];
|
||||
readTable[page] = pointer;
|
||||
writeTable[page++] = pointer;
|
||||
for (u32 i = 0; i < totalPageCount; i++) {
|
||||
readTable[i] = 0;
|
||||
writeTable[i] = 0;
|
||||
}
|
||||
|
||||
// Map stack pages as R/W
|
||||
// We have 16KB for the stack, so we allocate the last 4 pages of FCRAM for the stack
|
||||
u32 stackPageCount = VirtualAddrs::StackSize / pageSize;
|
||||
u32 fcramStackPage = fcramPageCount - 4;
|
||||
page = VirtualAddrs::StackBottom / pageSize;
|
||||
// We have 16KB for the stack, so we allocate the last 16KB of APPLICATION FCRAM for the stack
|
||||
u32 basePaddrForStack = FCRAM_APPLICATION_SIZE - VirtualAddrs::StackSize;
|
||||
allocateMemory(VirtualAddrs::StackBottom, basePaddrForStack, VirtualAddrs::StackSize, true);
|
||||
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
auto pointer = (uintptr_t)&fcram[fcramStackPage * pageSize];
|
||||
readTable[page] = pointer;
|
||||
writeTable[page++] = pointer;
|
||||
fcramStackPage++;
|
||||
// And map 4KB of FCRAM before the stack for TLS
|
||||
u32 basePaddrForTLS = basePaddrForStack - VirtualAddrs::TLSSize;
|
||||
allocateMemory(VirtualAddrs::TLSBase, basePaddrForTLS, VirtualAddrs::TLSSize, true);
|
||||
}
|
||||
|
||||
std::optional<u32> Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool linear, bool r, bool w, bool x) {
|
||||
// Kernel-allocated memory & size must always be aligned to a page boundary
|
||||
// Additionally assert we don't OoM and that we don't try to allocate physical FCRAM past what's available to userland
|
||||
assert(isAligned(vaddr) && isAligned(paddr) && isAligned(size));
|
||||
assert(size <= FCRAM_APPLICATION_SIZE);
|
||||
assert(usedUserMemory + size <= FCRAM_APPLICATION_SIZE);
|
||||
assert(paddr + size <= FCRAM_APPLICATION_SIZE);
|
||||
|
||||
// Amount of available user FCRAM pages and FCRAM pages to allocate respectively
|
||||
const u32 availablePageCount = (FCRAM_APPLICATION_SIZE - usedUserMemory) / pageSize;
|
||||
const u32 neededPageCount = size / pageSize;
|
||||
|
||||
assert(availablePageCount >= neededPageCount);
|
||||
|
||||
// If the vaddr is 0 that means we need to select our own
|
||||
// Depending on whether our mapping should be linear or not we allocate from one of the 2 typical heap spaces
|
||||
// We don't plan on implementing freeing any time soon, so we can pick added userUserMemory to the vaddr base to
|
||||
// Get the full vaddr.
|
||||
// TODO: Fix this
|
||||
if (vaddr == 0) {
|
||||
vaddr = usedUserMemory + (linear ? VirtualAddrs::LinearHeapStart : VirtualAddrs::NormalHeapStart);
|
||||
}
|
||||
|
||||
// Map 1 page of FCRAM for thread-local storage
|
||||
u32 fcramTLSPage = fcramPageCount - 5;
|
||||
page = VirtualAddrs::TLSBase / pageSize;
|
||||
usedUserMemory += size;
|
||||
|
||||
auto pointer = (uintptr_t)&fcram[fcramTLSPage * pageSize];
|
||||
readTable[page] = pointer;
|
||||
writeTable[page++] = pointer;
|
||||
// If the paddr is 0, that means we need to select our own
|
||||
// TODO: Fix. This method always tries to allocate blocks linearly.
|
||||
// However, if the allocation is non-linear, the panic will trigger when it shouldn't.
|
||||
// Non-linear allocation needs special handling
|
||||
if (paddr == 0) {
|
||||
std::optional<u32> newPaddr = findPaddr(size);
|
||||
if (!newPaddr.has_value())
|
||||
Helpers::panic("Failed to find paddr");
|
||||
|
||||
paddr = newPaddr.value();
|
||||
assert(paddr + size <= FCRAM_APPLICATION_SIZE);
|
||||
}
|
||||
|
||||
// Do linear mapping
|
||||
u32 virtualPage = vaddr >> pageShift;
|
||||
u32 physPage = paddr >> pageShift; // TODO: Special handle when non-linear mapping is necessary
|
||||
for (u32 i = 0; i < neededPageCount; i++) {
|
||||
if (r) {
|
||||
readTable[virtualPage] = uintptr_t(&fcram[physPage * pageSize]);
|
||||
}
|
||||
if (w) {
|
||||
writeTable[virtualPage] = uintptr_t(&fcram[physPage * pageSize]);
|
||||
}
|
||||
|
||||
// Mark FCRAM page as allocated and go on
|
||||
usedFCRAMPages[physPage] = true;
|
||||
virtualPage++;
|
||||
physPage++;
|
||||
}
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
// Find a paddr which we can use for allocating "size" bytes
|
||||
std::optional<u32> Memory::findPaddr(u32 size) {
|
||||
const u32 neededPages = size / pageSize;
|
||||
|
||||
// The FCRAM page we're testing to see if it's appropriate to use
|
||||
u32 candidatePage = 0;
|
||||
// The number of linear available pages we could find starting from this candidate page.
|
||||
// If this ends up >= than neededPages then the paddr is good (ie we can use the candidate page as a base address)
|
||||
u32 counter = 0;
|
||||
|
||||
for (u32 i = 0; i < FCRAM_PAGE_COUNT; i++) {
|
||||
if (usedFCRAMPages[i]) { // Page is occupied already, go to new candidate
|
||||
candidatePage = i + 1;
|
||||
counter = 0;
|
||||
}
|
||||
else { // Our candidate page has 1 mor
|
||||
counter++;
|
||||
// Check if there's enough free memory to use this page
|
||||
if (counter == neededPages) {
|
||||
return candidatePage * pageSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find any page :(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
u8 Memory::read8(u32 vaddr) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue