Memory rework pt 2 (#801)
* Memory: Rework FCRAM management entirely Disables a lot of functionality... but I didn't want to commit too much to this commit Also reworks virtual memory management somewhat (but needs more work) * Accurately handle MemoryState for virtual memory Previously all non-free blocks were marked as Reserved * Memory: Consolidate state and permission changes Can now use a single function to change either state, permissions, or both Also merge vmem blocks that have the same state and permissions * Memory: Fix double reset for FCRAM manager Fix minor bug with permission tracking * Memory: Implement Protect operation in ControlMemory * Memory: Implement Unmap in ControlMemory Also do a sanity check to make sure the memory region is free for linear allocations * Memory: Make TLS only 0x200 bytes for each thread Also move TLS to Base region * RO: Unmap CROs when unloaded Thanks @noumidev * Kernel: Return used app memory for Commit ResourceLimit Not quite correct, but nothing to be done until process management is improved Also remove the stack limit for CXIs (thanks amogus) * Kernel: Report used app memory for GetProcessInfo 2 Not really correct, but it should be accurate for applications at least * Formatting changes * Initial fastmem support * PCSX2 fastmem depression * Move away from PCSX2 fastmem * Add enum_flag_ops.hpp * Finally building on Windows * Almost got a PoC * Fix arm64 builds * This somehow works * This also works... * Properly fix fastmem * Add free region manager * Update boost * Add ScopeExit * Comment out asserts on Linux/Mac/Android * Comment out ASSERT_MSG asserts too * Fix derp * Attempt to fix Android * Disable fastmem on Android * Fix Android again maybe pt 2 * android pls * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA * Update host_memory.cpp * Properly reset memory arena on reset * Proper ashmem code for Android * more * Add temporary Android buildjet script for faster prototype builds * Fix fastmem (again) * Clean up shared memory * Remove Android BuildJet runner * a * Revert "a" This reverts commit 5443ad6f2a794c19c9b1a1567ca1c7f58eed78cd. * Re-add ELF support * Re-add 3DSX support * GetSystemInfo, GetProcessInfo: Memory sizes should be in bytes * Update Boost * Update metal-cpp * Fix metal renderer compilation * Fix fastmem mapping * Clean up fastmem code * Fix oopsie again * Emulator: Reorder struct * Kernel types: Cleanup * Cleanup * More cleanup * Make invalid mprotects warn instead of panicking * Add setting for toggling fastmem * More cleanup * Properly initialize BSS to zeroes * Remove unused code * Formatting * Cleanup * Memory/CRO: Workaround for Pokemon XY * NCSD loader: Fix BSS (again) * NCSD loader: Fix BSS (again) (again) * More memory fixes * Memory: Remove unused code * FS: Warn on unimplemented functions instead of panic * Update software_keyboard.cpp * Libretro: Add fastmem option * FRD: Stub SaveLocalAccountData --------- Co-authored-by: PSI-Rockin <PSI-Rockin@users.noreply.github.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
|
||||
CPU::CPU(Memory& mem, Kernel& kernel, Emulator& emu) : mem(mem), emu(emu), scheduler(emu.getScheduler()), env(mem, kernel, emu.getScheduler()) {
|
||||
cp15 = std::make_shared<CP15>();
|
||||
mem.setCPUTicks(getTicksRef());
|
||||
|
||||
Dynarmic::A32::UserConfig config;
|
||||
config.arch_version = Dynarmic::A32::ArchVersion::v6K;
|
||||
@@ -15,6 +16,12 @@ CPU::CPU(Memory& mem, Kernel& kernel, Emulator& emu) : mem(mem), emu(emu), sched
|
||||
config.global_monitor = &exclusiveMonitor;
|
||||
config.processor_id = 0;
|
||||
|
||||
if (mem.isFastmemEnabled()) {
|
||||
config.fastmem_pointer = u64(mem.getFastmemArenaBase());
|
||||
} else {
|
||||
config.fastmem_pointer = std::nullopt;
|
||||
}
|
||||
|
||||
jit = std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ Result::HorizonResult SoftwareKeyboardApplet::start(const MemoryBlock* sharedMem
|
||||
}
|
||||
mem.write16(textAddress, 0); // Write UTF-16 null terminator
|
||||
|
||||
// Temporarily hardcode the pressed button to be the firs tone
|
||||
// Temporarily hardcode the pressed button to be the first one
|
||||
switch (config.numButtonsM1) {
|
||||
case SoftwareKeyboardButtonConfig::SingleButton: config.returnCode = SoftwareKeyboardResult::D0Click; break;
|
||||
case SoftwareKeyboardButtonConfig::DualButton: config.returnCode = SoftwareKeyboardResult::D1Click1; break;
|
||||
|
||||
102
src/core/kernel/fcram.cpp
Normal file
102
src/core/kernel/fcram.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "fcram.hpp"
|
||||
|
||||
#include "memory.hpp"
|
||||
|
||||
void KFcram::Region::reset(u32 start, size_t size) {
|
||||
this->start = start;
|
||||
pages = size >> 12;
|
||||
freePages = pages;
|
||||
|
||||
Block initialBlock(pages, 0);
|
||||
blocks.clear();
|
||||
blocks.push_back(initialBlock);
|
||||
}
|
||||
|
||||
void KFcram::Region::alloc(std::list<FcramBlock>& out, s32 allocPages, bool linear) {
|
||||
for (auto it = blocks.begin(); it != blocks.end(); it++) {
|
||||
if (it->used) continue;
|
||||
|
||||
// On linear allocations, only a single contiguous block may be used
|
||||
if (it->pages < allocPages && linear) continue;
|
||||
|
||||
// If the current block is bigger than the allocation, split it
|
||||
if (it->pages > allocPages) {
|
||||
Block newBlock(it->pages - allocPages, it->pageOffset + allocPages);
|
||||
it->pages = allocPages;
|
||||
blocks.insert(it, newBlock);
|
||||
}
|
||||
|
||||
// Mark the block as allocated and add it to the output list
|
||||
it->used = true;
|
||||
allocPages -= it->pages;
|
||||
freePages -= it->pages;
|
||||
|
||||
u32 paddr = start + (it->pageOffset << 12);
|
||||
FcramBlock outBlock(paddr, it->pages);
|
||||
out.push_back(outBlock);
|
||||
|
||||
if (allocPages < 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Official kernel panics here
|
||||
Helpers::panic("Failed to allocate FCRAM, not enough guest memory");
|
||||
}
|
||||
|
||||
u32 KFcram::Region::getUsedCount() { return pages - freePages; }
|
||||
u32 KFcram::Region::getFreeCount() { return freePages; }
|
||||
|
||||
KFcram::KFcram(Memory& mem) : mem(mem) {}
|
||||
|
||||
void KFcram::reset(size_t ramSize, size_t appSize, size_t sysSize, size_t baseSize) {
|
||||
fcram = mem.getFCRAM();
|
||||
refs = std::unique_ptr<u32>(new u32[ramSize >> 12]);
|
||||
std::memset(refs.get(), 0, (ramSize >> 12) * sizeof(u32));
|
||||
|
||||
appRegion.reset(0, appSize);
|
||||
sysRegion.reset(appSize, sysSize);
|
||||
baseRegion.reset(appSize + sysSize, baseSize);
|
||||
}
|
||||
|
||||
void KFcram::alloc(FcramBlockList& out, s32 pages, FcramRegion region, bool linear) {
|
||||
switch (region) {
|
||||
case FcramRegion::App: appRegion.alloc(out, pages, linear); break;
|
||||
case FcramRegion::Sys: sysRegion.alloc(out, pages, linear); break;
|
||||
case FcramRegion::Base: baseRegion.alloc(out, pages, linear); break;
|
||||
default: Helpers::panic("Invalid FCRAM region chosen for allocation!"); break;
|
||||
}
|
||||
|
||||
incRef(out);
|
||||
}
|
||||
|
||||
void KFcram::incRef(FcramBlockList& list) {
|
||||
for (auto it = list.begin(); it != list.end(); it++) {
|
||||
for (int i = 0; i < it->pages; i++) {
|
||||
u32 index = (it->paddr >> 12) + i;
|
||||
refs.get()[index]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KFcram::decRef(FcramBlockList& list) {
|
||||
for (auto it = list.begin(); it != list.end(); it++) {
|
||||
for (int i = 0; i < it->pages; i++) {
|
||||
u32 index = (it->paddr >> 12) + i;
|
||||
refs.get()[index]--;
|
||||
|
||||
if (!refs.get()[index]) {
|
||||
Helpers::panic("TODO: Freeing FCRAM");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 KFcram::getUsedCount(FcramRegion region) {
|
||||
switch (region) {
|
||||
case FcramRegion::App: return appRegion.getUsedCount();
|
||||
case FcramRegion::Sys: return sysRegion.getUsedCount();
|
||||
case FcramRegion::Base: return baseRegion.getUsedCount();
|
||||
default: Helpers::panic("Invalid FCRAM region in getUsedCount!");
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ idle_thread_main:
|
||||
b idle_thread_main
|
||||
*/
|
||||
|
||||
using namespace KernelMemoryTypes;
|
||||
|
||||
static constexpr u8 idleThreadCode[] = {
|
||||
0x00, 0x00, 0xA0, 0xE3, // mov r0, #0
|
||||
0x00, 0x10, 0xA0, 0xE3, // mov r1, #0
|
||||
@@ -27,18 +29,16 @@ static constexpr u8 idleThreadCode[] = {
|
||||
// Set up an idle thread to run when no thread is able to run
|
||||
void Kernel::setupIdleThread() {
|
||||
Thread& t = threads[idleThreadIndex];
|
||||
constexpr u32 codeAddress = 0xBFC00000;
|
||||
|
||||
// Reserve some memory for the idle thread's code. We map this memory to vaddr BFC00000 which is not userland-accessible
|
||||
// Reserve some memory for the idle thread's code. We map this memory to vaddr 3FC00000 which shouldn't be accessed by applications
|
||||
// We only allocate 4KB (1 page) because our idle code is pretty small
|
||||
const u32 fcramIndex = mem.allocateSysMemory(Memory::pageSize);
|
||||
auto vaddr = mem.allocateMemory(codeAddress, fcramIndex, Memory::pageSize, true, true, false, true, false, true);
|
||||
if (!vaddr.has_value() || vaddr.value() != codeAddress) {
|
||||
constexpr u32 codeAddress = 0x3FC00000;
|
||||
if (!mem.allocMemory(codeAddress, 1, FcramRegion::Base, true, true, false, MemoryState::Locked)) {
|
||||
Helpers::panic("Failed to setup idle thread");
|
||||
}
|
||||
|
||||
// Copy idle thread code to the allocated FCRAM
|
||||
std::memcpy(&mem.getFCRAM()[fcramIndex], idleThreadCode, sizeof(idleThreadCode));
|
||||
mem.copyToVaddr(codeAddress, idleThreadCode, sizeof(idleThreadCode));
|
||||
|
||||
t.entrypoint = codeAddress;
|
||||
t.initialSP = 0;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "kernel_types.hpp"
|
||||
|
||||
Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config, LuaManager& lua)
|
||||
: cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this, config, lua) {
|
||||
: cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this, config, lua), fcramManager(mem) {
|
||||
objects.reserve(512); // Make room for a few objects to avoid further memory allocs later
|
||||
mutexHandles.reserve(8);
|
||||
portHandles.reserve(32);
|
||||
@@ -180,9 +180,7 @@ void Kernel::reset() {
|
||||
}
|
||||
|
||||
// Get pointer to thread-local storage
|
||||
u32 Kernel::getTLSPointer() {
|
||||
return VirtualAddrs::TLSBase + currentThreadIndex * VirtualAddrs::TLSSize;
|
||||
}
|
||||
u32 Kernel::getTLSPointer() { return VirtualAddrs::TLSBase + currentThreadIndex * VirtualAddrs::TLSSize; }
|
||||
|
||||
// Result CloseHandle(Handle handle)
|
||||
void Kernel::svcCloseHandle() {
|
||||
@@ -271,7 +269,7 @@ void Kernel::getProcessInfo() {
|
||||
// According to 3DBrew: Amount of private (code, data, heap) memory used by the process + total supervisor-mode
|
||||
// stack size + page-rounded size of the external handle table
|
||||
case 2:
|
||||
regs[1] = mem.getUsedUserMem();
|
||||
regs[1] = fcramManager.getUsedCount(FcramRegion::App) * Memory::pageSize;
|
||||
regs[2] = 0;
|
||||
break;
|
||||
|
||||
@@ -364,7 +362,7 @@ void Kernel::getSystemInfo() {
|
||||
switch (subtype) {
|
||||
// Total used memory size in the APPLICATION memory region
|
||||
case 1:
|
||||
regs[1] = mem.getUsedUserMem();
|
||||
regs[1] = fcramManager.getUsedCount(FcramRegion::App) * Memory::pageSize;
|
||||
regs[2] = 0;
|
||||
break;
|
||||
|
||||
|
||||
@@ -30,10 +30,10 @@ namespace MemoryPermissions {
|
||||
};
|
||||
}
|
||||
|
||||
using namespace KernelMemoryTypes;
|
||||
|
||||
// Returns whether "value" is aligned to a page boundary (Ie a boundary of 4096 bytes)
|
||||
static constexpr bool isAligned(u32 value) {
|
||||
return (value & 0xFFF) == 0;
|
||||
}
|
||||
static constexpr bool isAligned(u32 value) { return (value & 0xFFF) == 0; }
|
||||
|
||||
// Result ControlMemory(u32* outaddr, u32 addr0, u32 addr1, u32 size,
|
||||
// MemoryOperation operation, MemoryPermission permissions)
|
||||
@@ -44,6 +44,7 @@ void Kernel::controlMemory() {
|
||||
u32 addr0 = regs[1];
|
||||
u32 addr1 = regs[2];
|
||||
u32 size = regs[3];
|
||||
u32 pages = size >> 12; // Official kernel truncates nonaligned sizes
|
||||
u32 perms = regs[4];
|
||||
|
||||
if (perms == MemoryPermissions::DontCare) {
|
||||
@@ -61,7 +62,7 @@ void Kernel::controlMemory() {
|
||||
Helpers::panic("ControlMemory: attempted to allocate executable memory");
|
||||
}
|
||||
|
||||
if (!isAligned(addr0) || !isAligned(addr1) || !isAligned(size)) {
|
||||
if (!isAligned(addr0) || !isAligned(addr1)) {
|
||||
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
|
||||
}
|
||||
|
||||
@@ -72,22 +73,54 @@ void Kernel::controlMemory() {
|
||||
|
||||
switch (operation & 0xFF) {
|
||||
case Operation::Commit: {
|
||||
std::optional<u32> address = mem.allocateMemory(addr0, 0, size, linear, r, w, x, true);
|
||||
if (!address.has_value()) {
|
||||
Helpers::panic("ControlMemory: Failed to allocate memory");
|
||||
// TODO: base this from the exheader
|
||||
auto region = FcramRegion::App;
|
||||
|
||||
u32 outAddr = 0;
|
||||
if (linear) {
|
||||
if (!mem.allocMemoryLinear(outAddr, addr0, pages, region, r, w, false)) {
|
||||
Helpers::panic("ControlMemory: Failed to allocate linear memory");
|
||||
}
|
||||
} else {
|
||||
if (!mem.allocMemory(addr0, pages, region, r, w, false, MemoryState::Private)) {
|
||||
Helpers::panic("ControlMemory: Failed to allocate memory");
|
||||
}
|
||||
|
||||
outAddr = addr0;
|
||||
}
|
||||
|
||||
regs[1] = address.value();
|
||||
regs[1] = outAddr;
|
||||
break;
|
||||
}
|
||||
|
||||
case Operation::Map: mem.mirrorMapping(addr0, addr1, size); break;
|
||||
case Operation::Map:
|
||||
// Official kernel only allows Private regions to be mapped to Free regions. An Alias or Aliased region cannot be mapped again
|
||||
if (!mem.mapVirtualMemory(
|
||||
addr0, addr1, pages, r, w, false, MemoryState::Free, MemoryState::Private, MemoryState::Alias, MemoryState::Aliased
|
||||
))
|
||||
Helpers::panic("ControlMemory: Failed to map memory");
|
||||
break;
|
||||
|
||||
case Operation::Unmap:
|
||||
// The same as a Map operation, except in reverse
|
||||
if (!mem.mapVirtualMemory(
|
||||
addr0, addr1, pages, false, false, false, MemoryState::Alias, MemoryState::Aliased, MemoryState::Free, MemoryState::Private
|
||||
)) {
|
||||
Helpers::panic("ControlMemory: Failed to unmap memory");
|
||||
}
|
||||
break;
|
||||
|
||||
case Operation::Protect:
|
||||
Helpers::warn(
|
||||
"Ignoring mprotect! Hope nothing goes wrong but if the game accesses invalid memory or crashes then we prolly need to implement "
|
||||
"this\n"
|
||||
);
|
||||
// Official kernel has an internal state bit to indicate that the region's permissions may be changed
|
||||
// But this should account for all cases
|
||||
if (!mem.testMemoryState(addr0, pages, MemoryState::Private) && !mem.testMemoryState(addr0, pages, MemoryState::Alias) &&
|
||||
!mem.testMemoryState(addr0, pages, MemoryState::Aliased) && !mem.testMemoryState(addr0, pages, MemoryState::AliasCode)) {
|
||||
Helpers::warn("Tried to mprotect invalid region!");
|
||||
return;
|
||||
}
|
||||
|
||||
mem.changePermissions(addr0, pages, r, w, false);
|
||||
regs[1] = addr0;
|
||||
break;
|
||||
|
||||
default: Helpers::warn("ControlMemory: unknown operation %X\n", operation); break;
|
||||
@@ -104,10 +137,11 @@ void Kernel::queryMemory() {
|
||||
|
||||
logSVC("QueryMemory(mem info pointer = %08X, page info pointer = %08X, addr = %08X)\n", memInfo, pageInfo, addr);
|
||||
|
||||
const auto info = mem.queryMemory(addr);
|
||||
regs[0] = Result::Success;
|
||||
KernelMemoryTypes::MemoryInfo info;
|
||||
const auto result = mem.queryMemory(info, addr);
|
||||
regs[0] = result;
|
||||
regs[1] = info.baseAddr;
|
||||
regs[2] = info.size;
|
||||
regs[2] = info.pages * Memory::pageSize;
|
||||
regs[3] = info.perms;
|
||||
regs[4] = info.state;
|
||||
regs[5] = 0; // page flags
|
||||
|
||||
@@ -82,7 +82,9 @@ 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 mem.usedUserMemory;
|
||||
// TODO: needs to use the current amount of memory allocated by the process
|
||||
case ResourceType::Commit: return fcramManager.getUsedCount(FcramRegion::App) * Memory::pageSize;
|
||||
|
||||
case ResourceType::Thread: return threadIndices.size();
|
||||
default: Helpers::panic("Attempted to get current value of unknown kernel resource: %d\n", resourceName);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "memory.hpp"
|
||||
|
||||
using namespace KernelMemoryTypes;
|
||||
|
||||
namespace {
|
||||
struct LoadInfo {
|
||||
u32 codeSegSizeAligned;
|
||||
@@ -53,12 +55,6 @@ bool Memory::map3DSX(HB3DSX& hb3dsx, const HB3DSX::Header& header) {
|
||||
// suum of aligned values is always aligned, have an extra RW page for libctru
|
||||
const u32 totalSize = hbInfo.codeSegSizeAligned + hbInfo.rodataSegSizeAligned + hbInfo.dataSegSizeAligned + 4_KB;
|
||||
|
||||
const auto opt = findPaddr(totalSize);
|
||||
if (!opt.has_value()) {
|
||||
Helpers::panic("Failed to find paddr to map 3DSX file's code to");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Map the ROM on the kernel side
|
||||
const u32 textOffset = 0;
|
||||
const u32 rodataOffset = textOffset + hbInfo.codeSegSizeAligned;
|
||||
@@ -213,7 +209,8 @@ bool Memory::map3DSX(HB3DSX& hb3dsx, const HB3DSX::Header& header) {
|
||||
{
|
||||
pst->heapSize = u32(48_MB);
|
||||
pst->linearHeapSize = u32(64_MB);
|
||||
} else */ {
|
||||
} else */
|
||||
{
|
||||
pst.heapSize = u32(24_MB);
|
||||
pst.linearHeapSize = u32(32_MB);
|
||||
}
|
||||
@@ -221,12 +218,17 @@ bool Memory::map3DSX(HB3DSX& hb3dsx, const HB3DSX::Header& header) {
|
||||
std::memcpy(&code[4], &pst, sizeof(pst));
|
||||
}
|
||||
|
||||
const auto paddr = opt.value();
|
||||
std::memcpy(&fcram[paddr], &code[0], totalSize); // Copy the 3 segments + BSS to FCRAM
|
||||
// Text is R-X
|
||||
allocMemory(textSegAddr, hbInfo.codeSegSizeAligned / Memory::pageSize, FcramRegion::App, true, false, true, MemoryState::Code);
|
||||
copyToVaddr(textSegAddr, &code[textOffset], hbInfo.codeSegSizeAligned);
|
||||
|
||||
allocateMemory(textSegAddr, paddr + textOffset, hbInfo.codeSegSizeAligned, true, true, false, true); // Text is R-X
|
||||
allocateMemory(rodataSegAddr, paddr + rodataOffset, hbInfo.rodataSegSizeAligned, true, true, false, false); // Rodata is R--
|
||||
allocateMemory(dataSegAddr, paddr + dataOffset, hbInfo.dataSegSizeAligned + 0x1000, true, true, true, false); // Data+BSS+Extra is RW-
|
||||
// Rodata is R--
|
||||
allocMemory(rodataSegAddr, hbInfo.rodataSegSizeAligned / Memory::pageSize, FcramRegion::App, true, false, false, MemoryState::Code);
|
||||
copyToVaddr(rodataSegAddr, &code[rodataOffset], hbInfo.rodataSegSizeAligned);
|
||||
|
||||
// Data + BSS + Extra is RW-. We allocate 1 extra page (4KB) which is not initialized to anything.
|
||||
allocMemory(dataSegAddr, (hbInfo.dataSegSizeAligned + 4_KB) / Memory::pageSize, FcramRegion::App, true, true, false, MemoryState::Private);
|
||||
copyToVaddr(dataSegAddr, &code[dataOffset], hbInfo.dataSegSizeAligned);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "memory.hpp"
|
||||
|
||||
using namespace ELFIO;
|
||||
using namespace KernelMemoryTypes;
|
||||
|
||||
std::optional<u32> Memory::loadELF(std::ifstream& file) {
|
||||
loadedCXI = std::nullopt; // ELF files don't have a CXI, so set this to null
|
||||
@@ -24,6 +25,7 @@ std::optional<u32> Memory::loadELF(std::ifstream& file) {
|
||||
auto segNum = reader.segments.size();
|
||||
printf("Number of segments: %d\n", segNum);
|
||||
printf(" # Perms Vaddr File Size Mem Size\n");
|
||||
|
||||
for (int i = 0; i < segNum; ++i) {
|
||||
const auto seg = reader.segments[i];
|
||||
const auto flags = seg->get_flags();
|
||||
@@ -55,12 +57,8 @@ std::optional<u32> Memory::loadELF(std::ifstream& file) {
|
||||
Helpers::warn("Rounding ELF segment size to %08X\n", memorySize);
|
||||
}
|
||||
|
||||
// This should also assert that findPaddr doesn't fail
|
||||
u32 fcramAddr = findPaddr(memorySize).value();
|
||||
std::memcpy(&fcram[fcramAddr], data, fileSize);
|
||||
|
||||
// Allocate the segment on the OS side
|
||||
allocateMemory(vaddr, fcramAddr, memorySize, true, r, w, x);
|
||||
allocMemory(vaddr, memorySize / Memory::pageSize, FcramRegion::App, r, w, x, MemoryState::Code);
|
||||
copyToVaddr(vaddr, data, fileSize);
|
||||
}
|
||||
|
||||
// ELF can't specify a region, make it default to USA
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
|
||||
#include "kernel/fcram.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
using namespace KernelMemoryTypes;
|
||||
|
||||
bool Memory::mapCXI(NCSD& ncsd, NCCH& cxi) {
|
||||
printf("Text address = %08X, size = %08X\n", cxi.text.address, cxi.text.size);
|
||||
printf("Rodata address = %08X, size = %08X\n", cxi.rodata.address, cxi.rodata.size);
|
||||
@@ -24,12 +27,6 @@ bool Memory::mapCXI(NCSD& ncsd, NCCH& cxi) {
|
||||
// Round up the size of the CXI stack size to a page (4KB) boundary, as the OS can only allocate memory this way
|
||||
u32 stackSize = (cxi.stackSize + pageSize - 1) & -pageSize;
|
||||
|
||||
if (stackSize > 512_KB) {
|
||||
// TODO: Figure out the actual max stack size
|
||||
Helpers::warn("CXI stack size is %08X which seems way too big. Clamping to 512KB", stackSize);
|
||||
stackSize = 512_KB;
|
||||
}
|
||||
|
||||
// Allocate stack
|
||||
if (!allocateMainThreadStack(stackSize)) {
|
||||
// Should be unreachable
|
||||
@@ -42,40 +39,41 @@ bool Memory::mapCXI(NCSD& ncsd, NCCH& cxi) {
|
||||
u32 bssSize = (cxi.bssSize + 0xfff) & ~0xfff; // Round BSS size up to a page boundary
|
||||
// Total memory to allocate for loading
|
||||
u32 totalSize = (cxi.text.pageCount + cxi.rodata.pageCount + cxi.data.pageCount) * pageSize + bssSize;
|
||||
code.resize(code.size() + bssSize, 0); // Pad the .code file with zeroes for the BSS segment
|
||||
|
||||
if (code.size() < totalSize) {
|
||||
if (code.size() + bssSize < totalSize) {
|
||||
Helpers::panic("Total code size as reported by the exheader is larger than the .code file");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto opt = findPaddr(totalSize);
|
||||
if (!opt.has_value()) {
|
||||
Helpers::panic("Failed to find paddr to map CXI file's code to");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto paddr = opt.value();
|
||||
std::memcpy(&fcram[paddr], &code[0], totalSize); // Copy the 3 segments + BSS to FCRAM
|
||||
|
||||
// Map the ROM on the kernel side
|
||||
u32 textOffset = 0;
|
||||
u32 textAddr = cxi.text.address;
|
||||
u32 textSize = cxi.text.pageCount * pageSize;
|
||||
|
||||
u32 rodataOffset = textOffset + textSize;
|
||||
u32 rodataAddr = cxi.rodata.address;
|
||||
u32 rodataSize = cxi.rodata.pageCount * pageSize;
|
||||
|
||||
u32 dataOffset = rodataOffset + rodataSize;
|
||||
u32 dataAddr = cxi.data.address;
|
||||
u32 dataSize = cxi.data.pageCount * pageSize + bssSize; // We're merging the data and BSS segments, as BSS is just pre-initted .data
|
||||
|
||||
allocateMemory(textAddr, paddr + textOffset, textSize, true, true, false, true); // Text is R-X
|
||||
allocateMemory(rodataAddr, paddr + rodataOffset, rodataSize, true, true, false, false); // Rodata is R--
|
||||
allocateMemory(dataAddr, paddr + dataOffset, dataSize, true, true, true, false); // Data+BSS is RW-
|
||||
// TODO: base this off the exheader
|
||||
auto region = FcramRegion::App;
|
||||
u32 bssAddr = dataAddr + (cxi.data.pageCount << 12);
|
||||
|
||||
ncsd.entrypoint = textAddr;
|
||||
allocMemory(textAddr, cxi.text.pageCount, region, true, false, true, MemoryState::Code);
|
||||
allocMemory(rodataAddr, cxi.rodata.pageCount, region, true, false, false, MemoryState::Code);
|
||||
allocMemory(dataAddr, cxi.data.pageCount, region, true, true, false, MemoryState::Private);
|
||||
allocMemory(bssAddr, bssSize >> 12, region, true, true, false, MemoryState::Private);
|
||||
|
||||
// Copy .code file to FCRAM
|
||||
copyToVaddr(textAddr, code.data(), textSize);
|
||||
copyToVaddr(rodataAddr, code.data() + textSize, rodataSize);
|
||||
copyToVaddr(dataAddr, code.data() + textSize + rodataSize, cxi.data.pageCount << 12);
|
||||
|
||||
// Set BSS to zeroes
|
||||
std::vector<u8> bss(bssSize, 0);
|
||||
copyToVaddr(bssAddr, bss.data(), bssSize);
|
||||
|
||||
ncsd.entrypoint = cxi.text.address;
|
||||
|
||||
// Back the IOFile for accessing the ROM, as well as the ROM's CXI partition, in the memory class.
|
||||
CXIFile = ncsd.file;
|
||||
@@ -85,7 +83,9 @@ bool Memory::mapCXI(NCSD& ncsd, NCCH& cxi) {
|
||||
|
||||
std::optional<NCSD> Memory::loadNCSD(Crypto::AESEngine& aesEngine, const std::filesystem::path& path) {
|
||||
NCSD ncsd;
|
||||
if (!ncsd.file.open(path, "rb")) return std::nullopt;
|
||||
if (!ncsd.file.open(path, "rb")) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
u8 magic[4]; // Must be "NCSD"
|
||||
ncsd.file.seek(0x100);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <ctime>
|
||||
|
||||
#include "config_mem.hpp"
|
||||
#include "kernel/fcram.hpp"
|
||||
#include "resource_limits.hpp"
|
||||
#include "services/fonts.hpp"
|
||||
#include "services/ptm.hpp"
|
||||
@@ -14,38 +15,43 @@ CMRC_DECLARE(ConsoleFonts);
|
||||
|
||||
using namespace KernelMemoryTypes;
|
||||
|
||||
Memory::Memory(u64& cpuTicks, const EmulatorConfig& config) : cpuTicks(cpuTicks), config(config) {
|
||||
fcram = new uint8_t[FCRAM_SIZE]();
|
||||
Memory::Memory(KFcram& fcramManager, const EmulatorConfig& config) : fcramManager(fcramManager), config(config) {
|
||||
const bool fastmemEnabled = config.fastmemEnabled;
|
||||
arena = new Common::HostMemory(FASTMEM_BACKING_SIZE, FASTMEM_VIRTUAL_SIZE, fastmemEnabled);
|
||||
|
||||
readTable.resize(totalPageCount, 0);
|
||||
writeTable.resize(totalPageCount, 0);
|
||||
memoryInfo.reserve(32); // Pre-allocate some room for memory allocation info to avoid dynamic allocs
|
||||
paddrTable.resize(totalPageCount, 0);
|
||||
|
||||
fcram = arena->BackingBasePointer() + FASTMEM_FCRAM_OFFSET;
|
||||
// arenaDSPRam = arena->BackingBasePointer() + FASTMEM_DSP_RAM_OFFSET;
|
||||
useFastmem = fastmemEnabled && arena->VirtualBasePointer() != nullptr;
|
||||
}
|
||||
|
||||
void Memory::reset() {
|
||||
// Unallocate all memory
|
||||
// Mark the entire process address space as free
|
||||
constexpr static int MAX_USER_PAGES = 0x40000000 >> 12;
|
||||
memoryInfo.clear();
|
||||
usedFCRAMPages.reset();
|
||||
usedUserMemory = u32(0_MB);
|
||||
usedSystemMemory = u32(0_MB);
|
||||
memoryInfo.push_back(MemoryInfo(0, MAX_USER_PAGES, 0, KernelMemoryTypes::Free));
|
||||
|
||||
// TODO: remove this, only needed to make the subsequent allocations work for now
|
||||
fcramManager.reset(FCRAM_SIZE, FCRAM_APPLICATION_SIZE, FCRAM_SYSTEM_SIZE, FCRAM_BASE_SIZE);
|
||||
|
||||
if (useFastmem) {
|
||||
// Unmap any mappings when resetting
|
||||
arena->Unmap(0, 4_GB, false);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < totalPageCount; i++) {
|
||||
readTable[i] = 0;
|
||||
writeTable[i] = 0;
|
||||
paddrTable[i] = 0;
|
||||
}
|
||||
|
||||
// Map (32 * 4) KB of FCRAM before the stack for the TLS of each thread
|
||||
std::optional<u32> tlsBaseOpt = findPaddr(32 * 4_KB);
|
||||
if (!tlsBaseOpt.has_value()) { // Should be unreachable but still good to have
|
||||
Helpers::panic("Failed to allocate memory for thread-local storage");
|
||||
}
|
||||
|
||||
u32 basePaddrForTLS = tlsBaseOpt.value();
|
||||
for (u32 i = 0; i < appResourceLimits.maxThreads; i++) {
|
||||
u32 vaddr = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize;
|
||||
allocateMemory(vaddr, basePaddrForTLS, VirtualAddrs::TLSSize, true);
|
||||
basePaddrForTLS += VirtualAddrs::TLSSize;
|
||||
}
|
||||
// Allocate 512 bytes of TLS for each thread. Since the smallest allocatable unit is 4 KB, that means allocating one page for every 8 threads
|
||||
// Note that TLS is always allocated in the Base region
|
||||
s32 tlsPages = (appResourceLimits.maxThreads + 7) >> 3;
|
||||
allocMemory(VirtualAddrs::TLSBase, tlsPages, FcramRegion::Base, true, true, false, MemoryState::Locked);
|
||||
|
||||
// Initialize shared memory blocks and reserve memory for them
|
||||
for (auto& e : sharedMemBlocks) {
|
||||
@@ -56,19 +62,23 @@ void Memory::reset() {
|
||||
}
|
||||
|
||||
e.mapped = false;
|
||||
e.paddr = allocateSysMemory(e.size);
|
||||
FcramBlockList memBlock;
|
||||
fcramManager.alloc(memBlock, e.size >> 12, FcramRegion::Sys, false);
|
||||
e.paddr = memBlock.begin()->paddr;
|
||||
}
|
||||
|
||||
// Map DSP RAM as R/W at [0x1FF00000, 0x1FF7FFFF]
|
||||
constexpr u32 dspRamPages = DSP_RAM_SIZE / pageSize; // Number of DSP RAM pages
|
||||
constexpr u32 initialPage = VirtualAddrs::DSPMemStart / pageSize; // First page of DSP RAM in the virtual address space
|
||||
constexpr u32 dspRamPages = DSP_RAM_SIZE / pageSize; // Number of DSP RAM pages
|
||||
|
||||
for (u32 i = 0; i < dspRamPages; i++) {
|
||||
auto pointer = uintptr_t(&dspRam[i * pageSize]);
|
||||
u32 vaddr = VirtualAddrs::DSPMemStart;
|
||||
u32 paddr = PhysicalAddrs::DSP_RAM;
|
||||
|
||||
readTable[i + initialPage] = pointer;
|
||||
writeTable[i + initialPage] = pointer;
|
||||
}
|
||||
Operation op{.newState = MemoryState::Static, .r = true, .w = true, .changeState = true, .changePerms = true};
|
||||
changeMemoryState(vaddr, dspRamPages, op);
|
||||
mapPhysicalMemory(vaddr, paddr, dspRamPages, true, true, false);
|
||||
|
||||
// Allocate RW mapping for DSP RAM
|
||||
// addFastmemView(VirtualAddrs::DSPMemStart, FASTMEM_DSP_RAM_OFFSET, DSP_RAM_SIZE, true, false);
|
||||
|
||||
// Later adjusted based on ROM header when possible
|
||||
region = Regions::USA;
|
||||
@@ -76,14 +86,9 @@ void Memory::reset() {
|
||||
|
||||
bool Memory::allocateMainThreadStack(u32 size) {
|
||||
// Map stack pages as R/W
|
||||
std::optional<u32> basePaddr = findPaddr(size);
|
||||
if (!basePaddr.has_value()) { // Should also be unreachable but still good to have
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: get the region from the exheader
|
||||
const u32 stackBottom = VirtualAddrs::StackTop - size;
|
||||
std::optional<u32> result = allocateMemory(stackBottom, basePaddr.value(), size, true); // Should never be nullopt
|
||||
return result.has_value();
|
||||
return allocMemory(stackBottom, size >> 12, FcramRegion::App, true, true, false, MemoryState::Locked);
|
||||
}
|
||||
|
||||
u8 Memory::read8(u32 vaddr) {
|
||||
@@ -120,7 +125,7 @@ u8 Memory::read8(u32 vaddr) {
|
||||
case ConfigMem::FirmRevision: return firm.revision;
|
||||
case ConfigMem::FirmVersionMinor: return firm.minor;
|
||||
case ConfigMem::FirmVersionMajor: return firm.major;
|
||||
case ConfigMem::WifiLevel: return 0; // No wifi :(
|
||||
case ConfigMem::WifiLevel: return 0; // No wifi :(
|
||||
|
||||
case ConfigMem::WifiMac:
|
||||
case ConfigMem::WifiMac + 1:
|
||||
@@ -163,8 +168,8 @@ u32 Memory::read32(u32 vaddr) {
|
||||
case ConfigMem::Datetime0 + 4:
|
||||
return u32(timeSince3DSEpoch() >> 32); // top 32 bits
|
||||
// Ticks since time was last updated. For now we return the current tick count
|
||||
case ConfigMem::Datetime0 + 8: return u32(cpuTicks);
|
||||
case ConfigMem::Datetime0 + 12: return u32(cpuTicks >> 32);
|
||||
case ConfigMem::Datetime0 + 8: return u32(*cpuTicks);
|
||||
case ConfigMem::Datetime0 + 12: return u32(*cpuTicks >> 32);
|
||||
case ConfigMem::Datetime0 + 16: return 0xFFB0FF0; // Unknown, set by PTM
|
||||
case ConfigMem::Datetime0 + 20:
|
||||
case ConfigMem::Datetime0 + 24:
|
||||
@@ -172,11 +177,10 @@ u32 Memory::read32(u32 vaddr) {
|
||||
|
||||
case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit;
|
||||
case ConfigMem::SyscoreVer: return 2;
|
||||
case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does
|
||||
case 0x1FF81000:
|
||||
return 0; // TODO: Figure out what this config mem address does
|
||||
// Wifi MAC: First 4 bytes of MAC Address
|
||||
case ConfigMem::WifiMac:
|
||||
return (u32(MACAddress[3]) << 24) | (u32(MACAddress[2]) << 16) | (u32(MACAddress[1]) << 8) |
|
||||
MACAddress[0];
|
||||
case ConfigMem::WifiMac: return (u32(MACAddress[3]) << 24) | (u32(MACAddress[2]) << 16) | (u32(MACAddress[1]) << 8) | MACAddress[0];
|
||||
|
||||
// 3D slider. Float in range 0.0 = off, 1.0 = max.
|
||||
case ConfigMem::SliderState3D: return Helpers::bit_cast<u32, float>(0.0f);
|
||||
@@ -186,7 +190,7 @@ u32 Memory::read32(u32 vaddr) {
|
||||
default:
|
||||
if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) {
|
||||
static int shutUpCounter = 0;
|
||||
if (shutUpCounter < 5) { // Stop spamming about VRAM reads after the first 5
|
||||
if (shutUpCounter < 5) { // Stop spamming about VRAM reads after the first 5
|
||||
shutUpCounter++;
|
||||
Helpers::warn("VRAM read!\n");
|
||||
}
|
||||
@@ -296,149 +300,254 @@ std::string Memory::readString(u32 address, u32 maxSize) {
|
||||
// thanks to the New 3DS having more FCRAM
|
||||
u32 Memory::getLinearHeapVaddr() { return (kernelVersion < 0x22C) ? VirtualAddrs::LinearHeapStartOld : VirtualAddrs::LinearHeapStartNew; }
|
||||
|
||||
std::optional<u32> Memory::allocateMemory(u32 vaddr, u32 paddr, u32 size, bool linear, bool r, bool w, bool x, bool adjustAddrs, bool isMap) {
|
||||
// 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
|
||||
// If we're mapping there's no fear of OoM, because we're not really allocating memory, just binding vaddrs to specific paddrs
|
||||
assert(isAligned(vaddr) && isAligned(paddr) && isAligned(size));
|
||||
assert(size <= FCRAM_APPLICATION_SIZE || isMap);
|
||||
assert(usedUserMemory + size <= FCRAM_APPLICATION_SIZE || isMap);
|
||||
assert(paddr + size <= FCRAM_APPLICATION_SIZE || isMap);
|
||||
void Memory::changeMemoryState(u32 vaddr, s32 pages, const Operation& op) {
|
||||
assert(!(vaddr & 0xFFF));
|
||||
|
||||
// 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;
|
||||
if (!op.changePerms && !op.changeState) Helpers::panic("Invalid op passed to changeMemoryState!");
|
||||
|
||||
assert(availablePageCount >= neededPageCount || isMap);
|
||||
bool blockFound = false;
|
||||
|
||||
// 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 && adjustAddrs) {
|
||||
std::optional<u32> newPaddr = findPaddr(size);
|
||||
if (!newPaddr.has_value()) {
|
||||
Helpers::panic("Failed to find paddr");
|
||||
for (auto it = memoryInfo.begin(); it != memoryInfo.end(); it++) {
|
||||
// Find the block that the memory region is located in
|
||||
u32 blockStart = it->baseAddr;
|
||||
u32 blockEnd = it->end();
|
||||
|
||||
u32 reqStart = vaddr;
|
||||
u32 reqEnd = vaddr + (pages << 12);
|
||||
|
||||
if (!(reqStart >= blockStart && reqEnd <= blockEnd)) continue;
|
||||
|
||||
// Now that the block has been found, fill it with the necessary info
|
||||
auto oldState = it->state;
|
||||
u32 oldPerms = it->perms;
|
||||
it->baseAddr = reqStart;
|
||||
it->pages = pages;
|
||||
if (op.changePerms) it->perms = (op.r ? PERMISSION_R : 0) | (op.w ? PERMISSION_W : 0) | (op.x ? PERMISSION_X : 0);
|
||||
if (op.changeState) it->state = op.newState;
|
||||
|
||||
// If the requested memory region is smaller than the block found, the block must be split
|
||||
if (blockStart < reqStart) {
|
||||
MemoryInfo startBlock(blockStart, (reqStart - blockStart) >> 12, oldPerms, oldState);
|
||||
memoryInfo.insert(it, startBlock);
|
||||
}
|
||||
|
||||
paddr = newPaddr.value();
|
||||
assert(paddr + size <= FCRAM_APPLICATION_SIZE || isMap);
|
||||
}
|
||||
|
||||
// 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 && adjustAddrs) {
|
||||
// Linear memory needs to be allocated in a way where you can easily get the paddr by subtracting the linear heap base
|
||||
// In order to be able to easily send data to hardware like the GPU
|
||||
if (linear) {
|
||||
vaddr = getLinearHeapVaddr() + paddr;
|
||||
} else {
|
||||
vaddr = usedUserMemory + VirtualAddrs::NormalHeapStart;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMap) {
|
||||
usedUserMemory += 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]);
|
||||
if (reqEnd < blockEnd) {
|
||||
auto itAfter = std::next(it);
|
||||
MemoryInfo endBlock(reqEnd, (blockEnd - reqEnd) >> 12, oldPerms, oldState);
|
||||
memoryInfo.insert(itAfter, endBlock);
|
||||
}
|
||||
|
||||
// Mark FCRAM page as allocated and go on
|
||||
usedFCRAMPages[physPage] = true;
|
||||
virtualPage++;
|
||||
physPage++;
|
||||
blockFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Back up the info for this allocation in our memoryInfo vector
|
||||
u32 perms = (r ? PERMISSION_R : 0) | (w ? PERMISSION_W : 0) | (x ? PERMISSION_X : 0);
|
||||
memoryInfo.push_back(std::move(MemoryInfo(vaddr, size, perms, KernelMemoryTypes::Reserved)));
|
||||
if (!blockFound) Helpers::panic("Unable to find block in changeMemoryState!");
|
||||
|
||||
return vaddr;
|
||||
// Merge all blocks with the same state and permissions
|
||||
for (auto it = memoryInfo.begin(); it != memoryInfo.end();) {
|
||||
auto next = std::next(it);
|
||||
if (next == memoryInfo.end()) break;
|
||||
|
||||
if (it->state != next->state || it->perms != next->perms) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
|
||||
next->baseAddr = it->baseAddr;
|
||||
next->pages += it->pages;
|
||||
it = memoryInfo.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// Find a paddr which we can use for allocating "size" bytes
|
||||
std::optional<u32> Memory::findPaddr(u32 size) {
|
||||
assert(isAligned(size));
|
||||
const u32 neededPages = size / pageSize;
|
||||
void Memory::queryPhysicalBlocks(FcramBlockList& outList, u32 vaddr, s32 pages) {
|
||||
s32 srcPages = pages;
|
||||
for (auto& alloc : memoryInfo) {
|
||||
u32 blockStart = alloc.baseAddr;
|
||||
u32 blockEnd = alloc.end();
|
||||
|
||||
// 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;
|
||||
if (!(vaddr >= blockStart && vaddr < blockEnd)) continue;
|
||||
|
||||
for (u32 i = 0; i < FCRAM_APPLICATION_PAGE_COUNT; i++) {
|
||||
if (usedFCRAMPages[i]) { // Page is occupied already, go to new candidate
|
||||
candidatePage = i + 1;
|
||||
counter = 0;
|
||||
} else { // The paddr we're testing has 1 more free page
|
||||
counter++;
|
||||
// Check if there's enough free memory to use this page
|
||||
// We use == instead of >= because some software does 0-byte allocations
|
||||
if (counter >= neededPages) {
|
||||
return candidatePage * pageSize;
|
||||
s32 blockPaddr = paddrTable[vaddr >> 12];
|
||||
s32 blockPages = alloc.pages - ((vaddr - blockStart) >> 12);
|
||||
blockPages = std::min(srcPages, blockPages);
|
||||
FcramBlock physicalBlock(blockPaddr, blockPages);
|
||||
outList.push_back(physicalBlock);
|
||||
|
||||
vaddr += blockPages << 12;
|
||||
srcPages -= blockPages;
|
||||
if (srcPages == 0) break;
|
||||
}
|
||||
|
||||
if (srcPages != 0) Helpers::panic("Unable to find virtual pages to map!");
|
||||
}
|
||||
|
||||
void Memory::mapPhysicalMemory(u32 vaddr, u32 paddr, s32 pages, bool r, bool w, bool x) {
|
||||
assert(!(vaddr & 0xFFF));
|
||||
assert(!(paddr & 0xFFF));
|
||||
|
||||
// TODO: make this a separate function
|
||||
u8* hostPtr = nullptr;
|
||||
if (paddr < FCRAM_SIZE) {
|
||||
hostPtr = fcram + paddr; // FIXME: FCRAM doesn't actually start from physical address 0, but from 0x20000000
|
||||
|
||||
if (useFastmem) {
|
||||
addFastmemView(vaddr, FASTMEM_FCRAM_OFFSET + paddr, usize(pages) * pageSize, w);
|
||||
}
|
||||
} else if (paddr >= VirtualAddrs::DSPMemStart && paddr < VirtualAddrs::DSPMemStart + DSP_RAM_SIZE) {
|
||||
hostPtr = dspRam + (paddr - VirtualAddrs::DSPMemStart);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pages; i++) {
|
||||
u32 index = (vaddr >> 12) + i;
|
||||
paddrTable[index] = paddr + (i << 12);
|
||||
if (r)
|
||||
readTable[index] = (uintptr_t)(hostPtr + (i << 12));
|
||||
else
|
||||
readTable[index] = 0;
|
||||
|
||||
if (w)
|
||||
writeTable[index] = (uintptr_t)(hostPtr + (i << 12));
|
||||
else
|
||||
writeTable[index] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::unmapPhysicalMemory(u32 vaddr, u32 paddr, s32 pages) {
|
||||
for (int i = 0; i < pages; i++) {
|
||||
u32 index = (vaddr >> 12) + i;
|
||||
paddrTable[index] = 0;
|
||||
readTable[index] = 0;
|
||||
writeTable[index] = 0;
|
||||
}
|
||||
|
||||
if (useFastmem) {
|
||||
arena->Unmap(vaddr, pages * pageSize, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool Memory::allocMemory(u32 vaddr, s32 pages, FcramRegion region, bool r, bool w, bool x, MemoryState state) {
|
||||
auto res = testMemoryState(vaddr, pages, MemoryState::Free);
|
||||
if (res.isFailure()) return false;
|
||||
|
||||
FcramBlockList memList;
|
||||
fcramManager.alloc(memList, pages, region, false);
|
||||
|
||||
for (auto it = memList.begin(); it != memList.end(); it++) {
|
||||
Operation op{.newState = state, .r = r, .w = w, .x = x, .changeState = true, .changePerms = true};
|
||||
changeMemoryState(vaddr, it->pages, op);
|
||||
mapPhysicalMemory(vaddr, it->paddr, it->pages, r, w, x);
|
||||
vaddr += it->pages << 12;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Memory::allocMemoryLinear(u32& outVaddr, u32 inVaddr, s32 pages, FcramRegion region, bool r, bool w, bool x) {
|
||||
if (inVaddr) Helpers::panic("inVaddr specified for linear allocation!");
|
||||
|
||||
FcramBlockList memList;
|
||||
fcramManager.alloc(memList, pages, region, true);
|
||||
|
||||
u32 paddr = memList.begin()->paddr;
|
||||
u32 vaddr = getLinearHeapVaddr() + paddr;
|
||||
auto res = testMemoryState(vaddr, pages, MemoryState::Free);
|
||||
if (res.isFailure()) Helpers::panic("Unable to map linear allocation (vaddr:%08X pages:%08X)", vaddr, pages);
|
||||
|
||||
Operation op{.newState = MemoryState::Continuous, .r = r, .w = w, .x = x, .changeState = true, .changePerms = true};
|
||||
changeMemoryState(vaddr, pages, op);
|
||||
mapPhysicalMemory(vaddr, paddr, pages, r, w, x);
|
||||
|
||||
outVaddr = vaddr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Memory::mapVirtualMemory(
|
||||
u32 dstVaddr, u32 srcVaddr, s32 pages, bool r, bool w, bool x, MemoryState oldDstState, MemoryState oldSrcState, MemoryState newDstState,
|
||||
MemoryState newSrcState, bool unmapPages
|
||||
) {
|
||||
// Check that the regions have the specified state
|
||||
// TODO: check src perms
|
||||
auto res = testMemoryState(srcVaddr, pages, oldSrcState);
|
||||
if (res.isFailure()) return false;
|
||||
|
||||
res = testMemoryState(dstVaddr, pages, oldDstState);
|
||||
if (res.isFailure()) return false;
|
||||
|
||||
// Change the virtual memory state for both regions
|
||||
Operation srcOp{.newState = newSrcState, .changeState = true};
|
||||
changeMemoryState(srcVaddr, pages, srcOp);
|
||||
|
||||
Operation dstOp{.newState = newDstState, .r = r, .w = w, .x = x, .changeState = true, .changePerms = true};
|
||||
changeMemoryState(dstVaddr, pages, dstOp);
|
||||
|
||||
// Get a list of physical blocks in the source region
|
||||
FcramBlockList physicalList;
|
||||
queryPhysicalBlocks(physicalList, srcVaddr, pages);
|
||||
|
||||
// Map or unmap each physical block
|
||||
for (auto& block : physicalList) {
|
||||
if (newDstState == MemoryState::Free) {
|
||||
// TODO: Games with CROs will unmap the CRO yet still continue accessing the address it was mapped to?
|
||||
if (unmapPages) {
|
||||
unmapPhysicalMemory(dstVaddr, block.paddr, block.pages);
|
||||
}
|
||||
} else {
|
||||
mapPhysicalMemory(dstVaddr, block.paddr, block.pages, r, w, x);
|
||||
}
|
||||
|
||||
dstVaddr += block.pages << 12;
|
||||
}
|
||||
|
||||
// Couldn't find any page :(
|
||||
return std::nullopt;
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 Memory::allocateSysMemory(u32 size) {
|
||||
// Should never be triggered, only here as a sanity check
|
||||
if (!isAligned(size)) {
|
||||
Helpers::panic("Memory::allocateSysMemory: Size is not page aligned (val = %08X)", size);
|
||||
void Memory::changePermissions(u32 vaddr, s32 pages, bool r, bool w, bool x) {
|
||||
Operation op{.r = r, .w = w, .x = x, .changePerms = true};
|
||||
changeMemoryState(vaddr, pages, op);
|
||||
|
||||
// Now that permissions have been changed, update the corresponding host tables
|
||||
FcramBlockList physicalList;
|
||||
queryPhysicalBlocks(physicalList, vaddr, pages);
|
||||
|
||||
for (auto& block : physicalList) {
|
||||
mapPhysicalMemory(vaddr, block.paddr, block.pages, r, w, x);
|
||||
vaddr += block.pages;
|
||||
}
|
||||
|
||||
// We use a pretty dumb allocator for OS memory since this is not really accessible to the app and is only used internally
|
||||
// It works by just allocating memory linearly, starting from index 0 of OS memory and going up
|
||||
// This should also be unreachable in practice and exists as a sanity check
|
||||
if (size > remainingSysFCRAM()) {
|
||||
Helpers::panic("Memory::allocateSysMemory: Overflowed OS FCRAM");
|
||||
}
|
||||
|
||||
const u32 pageCount = size / pageSize; // Number of pages that will be used up
|
||||
const u32 startIndex = sysFCRAMIndex() + usedSystemMemory; // Starting FCRAM index
|
||||
const u32 startingPage = startIndex / pageSize;
|
||||
|
||||
for (u32 i = 0; i < pageCount; i++) {
|
||||
if (usedFCRAMPages[startingPage + i]) // Also a theoretically unreachable panic for safety
|
||||
Helpers::panic("Memory::reserveMemory: Trying to reserve already reserved memory");
|
||||
usedFCRAMPages[startingPage + i] = true;
|
||||
}
|
||||
|
||||
usedSystemMemory += size;
|
||||
return startIndex;
|
||||
}
|
||||
|
||||
// The way I understand how the kernel's QueryMemory is supposed to work is that you give it a vaddr
|
||||
// And the kernel looks up the memory allocations it's performed, finds which one it belongs in and returns its info?
|
||||
// TODO: Verify this
|
||||
MemoryInfo Memory::queryMemory(u32 vaddr) {
|
||||
Result::HorizonResult Memory::queryMemory(MemoryInfo& out, u32 vaddr) {
|
||||
// Check each allocation
|
||||
for (auto& alloc : memoryInfo) {
|
||||
// Check if the memory address belongs in this allocation and return the info if so
|
||||
if (vaddr >= alloc.baseAddr && vaddr < alloc.end()) {
|
||||
return alloc;
|
||||
out = alloc;
|
||||
return Result::Success;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, if this vaddr was never allocated
|
||||
// TODO: I think this is meant to return how much memory starting here is free as the size?
|
||||
return MemoryInfo(vaddr, pageSize, 0, KernelMemoryTypes::Free);
|
||||
// Official kernel just returns an error here
|
||||
Helpers::warn("Failed to find block in QueryMemory!");
|
||||
return Result::FailurePlaceholder;
|
||||
}
|
||||
|
||||
Result::HorizonResult Memory::testMemoryState(u32 vaddr, s32 pages, MemoryState desiredState) {
|
||||
for (auto& alloc : memoryInfo) {
|
||||
// Don't bother checking if we're to the left of the requested region
|
||||
if (vaddr >= alloc.end()) continue;
|
||||
if (alloc.state != desiredState) return Result::FailurePlaceholder; // TODO: error for state mismatch
|
||||
|
||||
// If the end of this block comes after the end of the requested range with no errors, it's a success
|
||||
if (alloc.end() >= vaddr + (pages << 12)) return Result::Success;
|
||||
}
|
||||
|
||||
// TODO: error for when address is outside of userland
|
||||
return Result::FailurePlaceholder;
|
||||
}
|
||||
|
||||
void Memory::copyToVaddr(u32 dstVaddr, const u8* srcHost, s32 size) {
|
||||
// TODO: check for noncontiguous allocations
|
||||
u8* dstHost = (u8*)readTable[dstVaddr >> 12] + (dstVaddr & 0xFFF);
|
||||
memcpy(dstHost, srcHost, size);
|
||||
}
|
||||
|
||||
u8* Memory::mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerms) {
|
||||
@@ -459,13 +568,11 @@ u8* Memory::mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerm
|
||||
bool w = myPerms & 0b010;
|
||||
bool x = myPerms & 0b100;
|
||||
|
||||
const auto result = allocateMemory(vaddr, paddr, size, true, r, w, x, false, true);
|
||||
e.mapped = true;
|
||||
if (!result.has_value()) {
|
||||
Helpers::panic("Memory::mapSharedMemory: Failed to map shared memory block");
|
||||
return nullptr;
|
||||
}
|
||||
Operation op{.newState = MemoryState::Shared, .r = r, .w = x, .x = x, .changeState = true, .changePerms = true};
|
||||
changeMemoryState(vaddr, size >> 12, op);
|
||||
mapPhysicalMemory(vaddr, paddr, size >> 12, r, w, x);
|
||||
|
||||
e.mapped = true;
|
||||
return &fcram[paddr];
|
||||
}
|
||||
}
|
||||
@@ -475,24 +582,6 @@ u8* Memory::mapSharedMemory(Handle handle, u32 vaddr, u32 myPerms, u32 otherPerm
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Memory::mirrorMapping(u32 destAddress, u32 sourceAddress, u32 size) {
|
||||
// Should theoretically be unreachable, only here for safety purposes
|
||||
assert(isAligned(destAddress) && isAligned(sourceAddress) && isAligned(size));
|
||||
|
||||
const u32 pageCount = size / pageSize; // How many pages we need to mirror
|
||||
for (u32 i = 0; i < pageCount; i++) {
|
||||
// Redo the shift here to "properly" handle wrapping around the address space instead of reading OoB
|
||||
const u32 sourcePage = sourceAddress / pageSize;
|
||||
const u32 destPage = destAddress / pageSize;
|
||||
|
||||
readTable[destPage] = readTable[sourcePage];
|
||||
writeTable[destPage] = writeTable[sourcePage];
|
||||
|
||||
sourceAddress += pageSize;
|
||||
destAddress += pageSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the number of ms since Jan 1 1900
|
||||
u64 Memory::timeSince3DSEpoch() {
|
||||
using namespace std::chrono;
|
||||
|
||||
@@ -16,27 +16,27 @@ namespace PICA {
|
||||
decodeTexelAI8ToRG8,
|
||||
true,
|
||||
{
|
||||
.red = MTL::TextureSwizzleRed,
|
||||
.green = MTL::TextureSwizzleRed,
|
||||
.blue = MTL::TextureSwizzleRed,
|
||||
.alpha = MTL::TextureSwizzleGreen,
|
||||
MTL::TextureSwizzleRed,
|
||||
MTL::TextureSwizzleRed,
|
||||
MTL::TextureSwizzleRed,
|
||||
MTL::TextureSwizzleGreen,
|
||||
}}, // IA8
|
||||
{MTL::PixelFormatRG8Unorm, 2, decodeTexelGR8ToRG8}, // RG8
|
||||
{MTL::PixelFormatR8Unorm,
|
||||
1,
|
||||
decodeTexelI8ToR8,
|
||||
true,
|
||||
{.red = MTL::TextureSwizzleRed, .green = MTL::TextureSwizzleRed, .blue = MTL::TextureSwizzleRed, .alpha = MTL::TextureSwizzleOne}}, // I8
|
||||
{MTL::PixelFormatA8Unorm, 1, decodeTexelA8ToA8}, // A8
|
||||
{MTL::PixelFormatABGR4Unorm, 2, decodeTexelAI4ToABGR4}, // IA4
|
||||
{MTL::TextureSwizzleRed, MTL::TextureSwizzleRed, MTL::TextureSwizzleRed, MTL::TextureSwizzleOne}}, // I8
|
||||
{MTL::PixelFormatA8Unorm, 1, decodeTexelA8ToA8}, // A8
|
||||
{MTL::PixelFormatABGR4Unorm, 2, decodeTexelAI4ToABGR4}, // IA4
|
||||
{MTL::PixelFormatR8Unorm,
|
||||
1,
|
||||
decodeTexelI4ToR8,
|
||||
true,
|
||||
{.red = MTL::TextureSwizzleRed, .green = MTL::TextureSwizzleRed, .blue = MTL::TextureSwizzleRed, .alpha = MTL::TextureSwizzleOne}}, // I4
|
||||
{MTL::PixelFormatA8Unorm, 1, decodeTexelA4ToA8}, // A4
|
||||
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1ToRGBA8}, // ETC1
|
||||
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1A4ToRGBA8}, // ETC1A4
|
||||
{MTL::TextureSwizzleRed, MTL::TextureSwizzleRed, MTL::TextureSwizzleRed, MTL::TextureSwizzleOne}}, // I4
|
||||
{MTL::PixelFormatA8Unorm, 1, decodeTexelA4ToA8}, // A4
|
||||
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1ToRGBA8}, // ETC1
|
||||
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1A4ToRGBA8}, // ETC1A4
|
||||
};
|
||||
|
||||
void checkForMTLPixelFormatSupport(MTL::Device* device) {
|
||||
@@ -57,10 +57,10 @@ namespace PICA {
|
||||
decodeTexelAI4ToRG8,
|
||||
true,
|
||||
{
|
||||
.red = MTL::TextureSwizzleRed,
|
||||
.green = MTL::TextureSwizzleRed,
|
||||
.blue = MTL::TextureSwizzleRed,
|
||||
.alpha = MTL::TextureSwizzleGreen,
|
||||
MTL::TextureSwizzleRed,
|
||||
MTL::TextureSwizzleRed,
|
||||
MTL::TextureSwizzleRed,
|
||||
MTL::TextureSwizzleGreen,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace FRDCommands {
|
||||
GetFriendAttributeFlags = 0x00170042,
|
||||
UpdateGameModeDescription = 0x001D0002,
|
||||
|
||||
SaveLocalAccountData = 0x04050000,
|
||||
UpdateMii = 0x040C0800,
|
||||
};
|
||||
}
|
||||
@@ -61,6 +62,7 @@ void FRDService::handleSyncRequest(u32 messagePointer, FRDService::Type type) {
|
||||
if (type == Type::A) {
|
||||
switch (command) {
|
||||
case FRDCommands::UpdateMii: updateMii(messagePointer); break;
|
||||
case FRDCommands::SaveLocalAccountData: saveLocalAccountData(messagePointer); break;
|
||||
default: Helpers::panic("FRD:A service requested. Command: %08X\n", command); break;
|
||||
}
|
||||
} else {
|
||||
@@ -265,6 +267,13 @@ void FRDService::logout(u32 messagePointer) {
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void FRDService::saveLocalAccountData(u32 messagePointer) {
|
||||
log("FRD::SaveLocalAccountData (stubbed)\n");
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x405, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
|
||||
void FRDService::updateMii(u32 messagePointer) {
|
||||
log("FRD::UpdateMii (stubbed)\n");
|
||||
|
||||
|
||||
@@ -194,7 +194,11 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
||||
case FSCommands::SetThisSaveDataSecureValue: setThisSaveDataSecureValue(messagePointer); break;
|
||||
case FSCommands::AbnegateAccessRight: abnegateAccessRight(messagePointer); break;
|
||||
case FSCommands::TheGameboyVCFunction: theGameboyVCFunction(messagePointer); break;
|
||||
default: Helpers::panic("FS service requested. Command: %08X\n", command);
|
||||
|
||||
default:
|
||||
Helpers::warn("Unimplemented FS service requested. Command: %08X\n", command);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace CROHeader {
|
||||
NameOffset = 0x084,
|
||||
NextCRO = 0x088,
|
||||
PrevCRO = 0x08C,
|
||||
FixedSize = 0x98,
|
||||
FileSize = 0x090,
|
||||
FixedSize = 0x098,
|
||||
OnUnresolved = 0x0AC,
|
||||
CodeOffset = 0x0B0,
|
||||
DataOffset = 0x0B8,
|
||||
@@ -146,8 +147,10 @@ static const std::string CRO_MAGIC("CRO0");
|
||||
static const std::string CRO_MAGIC_FIXED("FIXD");
|
||||
static const std::string CRR_MAGIC("CRR0");
|
||||
|
||||
using namespace KernelMemoryTypes;
|
||||
|
||||
class CRO {
|
||||
Memory &mem;
|
||||
Memory& mem;
|
||||
|
||||
u32 croPointer; // Origin address of CRO in RAM
|
||||
u32 oldDataSegmentOffset;
|
||||
@@ -155,7 +158,7 @@ class CRO {
|
||||
bool isCRO; // False if CRS
|
||||
|
||||
public:
|
||||
CRO(Memory &mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), oldDataSegmentOffset(0), isCRO(isCRO) {}
|
||||
CRO(Memory& mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), oldDataSegmentOffset(0), isCRO(isCRO) {}
|
||||
~CRO() = default;
|
||||
|
||||
std::string getModuleName() {
|
||||
@@ -164,25 +167,15 @@ class CRO {
|
||||
return mem.readString(moduleName.offset, moduleName.size);
|
||||
}
|
||||
|
||||
u32 getNextCRO() {
|
||||
return mem.read32(croPointer + CROHeader::NextCRO);
|
||||
}
|
||||
|
||||
u32 getPrevCRO() {
|
||||
return mem.read32(croPointer + CROHeader::PrevCRO);
|
||||
}
|
||||
u32 getNextCRO() { return mem.read32(croPointer + CROHeader::NextCRO); }
|
||||
|
||||
u32 getFixedSize() {
|
||||
return mem.read32(croPointer + CROHeader::FixedSize);
|
||||
}
|
||||
u32 getPrevCRO() { return mem.read32(croPointer + CROHeader::PrevCRO); }
|
||||
|
||||
void setNextCRO(u32 nextCRO) {
|
||||
mem.write32(croPointer + CROHeader::NextCRO, nextCRO);
|
||||
}
|
||||
u32 getFixedSize() { return mem.read32(croPointer + CROHeader::FixedSize); }
|
||||
|
||||
void setPrevCRO(u32 prevCRO) {
|
||||
mem.write32(croPointer + CROHeader::PrevCRO, prevCRO);
|
||||
}
|
||||
void setNextCRO(u32 nextCRO) { mem.write32(croPointer + CROHeader::NextCRO, nextCRO); }
|
||||
void setPrevCRO(u32 prevCRO) { mem.write32(croPointer + CROHeader::PrevCRO, prevCRO); }
|
||||
u32 getSize() { return mem.read32(croPointer + CROHeader::FileSize); }
|
||||
|
||||
void write32(u32 addr, u32 value) {
|
||||
// Note: some games export symbols to the static module, which doesn't contain any segments.
|
||||
@@ -228,21 +221,17 @@ class CRO {
|
||||
return entryOffset + offset;
|
||||
}
|
||||
|
||||
u32 getOnUnresolvedAddr() {
|
||||
return getSegmentAddr(mem.read32(croPointer + CROHeader::OnUnresolved));
|
||||
}
|
||||
u32 getOnUnresolvedAddr() { return getSegmentAddr(mem.read32(croPointer + CROHeader::OnUnresolved)); }
|
||||
|
||||
u32 getNamedExportSymbolAddr(const std::string& symbolName) {
|
||||
// Note: The CRO contains a trie for fast symbol lookup. For simplicity,
|
||||
// we won't use it and instead look up the symbol in the named export symbol table
|
||||
|
||||
const u32 exportStringSize = mem.read32(croPointer + CROHeader::ExportStringSize);
|
||||
|
||||
const CROHeaderEntry namedExportTable = getHeaderEntry(CROHeader::NamedExportTableOffset);
|
||||
|
||||
for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) {
|
||||
const u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport + NamedExportTable::NameOffset);
|
||||
|
||||
const std::string exportSymbolName = mem.readString(nameOffset, exportStringSize);
|
||||
|
||||
if (symbolName.compare(exportSymbolName) == 0) {
|
||||
@@ -437,7 +426,7 @@ class CRO {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rebaseSegmentTable(u32 dataVaddr, u32 bssVaddr, u32 *oldDataVaddr) {
|
||||
bool rebaseSegmentTable(u32 dataVaddr, u32 bssVaddr, u32* oldDataVaddr) {
|
||||
const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset);
|
||||
|
||||
for (u32 segment = 0; segment < segmentTable.size; segment++) {
|
||||
@@ -446,13 +435,16 @@ class CRO {
|
||||
const u32 segmentID = mem.read32(segmentTable.offset + 12 * segment + SegmentTable::ID);
|
||||
switch (segmentID) {
|
||||
case SegmentTable::SegmentID::DATA:
|
||||
*oldDataVaddr = segmentOffset + croPointer; oldDataSegmentOffset = segmentOffset; segmentOffset = dataVaddr; break;
|
||||
*oldDataVaddr = segmentOffset + croPointer;
|
||||
oldDataSegmentOffset = segmentOffset;
|
||||
segmentOffset = dataVaddr;
|
||||
break;
|
||||
case SegmentTable::SegmentID::BSS: segmentOffset = bssVaddr; break;
|
||||
case SegmentTable::SegmentID::TEXT:
|
||||
case SegmentTable::SegmentID::RODATA:
|
||||
if (segmentOffset != 0) segmentOffset += croPointer; break;
|
||||
default:
|
||||
Helpers::panic("Unknown segment ID = %u", segmentID);
|
||||
if (segmentOffset != 0) segmentOffset += croPointer;
|
||||
break;
|
||||
default: Helpers::panic("Unknown segment ID = %u", segmentID);
|
||||
}
|
||||
|
||||
mem.write32(segmentTable.offset + 12 * segment + SegmentTable::Offset, segmentOffset);
|
||||
@@ -473,9 +465,9 @@ class CRO {
|
||||
case SegmentTable::SegmentID::BSS: segmentOffset = 0; break;
|
||||
case SegmentTable::SegmentID::TEXT:
|
||||
case SegmentTable::SegmentID::RODATA:
|
||||
if (segmentOffset != 0) segmentOffset -= croPointer; break;
|
||||
default:
|
||||
Helpers::panic("Unknown segment ID = %u", segmentID);
|
||||
if (segmentOffset != 0) segmentOffset -= croPointer;
|
||||
break;
|
||||
default: Helpers::panic("Unknown segment ID = %u", segmentID);
|
||||
}
|
||||
|
||||
mem.write32(segmentTable.offset + 12 * segment + SegmentTable::Offset, segmentOffset);
|
||||
@@ -639,7 +631,9 @@ class CRO {
|
||||
u32 relocationOffset = mem.read32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset);
|
||||
|
||||
if (relocationOffset != 0) {
|
||||
mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + croPointer);
|
||||
mem.write32(
|
||||
anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + croPointer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -653,7 +647,9 @@ class CRO {
|
||||
u32 relocationOffset = mem.read32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset);
|
||||
|
||||
if (relocationOffset != 0) {
|
||||
mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset - croPointer);
|
||||
mem.write32(
|
||||
anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset - croPointer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,7 +669,6 @@ class CRO {
|
||||
const u32 addend = mem.read32(relocationPatchTable.offset + 12 * relocationPatch + RelocationPatch::Addend);
|
||||
|
||||
const u32 segmentAddr = getSegmentAddr(segmentOffset);
|
||||
|
||||
const u32 entryID = mem.read32(segmentTable.offset + 12 * (segmentOffset & 0xF) + SegmentTable::ID);
|
||||
|
||||
u32 relocationTarget = segmentAddr;
|
||||
@@ -1198,9 +1193,7 @@ class CRO {
|
||||
}
|
||||
};
|
||||
|
||||
void LDRService::reset() {
|
||||
loadedCRS = 0;
|
||||
}
|
||||
void LDRService::reset() { loadedCRS = 0; }
|
||||
|
||||
void LDRService::handleSyncRequest(u32 messagePointer) {
|
||||
const u32 command = mem.read32(messagePointer);
|
||||
@@ -1245,7 +1238,14 @@ void LDRService::initialize(u32 messagePointer) {
|
||||
}
|
||||
|
||||
// Map CRO to output address
|
||||
mem.mirrorMapping(mapVaddr, crsPointer, size);
|
||||
// TODO: how to handle permissions?
|
||||
bool succeeded = mem.mapVirtualMemory(
|
||||
mapVaddr, crsPointer, size >> 12, true, true, true, MemoryState::Free, MemoryState::Private, MemoryState::Locked, MemoryState::AliasCode
|
||||
);
|
||||
|
||||
if (!succeeded) {
|
||||
Helpers::panic("Failed to map CRS");
|
||||
}
|
||||
|
||||
CRO crs(mem, mapVaddr, false);
|
||||
|
||||
@@ -1312,7 +1312,9 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) {
|
||||
const u32 fixLevel = mem.read32(messagePointer + 40);
|
||||
const Handle process = mem.read32(messagePointer + 52);
|
||||
|
||||
log("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process);
|
||||
log("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size "
|
||||
"= %08X, auto link = %d, fix level = %X, process = %X)\n",
|
||||
isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process);
|
||||
|
||||
// Sanity checks
|
||||
if (size < CRO_HEADER_SIZE) {
|
||||
@@ -1332,7 +1334,14 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) {
|
||||
}
|
||||
|
||||
// Map CRO to output address
|
||||
mem.mirrorMapping(mapVaddr, croPointer, size);
|
||||
// TODO: how to handle permissions?
|
||||
bool succeeded = mem.mapVirtualMemory(
|
||||
mapVaddr, croPointer, size >> 12, true, true, true, MemoryState::Free, MemoryState::Private, MemoryState::Locked, MemoryState::AliasCode
|
||||
);
|
||||
|
||||
if (!succeeded) {
|
||||
Helpers::panic("Failed to map CRO");
|
||||
}
|
||||
|
||||
CRO cro(mem, mapVaddr, true);
|
||||
|
||||
@@ -1392,7 +1401,18 @@ void LDRService::unloadCRO(u32 messagePointer) {
|
||||
Helpers::panic("Failed to unrebase CRO");
|
||||
}
|
||||
|
||||
u32 size = cro.getSize();
|
||||
bool succeeded = mem.mapVirtualMemory(
|
||||
mapVaddr, croPointer, size >> 12, false, false, false, MemoryState::Locked, MemoryState::AliasCode, MemoryState::Free, MemoryState::Private,
|
||||
false
|
||||
);
|
||||
|
||||
if (!succeeded) {
|
||||
Helpers::panic("Failed to unmap CRO");
|
||||
}
|
||||
|
||||
kernel.clearInstructionCacheRange(mapVaddr, cro.getFixedSize());
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
}
|
||||
Reference in New Issue
Block a user