homebrew_launcher/sd_loader/src/entry.c

662 lines
25 KiB
C

#include <gctypes.h>
#include "elf_abi.h"
#include "../../src/common/common.h"
#include "../../src/common/os_defs.h"
#include "fs_defs.h"
#include "kernel_defs.h"
#include "loader_defs.h"
#define EXPORT_DECL(res, func, ...) res (* func)(__VA_ARGS__);
#define OS_FIND_EXPORT(handle, funcName, func) OSDynLoad_FindExport(handle, 0, funcName, &func)
typedef struct _private_data_t
{
EXPORT_DECL(void *, MEMAllocFromDefaultHeapEx,int size, int align);
EXPORT_DECL(void, MEMFreeToDefaultHeap,void *ptr);
EXPORT_DECL(void*, memcpy, void *p1, const void *p2, unsigned int s);
EXPORT_DECL(void*, memset, void *p1, int val, unsigned int s);
EXPORT_DECL(void, OSFatal, const char* msg);
EXPORT_DECL(unsigned int, OSEffectiveToPhysical, const void*);
EXPORT_DECL(void, exit, int);
EXPORT_DECL(int, FSInit, void);
EXPORT_DECL(int, FSAddClientEx, void *pClient, int unk_zero_param, int errHandling);
EXPORT_DECL(int, FSDelClient, void *pClient);
EXPORT_DECL(void, FSInitCmdBlock, void *pCmd);
EXPORT_DECL(int, FSGetMountSource, void *pClient, void *pCmd, int type, void *source, int errHandling);
EXPORT_DECL(int, FSMount, void *pClient, void *pCmd, void *source, const char *target, uint32_t bytes, int errHandling);
EXPORT_DECL(int, FSUnmount, void *pClient, void *pCmd, const char *target, int errHandling);
EXPORT_DECL(int, FSOpenFile, void *pClient, void *pCmd, const char *path, const char *mode, int *fd, int errHandling);
EXPORT_DECL(int, FSGetStatFile, void *pClient, void *pCmd, int fd, void *buffer, int error);
EXPORT_DECL(int, FSReadFile, void *pClient, void *pCmd, void *buffer, int size, int count, int fd, int flag, int errHandling);
EXPORT_DECL(int, FSCloseFile, void *pClient, void *pCmd, int fd, int errHandling);
EXPORT_DECL(int, SYSRelaunchTitle, int argc, char** argv);
} private_data_t;
static void (*DCFlushRange)(void *addr, unsigned int size);
static void (*DCInvalidateRange)(void *addr, unsigned int size);
static void (*ICInvalidateRange)(void *addr, unsigned int size);
static unsigned int hook_LiWaitOneChunk;
static unsigned int addrphys_LiWaitOneChunk;
extern void SC0x25_KernelCopyData(unsigned int addr, unsigned int src, unsigned int len);
extern void my_PrepareTitle_hook(void);
/* Write a 32-bit word with kernel permissions */
static void __attribute__ ((noinline)) kern_write(void *addr, uint32_t value)
{
asm volatile (
"li 3,1\n"
"li 4,0\n"
"mr 5,%1\n"
"li 6,0\n"
"li 7,0\n"
"lis 8,1\n"
"mr 9,%0\n"
"mr %1,1\n"
"li 0,0x3500\n"
"sc\n"
"nop\n"
"mr 1,%1\n"
:
: "r"(addr), "r"(value)
: "memory", "ctr", "lr", "0", "3", "4", "5", "6", "7", "8", "9", "10",
"11", "12"
);
}
static void KernelCopyData(unsigned int addr, unsigned int src, unsigned int len)
{
/*
* Setup a DBAT access with cache inhibited to write through and read directly from memory
*/
unsigned int dbatu0, dbatl0, dbatu1, dbatl1;
// save the original DBAT value
asm volatile("mfdbatu %0, 0" : "=r" (dbatu0));
asm volatile("mfdbatl %0, 0" : "=r" (dbatl0));
asm volatile("mfdbatu %0, 1" : "=r" (dbatu1));
asm volatile("mfdbatl %0, 1" : "=r" (dbatl1));
unsigned int target_dbatu0 = 0;
unsigned int target_dbatl0 = 0;
unsigned int target_dbatu1 = 0;
unsigned int target_dbatl1 = 0;
unsigned int *dst_p = (unsigned int*)addr;
unsigned int *src_p = (unsigned int*)src;
// we only need DBAT modification for addresses out of our own DBAT range
// as our own DBAT is available everywhere for user and supervisor
// since our own DBAT is on DBAT5 position we don't collide here
if(addr < 0x00800000 || addr >= 0x01000000)
{
target_dbatu0 = (addr & 0x00F00000) | 0xC0000000 | 0x1F;
target_dbatl0 = (addr & 0xFFF00000) | 0x32;
asm volatile("mtdbatu 0, %0" : : "r" (target_dbatu0));
asm volatile("mtdbatl 0, %0" : : "r" (target_dbatl0));
dst_p = (unsigned int*)((addr & 0xFFFFFF) | 0xC0000000);
}
if(src < 0x00800000 || src >= 0x01000000)
{
target_dbatu1 = (src & 0x00F00000) | 0xB0000000 | 0x1F;
target_dbatl1 = (src & 0xFFF00000) | 0x32;
asm volatile("mtdbatu 1, %0" : : "r" (target_dbatu1));
asm volatile("mtdbatl 1, %0" : : "r" (target_dbatl1));
src_p = (unsigned int*)((src & 0xFFFFFF) | 0xB0000000);
}
asm volatile("eieio; isync");
unsigned int i;
for(i = 0; i < len; i += 4)
{
// if we are on the edge to next chunk
if((target_dbatu0 != 0) && (((unsigned int)dst_p & 0x00F00000) != (target_dbatu0 & 0x00F00000)))
{
target_dbatu0 = ((addr + i) & 0x00F00000) | 0xC0000000 | 0x1F;
target_dbatl0 = ((addr + i) & 0xFFF00000) | 0x32;
dst_p = (unsigned int*)(((addr + i) & 0xFFFFFF) | 0xC0000000);
asm volatile("eieio; isync");
asm volatile("mtdbatu 0, %0" : : "r" (target_dbatu0));
asm volatile("mtdbatl 0, %0" : : "r" (target_dbatl0));
asm volatile("eieio; isync");
}
if((target_dbatu1 != 0) && (((unsigned int)src_p & 0x00F00000) != (target_dbatu1 & 0x00F00000)))
{
target_dbatu1 = ((src + i) & 0x00F00000) | 0xB0000000 | 0x1F;
target_dbatl1 = ((src + i) & 0xFFF00000) | 0x32;
src_p = (unsigned int*)(((src + i) & 0xFFFFFF) | 0xB0000000);
asm volatile("eieio; isync");
asm volatile("mtdbatu 1, %0" : : "r" (target_dbatu1));
asm volatile("mtdbatl 1, %0" : : "r" (target_dbatl1));
asm volatile("eieio; isync");
}
*dst_p = *src_p;
++dst_p;
++src_p;
}
/*
* Restore original DBAT value
*/
asm volatile("eieio; isync");
asm volatile("mtdbatu 0, %0" : : "r" (dbatu0));
asm volatile("mtdbatl 0, %0" : : "r" (dbatl0));
asm volatile("mtdbatu 1, %0" : : "r" (dbatu1));
asm volatile("mtdbatl 1, %0" : : "r" (dbatl1));
asm volatile("eieio; isync");
}
// This function is called every time after LiBounceOneChunk.
// It waits for the asynchronous call of LiLoadAsync for the IOSU to fill data to the RPX/RPL address
// and return the still remaining bytes to load.
// We override it and replace the loaded date from LiLoadAsync with our data and our remaining bytes to load.
static int LiWaitOneChunk(unsigned int * iRemainingBytes, const char *filename, int fileType)
{
unsigned int result;
register int core_id;
int remaining_bytes = 0;
int sgFileOffset;
int sgBufferNumber;
int *sgBounceError;
int *sgGotBytes;
int *sgTotalBytes;
int *sgIsLoadingBuffer;
int *sgFinishedLoadingBuffer;
// get the current core
asm volatile("mfspr %0, 0x3EF" : "=r" (core_id));
// get the offset of per core global variable for dynload initialized (just a simple address + (core_id * 4))
unsigned int gDynloadInitialized = *(volatile unsigned int*)(OS_SPECIFICS->addr_gDynloadInitialized + (core_id << 2));
// Comment (Dimok):
// time measurement at this position for logger -> we don't need it right now except maybe for debugging
//unsigned long long systemTime1 = Loader_GetSystemTime();
if(OS_FIRMWARE == 550)
{
// pointer to global variables of the loader
loader_globals_550_t *loader_globals = (loader_globals_550_t*)(0xEFE19E80);
sgBufferNumber = loader_globals->sgBufferNumber;
sgFileOffset = loader_globals->sgFileOffset;
sgBounceError = &loader_globals->sgBounceError;
sgGotBytes = &loader_globals->sgGotBytes;
sgTotalBytes = &loader_globals->sgTotalBytes;
sgFinishedLoadingBuffer = &loader_globals->sgFinishedLoadingBuffer;
// not available on 5.5.x
sgIsLoadingBuffer = NULL;
}
else
{
// pointer to global variables of the loader
loader_globals_t *loader_globals = (loader_globals_t*)(OS_SPECIFICS->addr_sgIsLoadingBuffer);
sgBufferNumber = loader_globals->sgBufferNumber;
sgFileOffset = loader_globals->sgFileOffset;
sgBounceError = &loader_globals->sgBounceError;
sgGotBytes = &loader_globals->sgGotBytes;
sgIsLoadingBuffer = &loader_globals->sgIsLoadingBuffer;
// not available on < 5.5.x
sgTotalBytes = NULL;
sgFinishedLoadingBuffer = NULL;
}
// the data loading was started in LiBounceOneChunk() and here it waits for IOSU to finish copy the data
if(gDynloadInitialized != 0) {
result = OS_SPECIFICS->LiWaitIopCompleteWithInterrupts(0x2160EC0, &remaining_bytes);
}
else {
result = OS_SPECIFICS->LiWaitIopComplete(0x2160EC0, &remaining_bytes);
}
// Comment (Dimok):
// time measurement at this position for logger -> we don't need it right now except maybe for debugging
//unsigned long long systemTime2 = Loader_GetSystemTime();
//------------------------------------------------------------------------------------------------------------------
// Start of our function intrusion:
// After IOSU is done writing the data into the 0xF6000000/0xF6400000 address,
// we overwrite it with our data before setting the global flag for IsLoadingBuffer to 0
// Do this only if we are in the game that was launched by our method
s_mem_area *mem_area = MEM_AREA_TABLE;
if((ELF_DATA_ADDR == mem_area->address) && (fileType == 0))
{
unsigned int load_address = (sgBufferNumber == 1) ? 0xF6000000 : (0xF6000000 + 0x00400000);
unsigned int load_addressPhys = (sgBufferNumber == 1) ? 0x1B000000 : (0x1B000000 + 0x00400000); // virtual 0xF6000000 and 0xF6400000
remaining_bytes = ELF_DATA_SIZE - sgFileOffset;
if (remaining_bytes > 0x400000)
// truncate size
remaining_bytes = 0x400000;
DCFlushRange((void*)load_address, remaining_bytes);
u32 rpxBlockPos = 0;
u32 done = 0;
u32 mapOffset = 0;
while((done < (u32)sgFileOffset) && mem_area)
{
if((done + mem_area->size) > (u32)sgFileOffset)
{
mapOffset = sgFileOffset - done;
done = sgFileOffset;
}
else
{
done += mem_area->size;
mem_area = mem_area->next;
}
}
while((done < ELF_DATA_SIZE) && (rpxBlockPos < 0x400000) && mem_area)
{
u32 address = mem_area->address + mapOffset;
u32 blockSize = ELF_DATA_SIZE - done;
if(blockSize > (0x400000 - rpxBlockPos))
{
blockSize = 0x400000 - rpxBlockPos;
}
if((mapOffset + blockSize) >= mem_area->size)
{
blockSize = mem_area->size - mapOffset;
//! this value is incremented later by blockSize, so set it to -blockSize for it to be 0 after copy
//! it makes smaller code then if(mapOffset == mem_area->size) after copy
mapOffset = -blockSize;
mem_area = mem_area->next;
}
SC0x25_KernelCopyData(load_addressPhys + rpxBlockPos, address, blockSize);
done += blockSize;
rpxBlockPos += blockSize;
mapOffset += blockSize;
}
DCInvalidateRange((void*)load_address, remaining_bytes);
if((u32)(sgFileOffset + remaining_bytes) == ELF_DATA_SIZE)
{
ELF_DATA_ADDR = 0xDEADC0DE;
ELF_DATA_SIZE = 0;
MAIN_ENTRY_ADDR = 0xC001C0DE;
}
// set result to 0 -> "everything OK"
result = 0;
}
// end of our little intrusion into this function
//------------------------------------------------------------------------------------------------------------------
// set the result to the global bounce error variable
if(sgBounceError) {
*sgBounceError = result;
}
// disable global flag that buffer is still loaded by IOSU
if(sgFinishedLoadingBuffer)
{
unsigned int zeroBitCount = 0;
asm volatile("cntlzw %0, %0" : "=r" (zeroBitCount) : "r"(*sgFinishedLoadingBuffer));
*sgFinishedLoadingBuffer = zeroBitCount >> 5;
}
else if(sgIsLoadingBuffer)
{
*sgIsLoadingBuffer = 0;
}
// check result for errors
if(result == 0)
{
// the remaining size is set globally and in stack variable only
// if a pointer was passed to this function
if(iRemainingBytes) {
if(sgGotBytes) {
*sgGotBytes = remaining_bytes;
}
*iRemainingBytes = remaining_bytes;
// on 5.5.x a new variable for total loaded bytes was added
if(sgTotalBytes) {
*sgTotalBytes += remaining_bytes;
}
}
// Comment (Dimok):
// calculate time difference and print it on logging how long the wait for asynchronous data load took
// something like (systemTime2 - systemTime1) * constant / bus speed, did not look deeper into it as we don't need that crap
}
else {
// Comment (Dimok):
// a lot of error handling here. depending on error code sometimes calls Loader_Panic() -> we don't make errors so we can skip that part ;-P
}
return result;
}
void my_PrepareTitle(CosAppXmlInfo *xmlKernelInfo)
{
if(ELF_DATA_ADDR == MEM_AREA_TABLE->address)
{
xmlKernelInfo->max_size = RPX_MAX_SIZE;
xmlKernelInfo->max_codesize = RPX_MAX_CODE_SIZE;
//! setup our hook to LiWaitOneChunk for RPX loading
hook_LiWaitOneChunk = ((u32)LiWaitOneChunk) | 0x48000002;
KernelCopyData(addrphys_LiWaitOneChunk, (u32) &hook_LiWaitOneChunk, 4);
asm volatile("icbi 0, %0" : : "r" (OS_SPECIFICS->addr_LiWaitOneChunk & ~31));
}
else if((MAIN_ENTRY_ADDR == 0xC001C0DE) && (*(u32*)xmlKernelInfo->rpx_name == 0x66666c5f)) // ffl_
{
//! restore original LiWaitOneChunk instruction as our RPX is done
MAIN_ENTRY_ADDR = 0xDEADC0DE;
KernelCopyData(addrphys_LiWaitOneChunk, (u32)&OS_SPECIFICS->orig_LiWaitOneChunkInstr, 4);
asm volatile("icbi 0, %0" : : "r" (OS_SPECIFICS->addr_LiWaitOneChunk & ~31));
}
}
static int LoadFileToMem(private_data_t *private_data, const char *filepath, unsigned char **fileOut, unsigned int * sizeOut)
{
int iFd = -1;
void *pClient = private_data->MEMAllocFromDefaultHeapEx(FS_CLIENT_SIZE, 4);
if(!pClient)
return 0;
void *pCmd = private_data->MEMAllocFromDefaultHeapEx(FS_CMD_BLOCK_SIZE, 4);
if(!pCmd)
{
private_data->MEMFreeToDefaultHeap(pClient);
return 0;
}
int success = 0;
private_data->FSInit();
private_data->FSInitCmdBlock(pCmd);
private_data->FSAddClientEx(pClient, 0, -1);
do
{
char tempPath[FS_MOUNT_SOURCE_SIZE];
char mountPath[FS_MAX_MOUNTPATH_SIZE];
int status = private_data->FSGetMountSource(pClient, pCmd, 0, tempPath, -1);
if (status != 0) {
private_data->OSFatal("FSGetMountSource failed.");
}
status = private_data->FSMount(pClient, pCmd, tempPath, mountPath, FS_MAX_MOUNTPATH_SIZE, -1);
if(status != 0) {
private_data->OSFatal("SD mount failed.");
}
status = private_data->FSOpenFile(pClient, pCmd, filepath, "r", &iFd, -1);
if(status != 0) {
private_data->OSFatal("FSOpenFile failed.");
}
FSStat stat;
stat.size = 0;
void *pBuffer = NULL;
private_data->FSGetStatFile(pClient, pCmd, iFd, &stat, -1);
if(stat.size > 0)
pBuffer = private_data->MEMAllocFromDefaultHeapEx((stat.size + 0x3F) & ~0x3F, 0x40);
else
private_data->OSFatal("ELF file empty.");
unsigned int done = 0;
while(done < stat.size)
{
int readBytes = private_data->FSReadFile(pClient, pCmd, pBuffer + done, 1, stat.size - done, iFd, 0, -1);
if(readBytes <= 0) {
break;
}
done += readBytes;
}
if(done != stat.size)
{
private_data->MEMFreeToDefaultHeap(pBuffer);
}
else
{
*fileOut = (unsigned char*)pBuffer;
*sizeOut = stat.size;
success = 1;
}
private_data->FSCloseFile(pClient, pCmd, iFd, -1);
private_data->FSUnmount(pClient, pCmd, mountPath, -1);
}
while(0);
private_data->FSDelClient(pClient);
private_data->MEMFreeToDefaultHeap(pClient);
private_data->MEMFreeToDefaultHeap(pCmd);
return success;
}
static void setup_patches(private_data_t *private_data)
{
//! setup necessary syscalls and hooks for HBL
kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl1 + (0x25 * 4)), (unsigned int)KernelCopyData);
kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl2 + (0x25 * 4)), (unsigned int)KernelCopyData);
kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl3 + (0x25 * 4)), (unsigned int)KernelCopyData);
kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl4 + (0x25 * 4)), (unsigned int)KernelCopyData);
kern_write((void*)(OS_SPECIFICS->addr_KernSyscallTbl5 + (0x25 * 4)), (unsigned int)KernelCopyData);
//! store physical address for later use
addrphys_LiWaitOneChunk = private_data->OSEffectiveToPhysical((void*)OS_SPECIFICS->addr_LiWaitOneChunk);
u32 addr_my_PrepareTitle_hook = ((u32)my_PrepareTitle_hook) | 0x48000003;
DCFlushRange(&addr_my_PrepareTitle_hook, 4);
//! create our copy syscall
SC0x25_KernelCopyData(OS_SPECIFICS->addr_PrepareTitle_hook, private_data->OSEffectiveToPhysical(&addr_my_PrepareTitle_hook), 4);
}
static unsigned int load_elf_image (private_data_t *private_data, unsigned char *elfstart)
{
Elf32_Ehdr *ehdr;
Elf32_Phdr *phdrs;
unsigned char *image;
int i;
ehdr = (Elf32_Ehdr *) elfstart;
if(ehdr->e_phoff == 0 || ehdr->e_phnum == 0)
return 0;
if(ehdr->e_phentsize != sizeof(Elf32_Phdr))
return 0;
phdrs = (Elf32_Phdr*)(elfstart + ehdr->e_phoff);
for(i = 0; i < ehdr->e_phnum; i++)
{
if(phdrs[i].p_type != PT_LOAD)
continue;
if(phdrs[i].p_filesz > phdrs[i].p_memsz)
continue;
if(!phdrs[i].p_filesz)
continue;
unsigned int p_paddr = phdrs[i].p_paddr;
image = (unsigned char *) (elfstart + phdrs[i].p_offset);
private_data->memcpy ((void *) p_paddr, image, phdrs[i].p_filesz);
DCFlushRange((void*)p_paddr, phdrs[i].p_filesz);
if(phdrs[i].p_flags & PF_X)
ICInvalidateRange ((void *) p_paddr, phdrs[i].p_memsz);
}
//! clear BSS
Elf32_Shdr *shdr = (Elf32_Shdr *) (elfstart + ehdr->e_shoff);
for(i = 0; i < ehdr->e_shnum; i++)
{
const char *section_name = ((const char*)elfstart) + shdr[ehdr->e_shstrndx].sh_offset + shdr[i].sh_name;
if(section_name[0] == '.' && section_name[1] == 'b' && section_name[2] == 's' && section_name[3] == 's')
{
private_data->memset((void*)shdr[i].sh_addr, 0, shdr[i].sh_size);
DCFlushRange((void*)shdr[i].sh_addr, shdr[i].sh_size);
}
else if(section_name[0] == '.' && section_name[1] == 's' && section_name[2] == 'b' && section_name[3] == 's' && section_name[4] == 's')
{
private_data->memset((void*)shdr[i].sh_addr, 0, shdr[i].sh_size);
DCFlushRange((void*)shdr[i].sh_addr, shdr[i].sh_size);
}
}
return ehdr->e_entry;
}
static void loadFunctionPointers(private_data_t * private_data)
{
unsigned int coreinit_handle;
EXPORT_DECL(int, OSDynLoad_Acquire, const char* rpl, u32 *handle);
EXPORT_DECL(int, OSDynLoad_FindExport, u32 handle, int isdata, const char *symbol, void *address);
OSDynLoad_Acquire = (int (*)(const char*, u32 *))OS_SPECIFICS->addr_OSDynLoad_Acquire;
OSDynLoad_FindExport = (int (*)(u32, int, const char *, void *))OS_SPECIFICS->addr_OSDynLoad_FindExport;
OSDynLoad_Acquire("coreinit", &coreinit_handle);
unsigned int *functionPtr = 0;
OSDynLoad_FindExport(coreinit_handle, 1, "MEMAllocFromDefaultHeapEx", &functionPtr);
private_data->MEMAllocFromDefaultHeapEx = (void * (*)(int, int))*functionPtr;
OSDynLoad_FindExport(coreinit_handle, 1, "MEMFreeToDefaultHeap", &functionPtr);
private_data->MEMFreeToDefaultHeap = (void (*)(void *))*functionPtr;
OS_FIND_EXPORT(coreinit_handle, "memcpy", private_data->memcpy);
OS_FIND_EXPORT(coreinit_handle, "memset", private_data->memset);
OS_FIND_EXPORT(coreinit_handle, "OSFatal", private_data->OSFatal);
OS_FIND_EXPORT(coreinit_handle, "DCFlushRange", DCFlushRange);
OS_FIND_EXPORT(coreinit_handle, "DCInvalidateRange", DCInvalidateRange);
OS_FIND_EXPORT(coreinit_handle, "ICInvalidateRange", ICInvalidateRange);
OS_FIND_EXPORT(coreinit_handle, "OSEffectiveToPhysical", private_data->OSEffectiveToPhysical);
OS_FIND_EXPORT(coreinit_handle, "exit", private_data->exit);
OS_FIND_EXPORT(coreinit_handle, "FSInit", private_data->FSInit);
OS_FIND_EXPORT(coreinit_handle, "FSAddClientEx", private_data->FSAddClientEx);
OS_FIND_EXPORT(coreinit_handle, "FSDelClient", private_data->FSDelClient);
OS_FIND_EXPORT(coreinit_handle, "FSInitCmdBlock", private_data->FSInitCmdBlock);
OS_FIND_EXPORT(coreinit_handle, "FSGetMountSource", private_data->FSGetMountSource);
OS_FIND_EXPORT(coreinit_handle, "FSMount", private_data->FSMount);
OS_FIND_EXPORT(coreinit_handle, "FSUnmount", private_data->FSUnmount);
OS_FIND_EXPORT(coreinit_handle, "FSOpenFile", private_data->FSOpenFile);
OS_FIND_EXPORT(coreinit_handle, "FSGetStatFile", private_data->FSGetStatFile);
OS_FIND_EXPORT(coreinit_handle, "FSReadFile", private_data->FSReadFile);
OS_FIND_EXPORT(coreinit_handle, "FSCloseFile", private_data->FSCloseFile);
unsigned int sysapp_handle;
OSDynLoad_Acquire("sysapp.rpl", &sysapp_handle);
OS_FIND_EXPORT(sysapp_handle, "SYSRelaunchTitle", private_data->SYSRelaunchTitle);
}
int _start(int argc, char **argv)
{
private_data_t private_data;
if(MAIN_ENTRY_ADDR != 0xC001C0DE)
{
loadFunctionPointers(&private_data);
while(1)
{
if(ELF_DATA_ADDR != 0xDEADC0DE && ELF_DATA_SIZE > 0)
{
//! copy data to safe area before processing it
unsigned char * pElfBuffer = (unsigned char *)private_data.MEMAllocFromDefaultHeapEx(ELF_DATA_SIZE, 4);
if(pElfBuffer)
{
private_data.memcpy(pElfBuffer, (unsigned char*)ELF_DATA_ADDR, ELF_DATA_SIZE);
MAIN_ENTRY_ADDR = load_elf_image(&private_data, pElfBuffer);
private_data.MEMFreeToDefaultHeap(pElfBuffer);
}
ELF_DATA_ADDR = 0xDEADC0DE;
ELF_DATA_SIZE = 0;
}
if(MAIN_ENTRY_ADDR == 0xDEADC0DE || MAIN_ENTRY_ADDR == 0)
{
//! setup necessary syscalls and hooks for HBL before launching it
setup_patches(&private_data);
if(HBL_CHANNEL)
{
break;
}
else
{
unsigned char *pElfBuffer = NULL;
unsigned int uiElfSize = 0;
LoadFileToMem(&private_data, CAFE_OS_SD_PATH WIIU_PATH "/apps/homebrew_launcher/homebrew_launcher.elf", &pElfBuffer, &uiElfSize);
if(!pElfBuffer)
{
private_data.OSFatal("Failed to load homebrew_launcher.elf");
}
else
{
MAIN_ENTRY_ADDR = load_elf_image(&private_data, pElfBuffer);
if(MAIN_ENTRY_ADDR == 0)
{
private_data.OSFatal("Failed to load homebrew_launcher.elf");
}
else
{
private_data.MEMFreeToDefaultHeap(pElfBuffer);
}
}
}
}
else
{
int returnVal = ((int (*)(int, char **))MAIN_ENTRY_ADDR)(argc, argv);
//! exit to miimaker and restart application on re-enter of another application
if(returnVal == (int)EXIT_RELAUNCH_ON_LOAD)
{
break;
}
//! exit to homebrew launcher in all other cases
else
{
MAIN_ENTRY_ADDR = 0xDEADC0DE;
private_data.SYSRelaunchTitle(0, 0);
private_data.exit(0);
break;
}
}
}
}
int ret = ( (int (*)(int, char **))(*(unsigned int*)OS_SPECIFICS->addr_OSTitle_main_entry) )(argc, argv);
//! if an application returns and was an RPX launch then launch HBL again
if(MAIN_ENTRY_ADDR == 0xC001C0DE)
{
private_data.SYSRelaunchTitle(0, 0);
private_data.exit(0);
}
return ret;
}