Skip to content

Instantly share code, notes, and snippets.

@exjam
Last active November 7, 2017 22:15
Show Gist options
  • Save exjam/c5ee47c1e9c8301d5b143bfb0616858f to your computer and use it in GitHub Desktop.
Save exjam/c5ee47c1e9c8301d5b143bfb0616858f to your computer and use it in GitHub Desktop.
#include "cafe_loader.h"
#include "cafe_loader_bounce.h"
#include "cafe_loader_iop.h"
#include "cafe_loader_ipcldriver.h"
#include "cafe/cafe_stackobject.h"
#include "cafe/cafe_tinyheap.h"
#include "cafe/kernel/cafe_kernel_process.h"
#include "cafe_loader_rpl.h"
#include <array>
#include <common/align.h>
#include <common/cbool.h>
#include <libcpu/cpu.h>
#include <libcpu/be2_struct.h>
#include <zlib.h>
namespace cafe::loader
{
struct RPL_STARTINFO
{
UNKNOWN(0x4);
be2_val<virt_addr> unk_0x04;
UNKNOWN(0x1C);
be2_val<virt_addr> unk_0x24;
};
CHECK_SIZE(RPL_STARTINFO, 0x28);
struct LoaderShared
{
be2_val<uint32_t> unk0x00;
be2_val<uint32_t> unk0x04;
be2_val<uint32_t> unk0x08;
be2_val<uint32_t> unk0x0C;
be2_val<uint32_t> unk0x10;
be2_val<uint32_t> unk0x14;
};
CHECK_SIZE(LoaderShared, 0x18);
struct StaticLoaderData
{
// .data 0xEFE00000 - 0xEFE0A400
be2_array<char, 0x1000> moduleName; // 0xEFE00000
be2_val<kernel::UniqueProcessId> upid; // 0xEFE01000
be2_virt_ptr<TinyHeap> codeAreaTracking; // 0xEFE01004
be2_val<uint32_t> codeAreaTrackingSize; // 0xEFE01008
be2_val<uint32_t> codeAreaNumBlocks; // 0xEFE0100C
be2_val<uint32_t> codeAreaSize; // 0xEFE01010
be2_val<uint32_t> maxCodeSize; // 0xEFE01014
be2_val<uint32_t> unk_0xEFE01018; // 0xEFE01018
// .bss EFE19E80
be2_array<BOOL, 3> gIPCLInitialized; // 0xEFE13DFC
be2_val<uint32_t> gProcFlags; // 0xEFE1AEA8
be2_val<uint32_t> gProcTitleLoc; // 0xEFE1AEAC
be2_val<uint32_t> gFatalErr; // 0xEFE1AEB0
be2_val<uint32_t> gFatalLine; // 0xEFE1AEB4
be2_val<uint32_t> gFatalMsgType; // 0xEFE1AEB8
be2_virt_ptr<char> gFatalFunction; // 0xEFE1AEBC
be2_virt_ptr<TinyHeap> gpSharedCodeHeapTracking; // 0xEFE1AEC0
be2_virt_ptr<TinyHeap> gpSharedReadHeapTracking; // 0xEFE1AEC4
be2_virt_ptr<LoaderShared> gpLoaderShared; // 0xEFE1AEC8
be2_val<uint32_t> dataAreaSize; // 0xEFE1CEE0
be2_val<uint32_t> dataAreaAllocations; // 0xEFE1CEE4
be2_virt_ptr<TinyHeap> sgpTrackComp; // 0xEFE1CEE8
};
// Contained in 0xEFE0A400 - 0xEFE0A4C0
struct IpcData
{
UNKNOWN(4); // 0xEFE0A400
be2_val<uint32_t> procFlags; // 0xEFE0A404
UNKNOWN(4); // 0xEFE0A408
be2_val<kernel::UniqueProcessId> upid; // 0xEFE0A40C
be2_val<uint32_t> num_codearea_heap_blocks; // 0xEFE0A410
be2_val<uint32_t> max_codesize; // 0xEFE0A414
be2_val<uint32_t> unk_0xEFE0A418;
be2_val<uint32_t> procTitleLoc; // 0xEFE0A41C
be2_val<uint32_t> unk_0xEFE0A420;
be2_val<uint32_t> unk_0xEFE0A424;
UNKNOWN(4); // 0xEFE0A428
be2_val<uint32_t> fatalMsgType; // 0xEFE0A42C
be2_val<uint32_t> fatalErr; // 0xEFE0A430
be2_val<uint32_t> loaderInitResult; // 0xEFE0A434
be2_val<uint32_t> fatalLine; // 0xEFE0A438
be2_array<char, 64> fatalFunction; // 0xEFE0A43C
be2_struct<RPL_STARTINFO> rplStartInfo; // 0xEFE0A47C
UNKNOWN(0xEFE0A4C0 - 0xEFE0A4A4);
};
CHECK_SIZE(IpcData, 0xEFE0A4C0 - 0xEFE0A400);
virt_ptr<IpcData>
gIpcData;
virt_ptr<StaticLoaderData>
sData;
constexpr auto MaxCodeSize = 224u * 1024 * 1024;
// LOADER_Entry
void
entry(void *params)
{
}
constexpr auto DataAreaBase = virt_addr { 0x10000000 };
constexpr auto SharedCodeTrackingSize = 0x830;
constexpr auto SharedReadTrackingSize = 0x1030;
namespace internal
{
void
LiResolveModuleName(virt_ptr<char> *moduleName,
uint32_t *moduleNameLen)
{
auto path = std::string_view { moduleName->getRawPointer(), *moduleNameLen };
auto prefix = path.find_last_of("\/");
if (prefix != std::string_view::npos) {
*moduleName += prefix;
*moduleNameLen -= prefix;
}
auto suffix = path.find_last_of(".");
if (suffix != std::string_view::npos) {
*moduleNameLen -= suffix;
}
}
int32_t
ELFFILE_ValidateAndPrepareMinELF(virt_ptr<void> chunkBuffer,
uint32_t chunkReadSize,
virt_ptr<rpl::Header> *outElfHeader,
virt_ptr<rpl::SectionHeader> *outSectionHeaders,
uint32_t *outShEntSize,
uint32_t *outPhEntSize)
{
if (chunkReadSize < 0x104) {
return 0xBAD00018;
}
auto header = virt_cast<rpl::Header *>(chunkBuffer);
if (outElfHeader) {
*outElfHeader = header;
}
if (header->magic != rpl::Header::Magic) {
return 0xBAD00019;
}
if (header->fileClass != rpl::ELFCLASS32) {
return 0xBAD0001A;
}
if (header->elfVersion != rpl::EV_CURRENT) {
return 0xBAD0001B;
}
decaf_check(header->encoding == rpl::ELFDATA2MSB);
if (header->machine != rpl::EM_PPC) {
return 0xBAD0001C;
}
if (header->version != 1) {
return 0xBAD0001D;
}
if (header->ehsize != sizeof(rpl::Header) ||
(chunkReadSize - 0xD0) < header->ehsize) {
return 0xBAD0001E;
}
if (header->phoff) {
if (header->phoff < sizeof(rpl::Header) ||
header->phoff < chunkReadSize) {
return 0xBAD0001F;
}
}
if (header->shoff) {
if (header->phoff < sizeof(rpl::Header) ||
header->phoff < chunkReadSize) {
return 0xBAD00020;
}
}
if (header->shstrndx) {
if (header->shstrndx > header->shnum) {
return 0xBAD00021;
}
}
auto phEntSize = header->phentsize;
if (!phEntSize) {
phEntSize = sizeof(rpl::ProgramHeader);
}
if (outPhEntSize) {
*outPhEntSize = phEntSize;
}
if (header->phoff) {
if (header->phoff + (header->phnum * phEntSize) >= chunkReadSize) {
return 0xBAD00022;
}
}
auto shEntSize = header->shentsize;
if (!shEntSize) {
shEntSize = sizeof(rpl::SectionHeader);
}
if (outShEntSize) {
*outShEntSize = shEntSize;
}
if (header->shoff) {
if (header->shoff + (header->shnum * shEntSize) >= chunkReadSize) {
return 0xBAD00023;
}
}
auto sectionHeaders = virt_cast<rpl::SectionHeader *>(virt_cast<virt_addr>(chunkBuffer) + header->shoff);
if (outSectionHeaders) {
*outSectionHeaders = sectionHeaders;
}
for (auto i = 1; i < header->shnum; ++i) {
auto sectionHeader = virt_cast<rpl::SectionHeader *>(virt_cast<virt_addr>(sectionHeaders) + shEntSize * i);
if (sectionHeader->size == 0) {
continue;
}
if (sectionHeader->type == rpl::SHT_NOBITS) {
continue;
}
if (sectionHeader->offset >= chunkReadSize) {
return 0xBAD00024;
}
if (header->phoff && header->phnum) {
// TODO: Verify something with sectionHeader->info
}
if (header->shoff) {
// TODO: Verify something with sectionHeader->info
}
for (auto j = i + 1; j < header->shnum; ++j) {
// TODO: Verify something with size+offset and sectionHeader->info
}
}
// TODO: Verify program headers
return 0;
}
struct RPLBasics
{
be2_val<cafe::kernel::UniqueProcessId> upid0x00;
be2_val<uint32_t> selfBufferSize;
be2_virt_ptr<void> moduleNameBuffer;
be2_val<uint32_t> moduleNameLen;
be2_val<uint32_t> moduleNameBufferSize;
be2_virt_ptr<void> pathBuffer;
be2_val<uint32_t> pathBufferSize;
be2_struct<rpl::Header> elfHeader;
be2_virt_ptr<void> sectionHeaderBuffer;
be2_val<uint32_t> sectionHeaderBufferSize;
be2_virt_ptr<void> rplFileInfoBuffer;
be2_val<uint32_t> rplFileInfoSize;
be2_val<uint32_t> rplFileInfoBufferSize;
be2_virt_ptr<void> rplCrcBuffer;
be2_val<uint32_t> rplCrcBufferSize;
UNKNOWN(0x4);
be2_val<uint32_t> unk0x70;
UNKNOWN(0x4);
be2_val<uint32_t> unk0x78;
be2_virt_ptr<void> firstChunkBuffer;
be2_val<uint32_t> firstChunkBufferSize;
be2_val<uint32_t> unk0x84;
be2_val<uint32_t> fileOffset;
be2_val<ios::mcp::MCPLibraryType> fileType;
be2_val<uint32_t> unk0x90;
be2_virt_ptr<void> chunkBuffer;
UNKNOWN(0xAC - 0x98);
be2_val<cafe::kernel::UniqueProcessId> upid0xAC;
be2_virt_ptr<rpl::Header> firstChunkElfHeader;
be2_virt_ptr<void> textBuffer;
be2_val<uint32_t> textBufferSize;
be2_val<uint32_t> unk0xBC;
be2_virt_ptr<void> sharedLibraryLoadBuffer;
UNKNOWN(0xF4 - 0xC4);
be2_virt_ptr<void> sectionAddressBuffer;
be2_val<uint32_t> sectionAddressBufferSize;
UNKNOWN(0x118 - 0xFC);
};
CHECK_OFFSET(RPLBasics, 0x00, upid0x00);
CHECK_OFFSET(RPLBasics, 0x04, selfBufferSize);
CHECK_OFFSET(RPLBasics, 0x08, moduleNameBuffer);
CHECK_OFFSET(RPLBasics, 0x0C, moduleNameLen);
CHECK_OFFSET(RPLBasics, 0x10, moduleNameBufferSize);
CHECK_OFFSET(RPLBasics, 0x14, pathBuffer);
CHECK_OFFSET(RPLBasics, 0x18, pathBufferSize);
CHECK_OFFSET(RPLBasics, 0x1C, elfHeader);
CHECK_OFFSET(RPLBasics, 0x50, sectionHeaderBuffer);
CHECK_OFFSET(RPLBasics, 0x54, sectionHeaderBufferSize);
CHECK_OFFSET(RPLBasics, 0x58, rplFileInfoBuffer);
CHECK_OFFSET(RPLBasics, 0x5C, rplFileInfoSize);
CHECK_OFFSET(RPLBasics, 0x60, rplFileInfoBufferSize);
CHECK_OFFSET(RPLBasics, 0x64, rplCrcBuffer);
CHECK_OFFSET(RPLBasics, 0x68, rplCrcBufferSize);
CHECK_OFFSET(RPLBasics, 0x70, unk0x70);
CHECK_OFFSET(RPLBasics, 0x78, unk0x78);
CHECK_OFFSET(RPLBasics, 0x7C, firstChunkBuffer);
CHECK_OFFSET(RPLBasics, 0x80, firstChunkBufferSize);
CHECK_OFFSET(RPLBasics, 0x88, fileOffset);
CHECK_OFFSET(RPLBasics, 0x8C, fileType);
CHECK_OFFSET(RPLBasics, 0x90, unk0x90);
CHECK_OFFSET(RPLBasics, 0x94, chunkBuffer);
CHECK_OFFSET(RPLBasics, 0xAC, upid0xAC);
CHECK_OFFSET(RPLBasics, 0xB0, firstChunkElfHeader);
CHECK_OFFSET(RPLBasics, 0xB4, textBuffer);
CHECK_OFFSET(RPLBasics, 0xB8, textBufferSize);
CHECK_OFFSET(RPLBasics, 0xBC, unk0xBC);
CHECK_OFFSET(RPLBasics, 0xC0, sharedLibraryLoadBuffer);
CHECK_OFFSET(RPLBasics, 0xF4, sectionAddressBuffer);
CHECK_OFFSET(RPLBasics, 0xF8, sectionAddressBufferSize);
CHECK_SIZE(RPLBasics, 0x118);
struct RPLBasicsLoadArgs
{
be2_val<cafe::kernel::UniqueProcessId> upid;
be2_virt_ptr<RPLBasics> rplBasics;
be2_virt_ptr<TinyHeap> readHeapTracking;
be2_val<uint32_t> pathNameLen;
be2_virt_ptr<char> pathName;
UNKNOWN(0x4);
be2_val<ios::mcp::MCPLibraryType> fileType;
be2_virt_ptr<void> chunkBuffer;
be2_val<uint32_t> chunkBufferSize;
be2_val<uint32_t> fileOffset;
};
CHECK_OFFSET(RPLBasicsLoadArgs, 0x00, upid);
CHECK_OFFSET(RPLBasicsLoadArgs, 0x04, rplBasics);
CHECK_OFFSET(RPLBasicsLoadArgs, 0x08, readHeapTracking);
CHECK_OFFSET(RPLBasicsLoadArgs, 0x0C, pathNameLen);
CHECK_OFFSET(RPLBasicsLoadArgs, 0x10, pathName);
CHECK_OFFSET(RPLBasicsLoadArgs, 0x18, fileType);
CHECK_OFFSET(RPLBasicsLoadArgs, 0x1C, chunkBuffer);
CHECK_OFFSET(RPLBasicsLoadArgs, 0x20, chunkBufferSize);
CHECK_OFFSET(RPLBasicsLoadArgs, 0x24, fileOffset);
CHECK_SIZE(RPLBasicsLoadArgs, 0x28);
void
LiSetFatalError(uint32_t baseError,
ios::mcp::MCPLibraryType fileType,
uint32_t unk,
std::string_view function,
uint32_t line)
{
sData->gFatalErr = baseError;
sData->gFatalMsgType = fileType;
// sData->gFatalFunction = function;
sData->gFatalLine = line;
}
void
LiResetFatalError()
{
sData->gFatalErr = 0;
sData->gFatalFunction = nullptr;
sData->gFatalLine = 0;
sData->gFatalMsgType = 0;
}
void
Loader_ReportInfo(const char *fmt, ...)
{
}
void
Loader_ReportError(const char *fmt, ...)
{
}
void
Loader_LogEntry(uint32_t unkR3,
uint32_t unkR4,
uint32_t unkR5,
const char *fmt, ...)
{
}
int32_t
LiCacheLineCorrectAllocEx(virt_ptr<TinyHeap> heap,
uint32_t textSize,
int32_t textAlign,
virt_ptr<void> *outPtr,
uint32_t /*unused*/,
uint32_t *outAllocSize,
uint32_t *outLargestFree,
ios::mcp::MCPLibraryType fileType)
{
auto fromEnd = false;
textSize = align_up(textSize, 128);
if (textAlign < 0) {
textAlign = -textAlign;
fromEnd = true;
}
if (textAlign == 0 && textAlign < 64) {
textAlign = 64;
}
if (fromEnd) {
textAlign = -textAlign;
}
auto tinyHeapError = TinyHeap_Alloc(heap, textSize, textAlign, outPtr);
if (tinyHeapError < TinyHeapError::OK) {
LiSetFatalError(0x187298, fileType, 0, "LiCacheLineCorrectAllocEx", 0x88);
*outLargestFree = TinyHeap_GetLargestFree(heap);
return static_cast<int32_t>(tinyHeapError);
}
std::memset(outPtr->getRawPointer(), 0, textSize);
*outAllocSize = textSize;
return 0;
}
int32_t
LiCacheLineCorrectAllocEx(virt_ptr<TinyHeap> heap,
uint32_t size,
int32_t align,
virt_ptr<virt_ptr<void>> outPtr,
uint32_t unused,
virt_ptr<uint32_t> outAllocSize,
uint32_t *outLargestFree,
ios::mcp::MCPLibraryType fileType)
{
virt_ptr<void> tmpPtr;
uint32_t tmpAllocSize;
auto error = LiCacheLineCorrectAllocEx(heap,
size,
align,
&tmpPtr,
unused,
&tmpAllocSize,
outLargestFree,
fileType);
*outPtr = tmpPtr;
*outAllocSize = tmpAllocSize;
return error;
}
int32_t
LiCacheLineCorrectFreeEx(virt_ptr<TinyHeap> heap,
virt_ptr<void> ptr,
uint32_t size)
{
TinyHeap_Free(heap, ptr);
}
int32_t
LiRefillUpcomingBounceBuffer(virt_ptr<RPLBasics> rplBasics,
int32_t bufferNumber)
{
StackObject<uint32_t> chunkReadSize;
return LiBounceOneChunk(virt_cast<char *>(rplBasics->pathBuffer).getRawPointer(),
rplBasics->fileType,
rplBasics->upid0xAC,
chunkReadSize,
rplBasics->fileOffset,
bufferNumber,
virt_addrof(rplBasics->chunkBuffer));
}
int32_t
LiInitBufferTracking(RPLBasicsLoadArgs *loadArgs)
{
virt_ptr<void> allocPtr;
uint32_t allocSize;
uint32_t largestFree;
auto error = LiCacheLineCorrectAllocEx(loadArgs->readHeapTracking,
align_up(loadArgs->pathNameLen + 1, 4),
4,
&allocPtr,
1,
&allocSize,
&largestFree,
loadArgs->fileType);
if (error != 0) {
return error;
}
auto rplBasics = loadArgs->rplBasics;
rplBasics->pathBuffer = allocPtr;
rplBasics->pathBufferSize = allocSize;
std::strncpy(virt_cast<char *>(rplBasics->pathBuffer).getRawPointer(),
loadArgs->pathName.getRawPointer(),
loadArgs->pathNameLen + 1);
rplBasics->unk0x78 = 1;
rplBasics->unk0x84 = loadArgs->fileOffset;
rplBasics->unk0x90 = loadArgs->chunkBufferSize;
rplBasics->upid0xAC = loadArgs->upid;
rplBasics->firstChunkBuffer = loadArgs->chunkBuffer;
rplBasics->firstChunkBufferSize = loadArgs->chunkBufferSize;
rplBasics->fileOffset = loadArgs->chunkBufferSize;
rplBasics->fileType = loadArgs->fileType;
if (loadArgs->chunkBufferSize == 0x400000) {
error = LiRefillUpcomingBounceBuffer(rplBasics, 2);
} else {
LiInitBuffer(false);
}
if (error != 0 && allocPtr) {
LiCacheLineCorrectFreeEx(loadArgs->readHeapTracking, allocPtr, allocSize);
}
return error;
}
static virt_ptr<rpl::SectionHeader>
getSectionHeader(virt_ptr<RPLBasics> rplBasics,
virt_ptr<rpl::SectionHeader> sectionHeaderBuffer,
uint32_t idx)
{
auto base = virt_cast<virt_addr>(sectionHeaderBuffer);
auto offset = idx * rplBasics->elfHeader.shentsize;
return virt_cast<rpl::SectionHeader *>(base + offset);
}
static virt_ptr<rpl::SectionHeader>
getSectionHeader(virt_ptr<RPLBasics> rplBasics,
uint32_t idx)
{
return getSectionHeader(rplBasics,
virt_cast<rpl::SectionHeader *>(rplBasics->sectionHeaderBuffer),
idx);
}
int32_t
LiCheckFileBounds(virt_ptr<RPLBasics> rplBasics)
{
auto shBase = virt_cast<virt_addr>(rplBasics->sectionHeaderBuffer);
auto dataMin = -1;
auto dataMax = 0;
auto readMin = -1;
auto readMax = 0;
auto textMin = -1;
auto textMax = 0;
auto tempMin = -1;
auto tempMax = 0;
for (auto i = 0u; i < rplBasics->elfHeader.shnum; ++i) {
auto sectionHeader = virt_cast<rpl::SectionHeader *>(shBase + i * rplBasics->elfHeader.shentsize);
if (sectionHeader->size == 0 ||
sectionHeader->type == rpl::SHT_RPL_FILEINFO ||
sectionHeader->type == rpl::SHT_RPL_CRCS ||
sectionHeader->type == rpl::SHT_NOBITS ||
sectionHeader->type == rpl::SHT_RPL_IMPORTS) {
continue;
}
if ((sectionHeader->flags & rpl::SHF_EXECINSTR) &&
sectionHeader->type != rpl::SHT_RPL_EXPORTS) {
textMin = std::min(textMin, static_cast<int32_t>(sectionHeader->offset));
textMax = std::min(textMax, static_cast<int32_t>(sectionHeader->offset + sectionHeader->size));
} else {
if (sectionHeader->flags & rpl::SHF_ALLOC) {
if (sectionHeader->flags & rpl::SHF_WRITE) {
dataMin = std::min(dataMin, static_cast<int32_t>(sectionHeader->offset));
dataMax = std::min(dataMax, static_cast<int32_t>(sectionHeader->offset + sectionHeader->size));
} else {
readMin = std::min(readMin, static_cast<int32_t>(sectionHeader->offset));
readMax = std::min(readMax, static_cast<int32_t>(sectionHeader->offset + sectionHeader->size));
}
} else {
tempMin = std::min(tempMin, static_cast<int32_t>(sectionHeader->offset));
tempMax = std::min(tempMax, static_cast<int32_t>(sectionHeader->offset + sectionHeader->size));
}
}
}
if (dataMin == -1) {
dataMax = (rplBasics->elfHeader.shnum * rplBasics->elfHeader.shentsize) + rplBasics->elfHeader.shoff;
}
if (readMin == -1) {
readMax = dataMax;
}
if (textMin == -1) {
textMax = readMax;
}
if (tempMin == -1) {
tempMax = textMax;
}
if (dataMin < rplBasics->elfHeader.shoff) {
Loader_ReportError("*** SecHrs, FileInfo, or CRCs in bad spot in file. Return %d.\n", -470026);
goto error;
}
// Data
if (dataMin > dataMax) {
Loader_ReportError("*** DataMin > DataMax. break.\n");
goto error;
}
if (dataMin > readMin) {
Loader_ReportError("*** DataMin > ReadMin. break.\n");
goto error;
}
if (dataMax > readMin) {
Loader_ReportError("*** DataMax > ReadMin. break.\n");
goto error;
}
// Read
if (readMin > readMax) {
Loader_ReportError("*** ReadMin > ReadMax. break.\n");
goto error;
}
if (readMin > textMin) {
Loader_ReportError("*** ReadMin > TextMin. break.\n");
goto error;
}
if (readMax > textMin) {
Loader_ReportError("*** ReadMax > TextMin. break.\n");
goto error;
}
// Text
if (textMin > textMax) {
Loader_ReportError("*** TextMin > TextMax. break.\n");
goto error;
}
if (textMin > tempMin) {
Loader_ReportError("*** TextMin > TempMin. break.\n");
goto error;
}
if (textMax > tempMin) {
Loader_ReportError("*** TextMax > TempMin. break.\n");
goto error;
}
// Temp
if (tempMin > tempMax) {
Loader_ReportError("*** TempMin > TempMax. break.\n");
goto error;
}
return 0;
error:
LiSetFatalError(0x18729B, rplBasics->fileType, 1, "LiCheckFileBounds", 0x247);
return -470026;
}
uint32_t
LiCalcCRC32(uint32_t crc,
const virt_ptr<void> data,
uint32_t size)
{
LiCheckAndHandleInterrupts();
if (!data || !size) {
return crc;
}
crc = crc32(crc, reinterpret_cast<Bytef *>(data.getRawPointer()), size);
LiCheckAndHandleInterrupts();
return crc;
}
int32_t
LiLoadRPLBasics(virt_ptr<char> moduleName,
uint32_t moduleNameLen,
virt_ptr<void> chunkBuffer,
virt_ptr<TinyHeap> codeHeapTracking,
virt_ptr<TinyHeap> dataHeapTracking,
uint32_t r8,
uint32_t r9,
virt_ptr<RPLBasics> *outRplBasics,
RPLBasicsLoadArgs *loadArgs,
uint32_t arg_C)
{
struct LoadAttemptErrorData
{
int32_t error;
ios::mcp::MCPLibraryType fileType;
uint32_t fatalErr;
virt_ptr<char> fatalFunction;
uint32_t fatalLine;
uint32_t fatalMsgType;
};
LoadAttemptErrorData loadAttemptErrors[3];
int32_t loadAttempt = 0;
uint32_t chunkReadSize;
int32_t error;
while (true) {
error = LiWaitOneChunk(&chunkReadSize, loadArgs->pathName.getRawPointer(), loadArgs->fileType);
if (error == 0) {
break;
}
if (loadAttempt < 2) {
if (sData->gFatalErr) {
auto &attemptErrors = loadAttemptErrors[loadAttempt];
attemptErrors.error = error;
attemptErrors.fileType = loadArgs->fileType;
attemptErrors.fatalErr = sData->gFatalErr;
attemptErrors.fatalFunction = sData->gFatalFunction;
attemptErrors.fatalLine = sData->gFatalLine;
attemptErrors.fatalMsgType = sData->gFatalMsgType;
LiResetFatalError();
}
if (loadAttempt == 0) {
if (loadArgs->fileType == ios::mcp::MCPLibraryType::Executable) {
loadArgs->fileType = ios::mcp::MCPLibraryType::CafeOS;
}
} else {
loadArgs->fileType = ios::mcp::MCPLibraryType::Unknown3;
}
LiInitBuffer(false);
chunkReadSize = 0;
loadArgs->chunkBufferSize = 0;
error = LiBounceOneChunk(loadArgs->pathName.getRawPointer(),
loadArgs->fileType,
loadArgs->upid,
virt_addrof(loadArgs->chunkBufferSize),
loadArgs->fileOffset,
1,
virt_addrof(loadArgs->chunkBuffer));
}
if (error != 0) {
Loader_ReportError("***Loader failure %d first time, %d second time and %d third time. loading \"%s\".\n",
loadAttemptErrors[0].error,
loadAttemptErrors[1].error,
error,
loadArgs->pathName.getRawPointer());
return error;
}
}
LiCheckAndHandleInterrupts();
// Load and validate the ELF header
uint32_t filePhStride;
uint32_t fileShStride;
virt_ptr<rpl::Header> fileElfHeader;
virt_ptr<rpl::SectionHeader> fileSectionHeaders;
error = ELFFILE_ValidateAndPrepareMinELF(chunkBuffer,
chunkReadSize,
&fileElfHeader,
&fileSectionHeaders,
&fileShStride,
&filePhStride);
if (error) {
Loader_ReportError("*** Failed ELF file checks (err=0x%08X)\n", error);
LiSetFatalError(0x18729B, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x325);
return error;
}
// Check that this ELF looks like a Wii U Cafe RPL
if (fileElfHeader->fileClass != rpl::ELFCLASS32 ||
fileElfHeader->encoding != rpl::ELFDATA2MSB ||
fileElfHeader->abi != rpl::EABI_CAFE ||
fileElfHeader->elfVersion > rpl::EV_CURRENT ||
fileElfHeader->machine != rpl::EM_PPC ||
fileElfHeader->version != 1 ||
fileElfHeader->shnum < 2) {
return -470025;
}
// Initialise temporary RPL basics
cafe::StackObject<RPLBasics> tmpRplBasics;
virt_ptr<RPLBasics> rplBasics;
rplBasics = tmpRplBasics;
std::memset(rplBasics.getRawPointer(), 0, sizeof(RPLBasics));
if (r9 == 0) {
rplBasics->upid0x00 = sData->upid;
}
std::memcpy(virt_addrof(rplBasics->elfHeader).getRawPointer(),
fileElfHeader.getRawPointer(),
sizeof(rpl::Header));
// Check some offsets are valid
if (!rplBasics->elfHeader.shentsize) {
rplBasics->elfHeader.shentsize = sizeof(rpl::SectionHeader);
}
if (rplBasics->elfHeader.shoff >= chunkReadSize ||
((rplBasics->elfHeader.shnum - 1) * rplBasics->elfHeader.shentsize) + rplBasics->elfHeader.shoff >= chunkReadSize) {
LiSetFatalError(0x18729B, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x33F);
return -470077;
}
auto shRplCrcs = getSectionHeader(rplBasics, fileSectionHeaders, rplBasics->elfHeader.shnum - 2);
if (shRplCrcs->offset >= chunkReadSize ||
shRplCrcs->offset + shRplCrcs->size >= chunkReadSize) {
LiSetFatalError(0x18729B, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x348);
return -470077;
}
auto shRplFileInfo = getSectionHeader(rplBasics, fileSectionHeaders, rplBasics->elfHeader.shnum - 1);
if (shRplFileInfo->offset + shRplFileInfo->size >= chunkReadSize) {
LiSetFatalError(0x18729B, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x351);
return -470078;
}
// Check RPL file info
if (shRplFileInfo->type != rpl::SHT_RPL_FILEINFO ||
shRplFileInfo->flags & rpl::SHF_DEFLATED) {
Loader_ReportError("***shnum-1 section type = 0x%08X, flags=0x%08X\n", shRplFileInfo->type, shRplFileInfo->flags);
LiSetFatalError(0x18729B, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x35A);
return -470082;
}
auto rplFileInfo = virt_cast<rpl::RPLFileInfo_v4_2 *>(virt_cast<virt_addr>(fileElfHeader) + shRplFileInfo->offset);
if (rplFileInfo->version < rpl::RPLFileInfo_v4_2::Version) {
Loader_ReportError("*** COS requires that %.*s be built with at least SDK %d.%02d.%d, it was built with an older SDK\n",
moduleName, moduleNameLen,
2, 5, 0);
LiSetFatalError(0x187298, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x38B);
return -470062;
}
if (rplFileInfo->minVersion < 0x5014 || rplFileInfo->minVersion > 0x5335) {
auto major = rplFileInfo->minVersion / 10000;
auto minor = (rplFileInfo->minVersion % 10000) / 100;
auto patch = rplFileInfo->minVersion % 100;
Loader_ReportError("*** COS requires that %.*s be built with at least SDK %d.%02d.%d, it was built with SDK %d.%02d.%d\n",
moduleName, moduleNameLen,
2, 5, 0,
major, minor, patch);
LiSetFatalError(0x187298, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x38B);
return -470062;
}
uint32_t largestFree;
if (rplFileInfo->textSize) {
error = LiCacheLineCorrectAllocEx(codeHeapTracking,
rplFileInfo->textSize,
rplFileInfo->textAlign,
virt_addrof(rplBasics->textBuffer),
0,
virt_addrof(rplBasics->textBufferSize),
&largestFree,
loadArgs->fileType);
if (error != 0) {
Loader_ReportError("***Could not allocate uncompressed text (%d) in %s heap \"%.*s\"; (needed %d, available %d).\n",
rplFileInfo->textSize,
r9 ? "shared code" : "code",
moduleName, moduleNameLen,
rplFileInfo->textSize,
largestFree);
goto lblError;
}
}
virt_ptr<void> allocPtr;
uint32_t rplBasicAllocSize;
error = LiCacheLineCorrectAllocEx(dataHeapTracking,
sizeof(RPLBasics),
4,
&allocPtr,
0,
&rplBasicAllocSize,
&largestFree,
loadArgs->fileType);
if (error != 0) {
Loader_ReportError("*** Failed %s alloc (err=0x%08X); (needed %d, available %d)\n",
r9 ? "readheap" : "workarea",
error,
sizeof(RPLBasics),
largestFree);
goto lblError;
}
rplBasics = virt_cast<RPLBasics *>(allocPtr);
std::memcpy(rplBasics.getRawPointer(), &tmpRplBasics, sizeof(RPLBasics));
rplBasics->selfBufferSize = rplBasicAllocSize;
loadArgs->rplBasics = rplBasics;
error = LiInitBufferTracking(loadArgs);
if (error != 0) {
goto lblError;
}
uint32_t largestFree;
error = LiCacheLineCorrectAllocEx(dataHeapTracking,
rplBasics->elfHeader.shnum * 8,
4,
virt_addrof(rplBasics->sectionAddressBuffer),
1,
virt_addrof(rplBasics->sectionAddressBufferSize),
&largestFree,
rplBasics->fileType);
if (error != 0) {
Loader_ReportError("***Allocate Error %d, Failed to allocate %d bytes for section addresses; (needed %d, available %d).\n",
error,
rplBasics->elfHeader.shnum * 8,
rplBasics->sectionAddressBufferSize,
largestFree);
goto lblError;
}
auto sectionAddresses = virt_cast<virt_addr *>(rplBasics->sectionAddressBuffer);
error = LiCacheLineCorrectAllocEx(dataHeapTracking,
rplBasics->elfHeader.shnum * rplBasics->elfHeader.shentsize,
4,
virt_addrof(rplBasics->sectionHeaderBuffer),
1,
virt_addrof(rplBasics->sectionHeaderBufferSize),
&largestFree,
rplBasics->fileType);
if (error != 0) {
Loader_ReportError("*** Could not allocate space for section headers in %s heap; (needed %d, available %d)\n",
r9 ? "shared readonly" : "readonly",
rplBasics->sectionHeaderBufferSize,
largestFree);
goto lblError;
}
std::memcpy(rplBasics->sectionHeaderBuffer.getRawPointer(),
virt_cast<void *>(virt_cast<virt_addr>(fileElfHeader) + rplBasics->elfHeader.shoff).getRawPointer(),
rplBasics->elfHeader.shnum * rplBasics->elfHeader.shentsize);
if (r8) {
error = LiCacheLineCorrectAllocEx(sData->codeAreaTracking,
align_up(moduleNameLen, 4),
4,
virt_addrof(rplBasics->moduleNameBuffer),
1,
virt_addrof(rplBasics->moduleNameBufferSize),
&largestFree,
rplBasics->fileType);
if (error != 0) {
Loader_ReportError("*** Could not allocate space for module name; (needed %d, available %d)\n",
rplBasics->moduleNameBufferSize,
largestFree);
goto lblError;
}
std::strncpy(virt_cast<char *>(rplBasics->moduleNameBuffer).getRawPointer(),
moduleName.getRawPointer(),
rplBasics->moduleNameBufferSize);
moduleNameLen = rplBasics->moduleNameBufferSize;
} else {
rplBasics->moduleNameBuffer = moduleName;
rplBasics->moduleNameBufferSize = moduleNameLen;
}
rplBasics->moduleNameLen = moduleNameLen;
// Load SHT_RPL_CRCS
shRplCrcs = getSectionHeader(rplBasics, rplBasics->elfHeader.shnum - 2);
if (shRplCrcs->type != rpl::SHT_RPL_CRCS ||
(shRplCrcs->flags & rpl::SHF_DEFLATED)) {
Loader_ReportError("***shnum-2 section type = 0x%08X, flags=0x%08X\n",
shRplCrcs->type, shRplCrcs->flags);
LiSetFatalError(0x18729B, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x403);
error = -470081;
goto lblError;
}
error = LiCacheLineCorrectAllocEx(sData->codeAreaTracking,
shRplCrcs->size,
-shRplCrcs->addralign,
virt_addrof(rplBasics->rplCrcBuffer),
1,
virt_addrof(rplBasics->rplCrcBufferSize),
&largestFree,
rplBasics->fileType);
if (error != 0) {
Loader_ReportError("*** Could not allocate space for CRCs; (needed %d, available %d)\n",
rplBasics->rplCrcBufferSize, largestFree);
goto lblError;
}
auto sectionCrcs = virt_cast<uint32_t *>(rplBasics->rplCrcBuffer);
std::memcpy(rplBasics->rplCrcBuffer.getRawPointer(),
virt_cast<void *>(virt_cast<virt_addr>(fileElfHeader) + shRplCrcs->offset).getRawPointer(),
shRplCrcs->size);
sectionAddresses[rplBasics->elfHeader.shnum - 2] = virt_cast<virt_addr>(rplBasics->rplCrcBuffer);
// Load SHT_RPL_FILEINFO
shRplFileInfo = getSectionHeader(rplBasics, rplBasics->elfHeader.shnum - 1);
if (shRplFileInfo->type != rpl::SHT_RPL_FILEINFO ||
(shRplFileInfo->flags & rpl::SHF_DEFLATED)) {
Loader_ReportError("***shnum-1 section type = 0x%08X, flags=0x%08X\n",
shRplFileInfo->type, shRplFileInfo->flags);
LiSetFatalError(0x18729B, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x41A);
error = -470082;
goto lblError;
}
rplBasics->rplFileInfoSize = shRplFileInfo->size;
error = LiCacheLineCorrectAllocEx(dataHeapTracking,
shRplFileInfo->size,
shRplFileInfo->addralign,
virt_addrof(rplBasics->rplFileInfoBuffer),
1,
virt_addrof(rplBasics->rplFileInfoBufferSize),
&largestFree,
rplBasics->fileType);
if (error != 0) {
Loader_ReportError("*** Could not allocate space for file info; (needed %d, available %d)\n",
rplBasics->rplFileInfoBufferSize, largestFree);
goto lblError;
}
if ((sData->gProcFlags >> 9) & 7) {
Loader_ReportInfo("RPL_LAYOUT:%s,FILE,start,=\"0%08x\"\nRPL_LAYOUT:%s,FILE,end,=\"0%08x\"\n",
rplBasics->moduleNameBuffer,
rplBasics->rplFileInfoBuffer,
rplBasics->moduleNameBuffer,
virt_cast<virt_addr>(rplBasics->rplFileInfoBuffer) + rplBasics->rplFileInfoBufferSize);
}
auto rplFileInfo = virt_cast<rpl::RPLFileInfo_v4_2 *>(rplBasics->rplFileInfoBuffer);
std::memcpy(rplBasics->rplFileInfoBuffer.getRawPointer(),
virt_cast<void *>(virt_cast<virt_addr>(fileElfHeader) + shRplFileInfo->offset).getRawPointer(),
shRplFileInfo->size);
sectionAddresses[rplBasics->elfHeader.shnum - 1] = virt_cast<virt_addr>(rplBasics->rplFileInfoBuffer);
auto rplFileInfoCrc = LiCalcCRC32(0, rplBasics->rplFileInfoBuffer, shRplFileInfo->size);
if (rplFileInfoCrc != sectionCrcs[rplBasics->elfHeader.shnum - 1]) {
Loader_ReportError("***FileInfo CRC failed check.\n");
LiSetFatalError(0x18729B, loadArgs->fileType, 1, "LiLoadRPLBasics", 0x433);
error = -470083;
goto lblError;
}
rplFileInfo->tlsModuleIndex = -1;
error = LiCheckFileBounds(rplBasics);
if (error == 0) {
rplBasics->firstChunkElfHeader = fileElfHeader;
*outRplBasics = rplBasics;
return error;
}
lblError:
if (rplBasics) {
if (rplBasics->rplFileInfoBuffer) {
LiCacheLineCorrectFreeEx(dataHeapTracking,
rplBasics->rplFileInfoBuffer,
rplBasics->rplFileInfoBufferSize);
}
if (rplBasics->rplCrcBuffer) {
LiCacheLineCorrectFreeEx(sData->codeAreaTracking,
rplBasics->rplCrcBuffer,
rplBasics->rplCrcBufferSize);
}
if (rplBasics->moduleNameBuffer) {
LiCacheLineCorrectFreeEx(sData->codeAreaTracking,
rplBasics->moduleNameBuffer,
rplBasics->moduleNameBufferSize);
}
if (rplBasics->sectionHeaderBuffer) {
LiCacheLineCorrectFreeEx(dataHeapTracking,
rplBasics->sectionHeaderBuffer,
rplBasics->sectionHeaderBufferSize);
}
if (rplBasics->sectionAddressBuffer) {
LiCacheLineCorrectFreeEx(dataHeapTracking,
rplBasics->sectionAddressBuffer,
rplBasics->sectionAddressBufferSize);
}
if (rplBasics->pathBuffer) {
LiCacheLineCorrectFreeEx(dataHeapTracking,
rplBasics->pathBuffer,
rplBasics->pathBufferSize);
}
if (rplBasics->textBuffer) {
LiCacheLineCorrectFreeEx(codeHeapTracking,
rplBasics->textBuffer,
rplBasics->textBufferSize);
}
LiCacheLineCorrectFreeEx(codeHeapTracking,
rplBasics,
rplBasics->selfBufferSize);
}
return error;
}
struct SegmentBounds
{
int32_t min = -1;
int32_t max = 0;
uint32_t unk_0x08 = 0;
const char *name = nullptr;
};
struct RplSegmentBounds
{
SegmentBounds data;
SegmentBounds load;
SegmentBounds text;
SegmentBounds temp;
};
int32_t
sLiSetupOneAllocSection(int32_t unk_r3,
virt_ptr<RPLBasics> rplBasics,
uint32_t shndx,
virt_ptr<rpl::SectionHeader> sectionHeader,
uint32_t unk_r7,
RplSegmentBounds *bounds,
uint32_t unk_r9,
uint32_t rplDataAlign,
uint32_t unk_arg8)
{
LiCheckAndHandleInterrupts();
}
int32_t
LiSetupOneRPL(int32_t unk_r3,
virt_ptr<RPLBasics> rplBasics,
virt_ptr<TinyHeap> codeHeapTracking,
virt_ptr<TinyHeap> dataHeapTracking)
{
auto shBase = virt_cast<virt_addr>(rplBasics->sectionHeaderBuffer);
// Initialise stack 4x -1, 0, 0, 0
// r25+0x0C "DATA"
// r25+0x1C "LOADERINFO"
// r25+0x2C "TEXT"
// r25+0x3C "TEMP"
RplSegmentBounds bounds;
bounds.data.name = "DATA";
bounds.load.name = "LOADERINFO";
bounds.text.name = "TEXT";
bounds.temp.name = "TEMP";
for (auto i = 0u; i < rplBasics->elfHeader.shnum; ++i) {
auto sectionHeader = virt_cast<rpl::SectionHeader *>(shBase + i * rplBasics->elfHeader.shentsize);
if (sectionHeader->size == 0 ||
sectionHeader->type == rpl::SHT_RPL_FILEINFO ||
sectionHeader->type == rpl::SHT_RPL_CRCS ||
sectionHeader->type == rpl::SHT_RPL_IMPORTS) {
continue;
}
if (sectionHeader->flags & rpl::SHF_ALLOC) {
if ((sectionHeader->flags & rpl::SHF_EXECINSTR) &&
sectionHeader->type != rpl::SHT_RPL_EXPORTS) {
bounds.text.min = std::min(bounds.text.min, static_cast<int32_t>(sectionHeader->addr));
} else {
if (sectionHeader->flags & rpl::SHF_WRITE) {
bounds.data.min = std::min(bounds.data.min, static_cast<int32_t>(sectionHeader->addr));
} else {
bounds.load.min = std::min(bounds.load.min, static_cast<int32_t>(sectionHeader->addr));
}
}
} else {
bounds.temp.min = std::min(bounds.temp.min, static_cast<int32_t>(sectionHeader->addr));
bounds.temp.max = std::max(bounds.temp.max, static_cast<int32_t>(sectionHeader->addr + sectionHeader->size));
}
}
if (bounds.data.min == -1) {
bounds.data.min = 0;
}
if (bounds.load.min == -1) {
bounds.load.min = 0;
}
if (bounds.text.min == -1) {
bounds.text.min = 0;
}
if (bounds.temp.min == -1) {
bounds.temp.min = 0;
}
auto rplFileInfo = virt_cast<rpl::RPLFileInfo_v4_2 *>(rplBasics->rplFileInfoBuffer);
bounds.text.max = (bounds.text.min + rplFileInfo->textSize) - rplFileInfo->trampAdjust;
bounds.data.max = bounds.data.min + rplFileInfo->dataSize;
bounds.load.max = (bounds.load.min + rplFileInfo->loadSize) - rplFileInfo->fileInfoPad;
auto textSize = bounds.text.max - bounds.text.min;
auto dataSize = bounds.data.max - bounds.data.min;
auto loadSize = bounds.load.max - bounds.load.min;
auto tempSize = bounds.temp.max - bounds.temp.min;
// r26 = 0xFFF8D3B5
if (rplFileInfo->trampAdjust >= textSize ||
rplFileInfo->textSize - rplFileInfo->trampAdjust < textSize ||
rplFileInfo->dataSize < dataSize ||
rplFileInfo->loadSize - rplFileInfo->fileInfoPad < loadSize ||
rplFileInfo->tempSize < tempSize) {
Loader_ReportError("***Bounds check failure.\n");
Loader_ReportError("b%d: %08X %08x\n", 0, bounds.data.min, bounds.data.max);
Loader_ReportError("b%d: %08X %08x\n", 1, bounds.load.min, bounds.load.max);
Loader_ReportError("b%d: %08X %08x\n", 2, bounds.text.min, bounds.text.max);
Loader_ReportError("b%d: %08X %08x\n", 3, bounds.temp.min, bounds.temp.max);
Loader_ReportError("TrampAdj = %08X\n", rplFileInfo->trampAdjust);
Loader_ReportError("Text = %08X\n", rplFileInfo->textSize);
Loader_ReportError("Data = %08X\n", rplFileInfo->dataSize);
Loader_ReportError("Read = %08X\n", rplFileInfo->loadSize - rplFileInfo->fileInfoPad);
Loader_ReportError("Temp = %08X\n", rplFileInfo->tempSize);
LiSetFatalError(0x18729B, rplBasics->fileType, 1, "LiSetupOneRPL", 0x715);
// TODO: Free and shit
return -470091 + 0x31;
}
auto unk_var_84 = 0;
auto sectionAddresses = virt_cast<virt_addr *>(rplBasics->sectionAddressBuffer);
if (rplBasics->unk0xBC) {
for (auto i = 0u; i < rplBasics->elfHeader.shnum; ++i) {
LiCheckAndHandleInterrupts();
auto sectionHeader = virt_cast<rpl::SectionHeader *>(shBase + i * rplBasics->elfHeader.shentsize);
if (sectionHeader->size == 0 ||
sectionAddresses[i] ||
(sectionHeader->flags & rpl::SHF_WRITE) ||
(sectionHeader->flags & rpl::SHF_ALLOC)) {
continue;
}
sLiSetupOneAllocSection(unk_r3, rplBasics, i, sectionHeader, 1, &bounds, unk_var_84, rplFileInfo->dataAlign, 0);
}
}
}
int32_t
sLoadOneShared(std::string_view pathName)
{
StackObject<uint32_t> chunkSize;
StackObject<virt_ptr<void>> chunkBuffer;
StackArray<char, 32> pathNameBuffer;
virt_ptr<RPLBasics> rplBasics;
RPLBasicsLoadArgs rplBasicLoadArgs;
LiInitBuffer(false);
auto error = LiBounceOneChunk(pathName,
ios::mcp::MCPLibraryType::CafeOS,
kernel::UniqueProcessId::Kernel,
chunkSize,
0,
1,
chunkBuffer);
if (error != 0) {
return error;
}
auto moduleName = virt_ptr<char> { pathNameBuffer };
auto moduleNameLen = std::min<uint32_t>(pathName.size(), 0x1Fu);
std::memcpy(pathNameBuffer.getRawPointer(),
pathName.data(),
moduleNameLen);
moduleName[moduleNameLen] = 0;
LiResolveModuleName(&moduleName, &moduleNameLen);
rplBasicLoadArgs.fileOffset = 0;
rplBasicLoadArgs.pathNameLen = pathName.size();
rplBasicLoadArgs.pathName = pathNameBuffer;
rplBasicLoadArgs.chunkBuffer = chunkBuffer;
rplBasicLoadArgs.chunkBufferSize = *chunkSize;
rplBasicLoadArgs.readHeapTracking = sData->gpSharedReadHeapTracking;
rplBasicLoadArgs.upid = kernel::UniqueProcessId::Kernel;
rplBasicLoadArgs.fileType = ios::mcp::MCPLibraryType::CafeOS;
error = LiLoadRPLBasics(moduleName,
moduleNameLen,
chunkBuffer,
sData->gpSharedCodeHeapTracking,
sData->gpSharedReadHeapTracking,
0,
1,
&rplBasics,
&rplBasicLoadArgs,
0);
if (error != 0) {
return error;
}
rplBasics->unk0x70 |= 0x20000004;
auto rplFileInfo = virt_cast<rpl::RPLFileInfo_v4_2 *>(rplBasics->rplFileInfoBuffer);
if (rplFileInfo->dataSize) {
rplBasics->unk0xBC = align_up(sData->gpLoaderShared->unk0x04, rplFileInfo->dataAlign);
sData->gpLoaderShared->unk0x04 = align_up(rplBasics->unk0xBC, 64);
}
if (rplFileInfo->loadSize != rplFileInfo->fileInfoPad) {
auto tinyHeapError = TinyHeap_Alloc(sData->gpSharedReadHeapTracking,
rplFileInfo->loadSize - rplFileInfo->fileInfoPad,
-rplFileInfo->loadAlign,
virt_addrof(rplBasics->sharedLibraryLoadBuffer));
if (tinyHeapError != TinyHeapError::OK) {
Loader_ReportError("Could not allocate read-only space for shared library \"%s\".\n",
rplBasics->moduleNameBuffer);
return static_cast<int32_t>(tinyHeapError);
}
}
Loader_LogEntry(2, 0, 0, "sLoadOneShared LiSetupOneRPL start.");
LiSetupOneRPL(-1, rplBasics, sData->gpSharedCodeHeapTracking, sData->gpSharedReadHeapTracking);
}
int32_t
LiInitSharedForAll()
{
virt_ptr<void> outAllocPtr;
auto tinyHeapError = TinyHeap_Alloc(sData->gpSharedCodeHeapTracking, 0x430, -4, &outAllocPtr);
if (tinyHeapError < TinyHeapError::OK) {
return static_cast<int32_t>(tinyHeapError);
}
sData->sgpTrackComp = virt_cast<TinyHeap *>(outAllocPtr);
tinyHeapError = TinyHeap_Setup(sData->sgpTrackComp, 0x430, virt_cast<void *>(DataAreaBase), 0x60000000); // 1536 mb??
if (tinyHeapError < TinyHeapError::OK) {
TinyHeap_Free(sData->gpSharedCodeHeapTracking, virt_cast<void *>(sData->sgpTrackComp));
return static_cast<int32_t>(tinyHeapError);
}
sLoadOneShared("coreinit.rpl");
}
int32_t
LiInitSharedForProcess(virt_ptr<RPL_STARTINFO> rplStartInfo)
{
if (sData->upid != kernel::UniqueProcessId::Root) {
if (sData->gProcFlags & 2) {
// Shared libraries disabled in process
rplStartInfo->unk_0x04 = 0x10000000;
return 0;
}
}
if ((sData->gProcFlags & 1) == 0) {
LiInitSharedForAll();
}
}
}
// LOADER_Init
int32_t
init(kernel::UniqueProcessId upid,
uint32_t num_codearea_heap_blocks,
uint32_t max_codesize,
uint32_t r6, // maybe max_size ?
virt_ptr<uint32_t> r7,
virt_ptr<uint32_t> r8,
virt_ptr<RPL_STARTINFO> rplStartInfo)
{
sData->upid = upid;
sData->codeAreaNumBlocks = num_codearea_heap_blocks;
if (max_codesize > MaxCodeSize) {
max_codesize = MaxCodeSize;
}
sData->maxCodeSize = max_codesize;
sData->unk_0xEFE01018 = r6;
rplStartInfo->unk_0x24 = DataAreaBase + r6;
sData->codeAreaTrackingSize = (num_codearea_heap_blocks * TinyHeapBlockSize) + TinyHeapHeaderSize;
sData->codeAreaSize = sData->maxCodeSize - align_up(sData->codeAreaTrackingSize, 64);
sData->codeAreaTracking = virt_cast<TinyHeap *>(DataAreaBase - align_up(sData->codeAreaTrackingSize, 64));
sData->dataAreaAllocations = 0u;
sData->dataAreaSize = 0u;
std::string_view moduleName;
bool isRoot = false;
if (upid == kernel::UniqueProcessId::Root) {
moduleName = "root.rpx";
isRoot = true;
} else {
moduleName = virt_addrof(sData->moduleName).getRawPointer();
isRoot = false;
}
auto tinyHeapError = TinyHeap_Setup(sData->codeAreaTracking,
sData->codeAreaTrackingSize,
virt_cast<void *>(virt_cast<virt_addr>(sData->codeAreaTracking) - sData->codeAreaSize),
sData->codeAreaSize);
if (tinyHeapError < TinyHeapError::OK) {
// LiSetFatalError(r27, r19, 1, "LOADER_Init", 0xCC);
// Loader_ReportError("*** Process code heap setup failed.\n");
// gUPID = 0
}
auto coreId = cpu::this_core::id();
if (!sData->gIPCLInitialized[coreId]) {
internal::IPCLDriver_Init();
internal::IPCLDriver_Open();
internal::LiInitIopInterface();
sData->gIPCLInitialized[coreId] = TRUE;
}
internal::LiInitSharedForProcess(rplStartInfo);
// ... stuff ..
internal::LiInitBuffer(true);
return 0;
}
// __LoaderStart
void
start(void *entryParams, void *startupParams)
{
if (entryParams) {
return entry(entryParams);
}
// Start up
sData->gProcFlags = gIpcData->procFlags;
auto loaderSharedAddr = static_cast<virt_addr>(0xFA000000);
sData->gpLoaderShared = virt_cast<LoaderShared *>(loaderSharedAddr);
sData->gpSharedCodeHeapTracking = virt_cast<TinyHeap *>(loaderSharedAddr + sizeof(LoaderShared));
sData->gpSharedReadHeapTracking = virt_cast<TinyHeap *>(loaderSharedAddr + sizeof(LoaderShared) + SharedCodeTrackingSize);
if (sData->gProcFlags & 1) {
TinyHeap_Setup(sData->gpSharedCodeHeapTracking,
SharedCodeTrackingSize,
virt_cast<void *>(static_cast<virt_addr>(0x01000000)),
0x007E0000);
TinyHeap_Setup(sData->gpSharedReadHeapTracking,
SharedReadTrackingSize,
virt_cast<void *>(static_cast<virt_addr>(0xF8000000)),
0x03000000);
// Shared code for loader
TinyHeap_AllocAt(sData->gpSharedCodeHeapTracking,
virt_cast<void *>(static_cast<virt_addr>(0x01000000)),
align_up(0x1C758, 1024));
// Reserve space for heap tracking
TinyHeap_AllocAt(sData->gpSharedReadHeapTracking,
virt_cast<void *>(static_cast<virt_addr>(0xF8000000)),
0x02000000);
TinyHeap_AllocAt(sData->gpSharedReadHeapTracking,
virt_cast<void *>(loaderSharedAddr),
0x18);
TinyHeap_AllocAt(sData->gpSharedReadHeapTracking,
virt_cast<void *>(loaderSharedAddr + sizeof(LoaderShared)),
0x1860);
// Clear gpLoaderShared
std::memset(sData->gpLoaderShared.getRawPointer(), 0, sizeof(LoaderShared));
}
auto result = init(gIpcData->upid,
gIpcData->num_codearea_heap_blocks,
gIpcData->max_codesize,
gIpcData->unk_0xEFE0A418,
virt_addrof(gIpcData->unk_0xEFE0A420),
virt_addrof(gIpcData->unk_0xEFE0A424),
virt_addrof(gIpcData->rplStartInfo));
if (result) {
}
}
} // namespace cafe::loader
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment