coreinit: Fix memory mapping API

Fixes Unity based games freezing on boot
This commit is contained in:
Exzap 2023-01-25 10:04:30 +01:00
parent f2671f417f
commit 4b5014c16a
5 changed files with 75 additions and 96 deletions

View File

@ -8,4 +8,5 @@ namespace Espresso
constexpr inline uint64 BUS_CLOCK = 248625000; constexpr inline uint64 BUS_CLOCK = 248625000;
constexpr inline uint64 TIMER_CLOCK = BUS_CLOCK / 4; constexpr inline uint64 TIMER_CLOCK = BUS_CLOCK / 4;
constexpr inline uint32 MEM_PAGE_SIZE = 0x20000;
}; };

View File

@ -138,8 +138,6 @@ MMURange* memory_getMMURangeByAddress(MPTR address);
bool memory_isAddressRangeAccessible(MPTR virtualAddress, uint32 size); bool memory_isAddressRangeAccessible(MPTR virtualAddress, uint32 size);
#define MEMORY_PAGE_SIZE (0x20000)
#define MEMORY_CODELOW0_ADDR (0x00010000) #define MEMORY_CODELOW0_ADDR (0x00010000)
#define MEMORY_CODELOW0_SIZE (0x000F0000) // ~1MB #define MEMORY_CODELOW0_SIZE (0x000F0000) // ~1MB
@ -158,27 +156,11 @@ bool memory_isAddressRangeAccessible(MPTR virtualAddress, uint32 size);
#define MEMORY_FGBUCKET_AREA_ADDR (0xE0000000) // actual offset is 0xE0000000 according to PPC kernel #define MEMORY_FGBUCKET_AREA_ADDR (0xE0000000) // actual offset is 0xE0000000 according to PPC kernel
#define MEMORY_FGBUCKET_AREA_SIZE (0x04000000) // 64MB split up into multiple subareas, size is verified with value from PPC kernel #define MEMORY_FGBUCKET_AREA_SIZE (0x04000000) // 64MB split up into multiple subareas, size is verified with value from PPC kernel
// move these to rpl loader
#define MEMORY_SDA_SIZE (0x10000)
#define MEMORY_SDA2_SIZE (0x10000)
//#define MEMORY_SYSTEM_AREA_ADDR (0x90000000)
//#define MEMORY_SYSTEM_AREA_SIZE (0x02000000) // 32MB of memory area that can't be allocated by the game directly - this is emulator specific.
//#define MEMORY_SYSTEM_AREA_ADDR (0x7C000000)
//#define MEMORY_SYSTEM_AREA_SIZE (0x02000000) // 32MB of memory area that can't be allocated by the game directly - this is emulator specific.
// moved the sys area below 0x80000000. Turns out that some games treat stack/os-object pointers as signed and run into issues if the highest bit is set (e.g. Monster Hunter Frontier G)
#define MEMORY_TILINGAPERTURE_AREA_ADDR (0xE8000000) #define MEMORY_TILINGAPERTURE_AREA_ADDR (0xE8000000)
#define MEMORY_TILINGAPERTURE_AREA_SIZE (0x02000000) // 32MB #define MEMORY_TILINGAPERTURE_AREA_SIZE (0x02000000) // 32MB
#define MEMORY_OVERLAY_AREA_OFFSET (0xA0000000) #define MEMORY_OVERLAY_AREA_OFFSET (0xA0000000)
#define MEMORY_OVERLAY_AREA_SIZE (448*1024*1024) // 448MB (todo: verify if correct) #define MEMORY_OVERLAY_AREA_SIZE (448*1024*1024) // 448MB (recycled background app memory)
#define MEMORY_MAPABLE_PHYS_AREA_OFFSET (0x80000000) // todo: verify offset
#define MEMORY_MAPABLE_PHYS_AREA_SIZE (32*1024*1024) // todo: verify size
#define MEMORY_MAPABLE_VIRT_AREA_OFFSET (0x70000000) // todo: verify offset
#define MEMORY_MAPABLE_VIRT_AREA_SIZE (32*1024*1024) // todo: verify size
#define MEMORY_MEM1_AREA_ADDR (0xF4000000) #define MEMORY_MEM1_AREA_ADDR (0xF4000000)
#define MEMORY_MEM1_AREA_SIZE (0x02000000) // 32MB #define MEMORY_MEM1_AREA_SIZE (0x02000000) // 32MB
@ -191,7 +173,6 @@ bool memory_isAddressRangeAccessible(MPTR virtualAddress, uint32 size);
static uint16 CPU_swapEndianU16(uint16 v) static uint16 CPU_swapEndianU16(uint16 v)
{ {
//return _byteswap_ushort(v);
return (v>>8)|(v<<8); return (v>>8)|(v<<8);
} }

View File

@ -366,6 +366,7 @@ void coreinit_load()
coreinit::InitializeMessageQueue(); coreinit::InitializeMessageQueue();
coreinit::InitializeIPC(); coreinit::InitializeIPC();
coreinit::InitializeIPCBuf(); coreinit::InitializeIPCBuf();
coreinit::InitializeMemoryMapping();
coreinit::InitializeCodeGen(); coreinit::InitializeCodeGen();
coreinit::InitializeCoroutine(); coreinit::InitializeCoroutine();
coreinit::InitializeOSScreen(); coreinit::InitializeOSScreen();

View File

@ -4,24 +4,34 @@
#define OS_MAP_READ_ONLY (1) #define OS_MAP_READ_ONLY (1)
#define OS_MAP_READ_WRITE (2) #define OS_MAP_READ_WRITE (2)
#define VIRT_RANGE_ADDR (0xA0000000) // todo: Process specific. For the main ram pid this overlaps with the overlay arena?
#define VIRT_RANGE_SIZE (0x40000000)
#define PHYS_RANGE_ADDR (0x10000000) // todo: Process specific
#define PHYS_RANGE_SIZE (0x40000000)
namespace coreinit namespace coreinit
{ {
std::mutex s_memMappingMtx;
struct OSVirtMemory struct OSVirtMemoryEntry
{ {
OSVirtMemoryEntry(MPTR virtualAddress, uint32 size, uint32 alignment) : virtualAddress(virtualAddress), size(size), alignment(alignment) {};
MPTR virtualAddress; MPTR virtualAddress;
uint32 size; uint32 size;
uint32 alignment; uint32 alignment;
OSVirtMemory* next;
}; };
OSVirtMemory* virtualMemoryList = nullptr; std::vector<OSVirtMemoryEntry> s_allocatedVirtMemory;
MPTR _VirtualMemoryAlloc(uint32 size, uint32 alignment) MPTR _OSAllocVirtAddr(uint32 size, uint32 alignment)
{ {
uint32 currentAddress = MEMORY_MAPABLE_VIRT_AREA_OFFSET; std::lock_guard _l(s_memMappingMtx);
uint32 endAddress = MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE; uint32 currentAddress = VIRT_RANGE_ADDR;
uint32 endAddress = VIRT_RANGE_ADDR + VIRT_RANGE_SIZE;
uint32 pageSize = (uint32)MemMapper::GetPageSize(); uint32 pageSize = (uint32)MemMapper::GetPageSize();
pageSize = std::max<uint32>(pageSize, Espresso::MEM_PAGE_SIZE);
while (true) while (true)
{ {
// calculated aligned start and end address for current region // calculated aligned start and end address for current region
@ -30,95 +40,85 @@ namespace coreinit
uint32 currentEndAddress = currentAddress + size; uint32 currentEndAddress = currentAddress + size;
currentEndAddress = (currentEndAddress + pageSize - 1) & ~(pageSize - 1); currentEndAddress = (currentEndAddress + pageSize - 1) & ~(pageSize - 1);
// check if out of available space // check if out of available space
if (currentEndAddress >= endAddress) if (currentEndAddress > endAddress)
{ {
debug_printf("coreinitVirtualMemory_alloc(): Unable to allocate memory\n"); cemuLog_log(LogType::APIErrors, "_OSAllocVirtAddr(): Unable to allocate memory\n");
debugBreakpoint();
return MPTR_NULL; return MPTR_NULL;
} }
// check for overlapping regions // check for overlapping regions
OSVirtMemory* virtMemItr = virtualMemoryList;
bool emptySpaceFound = true; bool emptySpaceFound = true;
while (virtMemItr) for(auto& virtMemIt : s_allocatedVirtMemory)
{ {
// check for range collision // check for range collision
if (currentAddress < (virtMemItr->virtualAddress + virtMemItr->size) && currentEndAddress > virtMemItr->virtualAddress) if (currentAddress < (virtMemIt.virtualAddress + virtMemIt.size) && currentEndAddress > virtMemIt.virtualAddress)
{ {
// regions overlap // regions overlap
// adjust current address and try again // adjust current address and keep looking
currentAddress = virtMemItr->virtualAddress + virtMemItr->size; currentAddress = virtMemIt.virtualAddress + virtMemIt.size;
emptySpaceFound = false; emptySpaceFound = false;
break; break;
} }
// next
virtMemItr = virtMemItr->next;
} }
if (emptySpaceFound) if (emptySpaceFound)
{ {
// add entry // add entry
OSVirtMemory* virtMemory = (OSVirtMemory*)malloc(sizeof(OSVirtMemory)); s_allocatedVirtMemory.emplace_back(currentAddress, currentEndAddress - currentAddress, alignment);
memset(virtMemory, 0x00, sizeof(OSVirtMemory));
virtMemory->virtualAddress = currentAddress;
virtMemory->size = currentEndAddress - currentAddress;
virtMemory->alignment = alignment;
virtMemory->next = virtualMemoryList;
virtualMemoryList = virtMemory;
return currentAddress; return currentAddress;
} }
} }
return MPTR_NULL; return MPTR_NULL;
} }
void coreinitExport_OSGetAvailPhysAddrRange(PPCInterpreter_t* hCPU) bool _OSFreeVirtAddr(MPTR virtAddr)
{ {
// parameters: std::lock_guard _l(s_memMappingMtx);
// r3 MPTR* areaStart auto it = s_allocatedVirtMemory.begin();
// r4 uint32 areaSize while (it != s_allocatedVirtMemory.end())
memory_writeU32(hCPU->gpr[3], MEMORY_MAPABLE_PHYS_AREA_OFFSET); {
memory_writeU32(hCPU->gpr[4], MEMORY_MAPABLE_PHYS_AREA_SIZE); if (it->virtualAddress == virtAddr)
{
osLib_returnFromFunction(hCPU, 0); s_allocatedVirtMemory.erase(it);
return true;
}
++it;
}
return false;
} }
void coreinitExport_OSAllocVirtAddr(PPCInterpreter_t* hCPU) void OSGetAvailPhysAddrRange(uint32be* physRangeStart, uint32be* physRangeSize)
{ {
// parameters: *physRangeStart = PHYS_RANGE_ADDR;
// r3 MPTR address *physRangeSize = PHYS_RANGE_SIZE;
// r4 uint32 size
// r5 uint32 align
uint32 address = hCPU->gpr[3];
uint32 size = hCPU->gpr[4];
uint32 align = hCPU->gpr[5];
if (address != MPTR_NULL)
{
debug_printf("coreinitExport_OSAllocVirtAddr(): Unsupported address != NULL\n");
debugBreakpoint();
} }
void* OSAllocVirtAddr(MEMPTR<void> address, uint32 size, uint32 align)
{
if (align == 0) if (align == 0)
align = 1; align = 1;
if (align != 0 && align != 1) if (align != 0 && align != 1)
assert_dbg(); assert_dbg();
cemu_assert_debug(address == nullptr); // todo - support for allocation with fixed address
address = _VirtualMemoryAlloc(size, align); address = _OSAllocVirtAddr(size, align);
debug_printf("coreinitExport_OSAllocVirtAddr(): Allocated virtual memory at 0x%08x\n", address); debug_printf("OSAllocVirtAddr(): Allocated virtual memory at 0x%08x\n", address.GetMPTR());
osLib_returnFromFunction(hCPU, address); return MEMPTR<void>(address);
} }
void coreinitExport_OSMapMemory(PPCInterpreter_t* hCPU) uint32 OSFreeVirtAddr(MEMPTR<void> address)
{ {
// parameters: bool r = _OSFreeVirtAddr(address.GetMPTR());
// r3 MPTR virtualAddress if(!r)
// r4 MPTR physicalAddress cemuLog_log(LogType::APIErrors, "OSFreeVirtAddr: Could not find allocation with address 0x{:08x}\n", address.GetMPTR());
// r5 uint32 size return r ? 1 : 0;
// r6 uint32 mode }
MPTR virtualAddress = hCPU->gpr[3];
MPTR physicalAddress = hCPU->gpr[4];
uint32 size = hCPU->gpr[5];
uint32 mode = hCPU->gpr[6];
if (virtualAddress < MEMORY_MAPABLE_VIRT_AREA_OFFSET || virtualAddress >= (MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE)) uint32 OSMapMemory(MPTR virtualAddress, MPTR physicalAddress, uint32 size, uint32 mode)
cemu_assert_suspicious(); {
if (virtualAddress < VIRT_RANGE_ADDR || virtualAddress >= (VIRT_RANGE_ADDR + VIRT_RANGE_SIZE))
{
cemuLog_log(LogType::APIErrors, "OSMapMemory: Virtual address out of bounds\n");
return 0;
}
uint8* virtualPtr = memory_getPointerFromVirtualOffset(virtualAddress); uint8* virtualPtr = memory_getPointerFromVirtualOffset(virtualAddress);
@ -133,21 +133,14 @@ namespace coreinit
if (!allocationResult) if (!allocationResult)
{ {
cemuLog_log(LogType::Force, "OSMapMemory failed"); cemuLog_log(LogType::Force, "OSMapMemory failed");
osLib_returnFromFunction(hCPU, 0); return 0;
return;
} }
osLib_returnFromFunction(hCPU, 1); return 1;
} }
void coreinitExport_OSUnmapMemory(PPCInterpreter_t* hCPU) uint32 OSUnmapMemory(MPTR virtualAddress, uint32 size)
{ {
// parameters: if (virtualAddress < VIRT_RANGE_ADDR || virtualAddress >= (VIRT_RANGE_ADDR + VIRT_RANGE_SIZE))
// r3 MPTR virtualAddress
// r4 uint32 size
MPTR virtualAddress = hCPU->gpr[3];
uint32 size = hCPU->gpr[4];
if (virtualAddress < MEMORY_MAPABLE_VIRT_AREA_OFFSET || virtualAddress >= (MEMORY_MAPABLE_VIRT_AREA_OFFSET + MEMORY_MAPABLE_VIRT_AREA_SIZE))
cemu_assert_suspicious(); cemu_assert_suspicious();
cemu_assert((size % MemMapper::GetPageSize()) == 0); cemu_assert((size % MemMapper::GetPageSize()) == 0);
@ -155,14 +148,16 @@ namespace coreinit
uint8* virtualPtr = memory_getPointerFromVirtualOffset(virtualAddress); uint8* virtualPtr = memory_getPointerFromVirtualOffset(virtualAddress);
MemMapper::FreeMemory(virtualPtr, size, true); MemMapper::FreeMemory(virtualPtr, size, true);
osLib_returnFromFunction(hCPU, 1); return 1;
} }
void InitializeMemoryMapping() void InitializeMemoryMapping()
{ {
osLib_addFunction("coreinit", "OSGetAvailPhysAddrRange", coreinitExport_OSGetAvailPhysAddrRange); s_allocatedVirtMemory.clear();
osLib_addFunction("coreinit", "OSAllocVirtAddr", coreinitExport_OSAllocVirtAddr); cafeExportRegister("coreinit", OSGetAvailPhysAddrRange, LogType::CoreinitMemoryMapping);
osLib_addFunction("coreinit", "OSMapMemory", coreinitExport_OSMapMemory); cafeExportRegister("coreinit", OSAllocVirtAddr, LogType::CoreinitMemoryMapping);
osLib_addFunction("coreinit", "OSUnmapMemory", coreinitExport_OSUnmapMemory); cafeExportRegister("coreinit", OSFreeVirtAddr, LogType::CoreinitMemoryMapping);
cafeExportRegister("coreinit", OSMapMemory, LogType::CoreinitMemoryMapping);
cafeExportRegister("coreinit", OSUnmapMemory, LogType::CoreinitMemoryMapping);
} }
} }

View File

@ -23,6 +23,7 @@ enum class LogType : sint32
CoreinitMP = 16, CoreinitMP = 16,
CoreinitThread = 17, CoreinitThread = 17,
CoreinitLogging = 18, // OSReport, OSConsoleWrite etc. CoreinitLogging = 18, // OSReport, OSConsoleWrite etc.
CoreinitMemoryMapping = 19, // OSGetAvailPhysAddrRange, OSAllocVirtAddr, OSMapMemory etc.
PPC_IPC = 20, PPC_IPC = 20,
NN_AOC = 21, NN_AOC = 21,