From a0a24c72de4dce8cf5366b230246063ed9dbf90a Mon Sep 17 00:00:00 2001 From: 0verjoY <59394546+0verjoY@users.noreply.github.com> Date: Thu, 2 Feb 2023 12:52:32 +0100 Subject: [PATCH] - Update / fix app boot code --- Makefile | 4 +- Test/menu.c | 1416 ++++++++++++++++++++++++++++++++++ data/appboot.bin | Bin 1816 -> 182208 bytes source/appboot.c | 188 ++++- source/appboot.h | 17 + source/appmetadata.c | 23 +- source/boot/Makefile | 174 ++++- source/boot/loaddol.c | 32 - source/boot/loaddol.h | 22 - source/boot/loadelf.c | 47 -- source/boot/loadelf.h | 55 -- source/boot/main.c | 40 - source/boot/openstub.ld | 15 - source/boot/source/crt0.s | 22 + source/boot/source/elf_abi.h | 593 ++++++++++++++ source/boot/source/link.ld | 27 + source/boot/source/loader.c | 104 +++ source/boot/source/loader.h | 26 + source/boot/source/main.c | 88 +++ source/boot/utils.c | 77 -- source/boot/utils.h | 54 -- 21 files changed, 2603 insertions(+), 421 deletions(-) create mode 100644 Test/menu.c delete mode 100644 source/boot/loaddol.c delete mode 100644 source/boot/loaddol.h delete mode 100644 source/boot/loadelf.c delete mode 100644 source/boot/loadelf.h delete mode 100644 source/boot/main.c delete mode 100644 source/boot/openstub.ld create mode 100644 source/boot/source/crt0.s create mode 100644 source/boot/source/elf_abi.h create mode 100644 source/boot/source/link.ld create mode 100644 source/boot/source/loader.c create mode 100644 source/boot/source/loader.h create mode 100644 source/boot/source/main.c delete mode 100644 source/boot/utils.c delete mode 100644 source/boot/utils.h diff --git a/Makefile b/Makefile index 442c112..fa36586 100644 --- a/Makefile +++ b/Makefile @@ -100,7 +100,9 @@ export OUTPUT := $(CURDIR)/$(TARGET) #--------------------------------------------------------------------------------- $(BUILD): @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -s -C source/boot all + @$(MAKE) --no-print-directory -s -C source/boot + @mv -u $(CURDIR)/source/boot/appboot.bin \ + $(CURDIR)/data/appboot.bin @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile #--------------------------------------------------------------------------------- diff --git a/Test/menu.c b/Test/menu.c new file mode 100644 index 0000000..aa50a82 --- /dev/null +++ b/Test/menu.c @@ -0,0 +1,1416 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys.h" +#include "fat.h" +#include "nand.h" +#include "restart.h" +#include "title.h" +#include "usbstorage.h" +#include "utils.h" +#include "video.h" +#include "wad.h" +#include "wpad.h" +#include +#include "globals.h" +#include "iospatch.h" +#include "appboot.h" + +/* FAT device list */ +//static fatDevice fdevList[] = { +fatDevice fdevList[] = { + { "sd", "Wii SD Slot", &__io_wiisd }, + { "usb", "USB Mass Storage Device", &__io_usbstorage }, + { "usb2", "USB 2.0 Mass Storage Device", &__io_wiiums }, + { "gcsda", "SD Gecko (Slot A)", &__io_gcsda }, + { "gcsdb", "SD Gecko (Slot B)", &__io_gcsdb }, + //{ "smb", "SMB share", NULL }, +}; + +/* NAND device list */ +//static nandDevice ndevList[] = { +nandDevice ndevList[] = { + { "Disable", 0, 0x00, 0x00 }, + { "SD/SDHC Card", 1, 0xF0, 0xF1 }, + { "USB 2.0 Mass Storage Device", 2, 0xF2, 0xF3 }, +}; + +/* FAT device */ +static fatDevice *fdev = NULL; +static nandDevice *ndev = NULL; + +// wiiNinja: Define a buffer holding the previous path names as user +// traverses the directory tree. Max of 10 levels is define at this point +static u8 gDirLevel = 0; +static char gDirList [MAX_DIR_LEVELS][MAX_FILE_PATH_LEN]; +static s32 gSeleted[MAX_DIR_LEVELS]; +static s32 gStart[MAX_DIR_LEVELS]; + +/* Macros */ +#define NB_FAT_DEVICES (sizeof(fdevList) / sizeof(fatDevice)) +#define NB_NAND_DEVICES (sizeof(ndevList) / sizeof(nandDevice)) + +// Local prototypes: wiiNinja +void WaitPrompt (char *prompt); +int PushCurrentDir(char *dirStr, int Selected, int Start); +char *PopCurrentDir(int *Selected, int *Start); +bool IsListFull (void); +char *PeekCurrentDir (void); +u32 WaitButtons(void); +u32 Pad_GetButtons(void); +void WiiLightControl (int state); + +int __Menu_IsGreater(const void *p1, const void *p2) +{ + u32 n1 = *(u32 *)p1; + u32 n2 = *(u32 *)p2; + + /* Equal */ + if (n1 == n2) + return 0; + + return (n1 > n2) ? 1 : -1; +} + + +int __Menu_EntryCmp(const void *p1, const void *p2) +{ + fatFile *f1 = (fatFile *)p1; + fatFile *f2 = (fatFile *)p2; + + /* Compare entries */ // wiiNinja: Include directory + if ((f1->isdir) && !(f2->isdir)) + return (-1); + else if (!(f1->isdir) && (f2->isdir)) + return (1); + else + return strcasecmp(f1->filename, f2->filename); +} + +static bool __FolderExists(const char *path) +{ + DIR *dir; + dir = opendir(path); + if(dir) + { + closedir(dir); + return true; + } + return false; +} + +static size_t __GetFileSizeBytes(const char *path) +{ + FILE *f; + size_t size = 0; + + f = fopen(path, "rb"); + if(!f) return 0; + + //Get file size + fseek(f, 0, SEEK_END); + size = ftell(f); + fclose(f); + + return size; +} + +char gFileName[MAX_FILE_PATH_LEN]; +s32 __Menu_RetrieveList(char *inPath, fatFile **outbuf, u32 *outlen) +{ + fatFile *buffer = NULL; + DIR *dir = NULL; + struct dirent *ent = NULL; + + //char dirpath[256], filename[768]; + u32 cnt; + + /* Generate dirpath */ + //sprintf(dirpath, "%s:" WAD_DIRECTORY, fdev->mount); + + /* Open directory */ + dir = opendir(inPath); + if (!dir) + return -1; + + /* Count entries */ + for (cnt = 0; ((ent = readdir(dir)) != NULL);) { + cnt++; + } + + if (cnt > 0) { + /* Allocate memory */ + buffer = malloc(sizeof(fatFile) * cnt); + if (!buffer) { + closedir(dir); + return -2; + } + + /* Reset directory */ + rewinddir(dir); + + /* Get entries */ + for (cnt = 0; ((ent = readdir(dir)) != NULL);) + { + bool addFlag = false; + bool isdir = false; + size_t fsize = 0; + + snprintf(gFileName, MAX_FILE_PATH_LEN, "%s/%s", inPath, ent->d_name); + if (__FolderExists(gFileName)) // wiiNinja + { + isdir = true; + // Add only the item ".." which is the previous directory + // AND if we're not at the root directory + if ((strcmp (ent->d_name, "..") == 0) && (gDirLevel > 1)) + addFlag = true; + else if (strcmp (ent->d_name, ".") != 0) + addFlag = true; + } + else + { + if(strlen(ent->d_name)>4) + { + if(!strcasecmp(ent->d_name+strlen(ent->d_name)-4, ".wad")) + { + fsize = __GetFileSizeBytes(gFileName); + addFlag = true; + } + } + } + + if (addFlag == true) + { + fatFile *file = &buffer[cnt++]; + + /* File name */ + strcpy(file->filename, ent->d_name); + + /* File stats */ + file->isdir = isdir; + file->fsize = fsize; + + } + } + + /* Sort list */ + qsort(buffer, cnt, sizeof(fatFile), __Menu_EntryCmp); + } + + /* Close directory */ + closedir(dir); + + /* Set values */ + *outbuf = buffer; + *outlen = cnt; + + return 0; +} + + +void Menu_SelectIOS(void) +{ + u8 *iosVersion = NULL; + u32 iosCnt; + u8 tmpVersion; + + u32 cnt; + s32 ret, selected = 0; + bool found = false; + + /* Get IOS versions */ + ret = Title_GetIOSVersions(&iosVersion, &iosCnt); + if (ret < 0) + return; + + /* Sort list */ + qsort(iosVersion, iosCnt, sizeof(u8), __Menu_IsGreater); + + if (gConfig.cIOSVersion < 0) + tmpVersion = CIOS_VERSION; + else + { + tmpVersion = (u8)gConfig.cIOSVersion; + // For debugging only + //printf ("User pre-selected cIOS: %i\n", tmpVersion); + //WaitButtons(); + } + + /* Set default version */ + for (cnt = 0; cnt < iosCnt; cnt++) { + u8 version = iosVersion[cnt]; + + /* Custom IOS available */ + //if (version == CIOS_VERSION) + if (version == tmpVersion) + { + selected = cnt; + found = true; + break; + } + + /* Current IOS */ + if (version == IOS_GetVersion()) + selected = cnt; + } + + /* Ask user for IOS version */ + if ((gConfig.cIOSVersion < 0) || (found == false)) + { + for (;;) + { + /* Clear console */ + Con_Clear(); + + printf("\t>> Select IOS version to use: < IOS%d >\n\n", iosVersion[selected]); + + printf("\t Press LEFT/RIGHT to change IOS version.\n\n"); + + printf("\t Press A button to continue.\n"); + printf("\t Press HOME button to restart.\n\n"); + + u32 buttons = WaitButtons(); + + /* LEFT/RIGHT buttons */ + if (buttons & WPAD_BUTTON_LEFT) { + if ((--selected) <= -1) + selected = (iosCnt - 1); + } + if (buttons & WPAD_BUTTON_RIGHT) { + if ((++selected) >= iosCnt) + selected = 0; + } + + /* HOME button */ + if (buttons & WPAD_BUTTON_HOME) + Restart(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + } + } + + + u8 version = iosVersion[selected]; + + if (IOS_GetVersion() != version) { + /* Shutdown subsystems */ + Wpad_Disconnect(); + //mload_close(); + + /* Load IOS */ + + if(!loadIOS(version)) Wpad_Init(), Menu_SelectIOS(); + + /* Initialize subsystems */ + Wpad_Init(); + } +} + +void Menu_FatDevice(void) +{ + int ret, selected = 0; + + /* Unmount FAT device */ + //if (fdev) + //Fat_Unmount(fdev); + //if (((fdevList[selected].mount[0] == 's') && (ndev->name[0] == 'S'))) + //selected++; + static const u16 konamiCode[] = { + WPAD_BUTTON_UP, WPAD_BUTTON_UP, WPAD_BUTTON_DOWN, WPAD_BUTTON_DOWN, WPAD_BUTTON_LEFT, + WPAD_BUTTON_RIGHT, WPAD_BUTTON_LEFT, WPAD_BUTTON_RIGHT, WPAD_BUTTON_B, WPAD_BUTTON_A + }; + + int codePosition = 0; + + /* Select source device */ + if (gConfig.fatDeviceIndex < 0) + { + for (;;) { + /* Clear console */ + Con_Clear(); + + /* Selected device */ + fdev = &fdevList[selected]; + + printf("\t>> Select source device: < %s >\n\n", fdev->name); + + printf("\t Press LEFT/RIGHT to change the selected device.\n\n"); + + printf("\t Press A button to continue.\n"); + printf("\t Press HOME button to restart.\n\n"); + + u32 buttons = WaitButtons(); + + if (buttons & (WPAD_BUTTON_UP | WPAD_BUTTON_DOWN | WPAD_BUTTON_RIGHT | WPAD_BUTTON_LEFT | WPAD_BUTTON_A | WPAD_BUTTON_B)) { + if (buttons & konamiCode[codePosition]) + ++codePosition; + else + codePosition = 0; + } + + /* LEFT/RIGHT buttons */ + if (buttons & WPAD_BUTTON_LEFT) { + if ((--selected) <= -1) + selected = (NB_FAT_DEVICES - 1); + if ((fdevList[selected].mount[0] == 's') && (ndev->name[0] == 'S')) + selected--; + if ((fdevList[selected].mount[0] == 'u' && fdevList[selected].mount[3] == '2') && (ndev->name[0] == 'U')) + selected--; + if ((selected) <= -1) + selected = (NB_FAT_DEVICES - 1); + } + if (buttons & WPAD_BUTTON_RIGHT) { + if ((++selected) >= NB_FAT_DEVICES) + selected = 0; + if ((fdevList[selected].mount[0] == 's') && (ndev->name[0] == 'S')) + selected++; + if ((fdevList[selected].mount[0] == 'u' && fdevList[selected].mount[3] == '2') && (ndev->name[0] == 'U')) + selected++; + } + + /* HOME button */ + if (buttons & WPAD_BUTTON_HOME) + Restart(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) { + if (codePosition == sizeof(konamiCode) / sizeof(konamiCode[0])) { + extern bool skipRegionSafetyCheck; + skipRegionSafetyCheck = true; + printf("[+] Disabled SM region checks\n"); + sleep(2); + } + break; + } + } + } + else + { + sleep(5); + fdev = &fdevList[gConfig.fatDeviceIndex]; + } + + printf("[+] Mounting %s, please wait...", fdev->name ); + fflush(stdout); + + /* Mount FAT device */ + + ret = Fat_Mount(fdev); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto err; + } else + printf(" OK!\n"); + + return; + +err: + + if(gConfig.fatDeviceIndex >= 0) gConfig.fatDeviceIndex = -1; + WiiLightControl (WII_LIGHT_OFF); + printf("\n"); + printf(" Press any button to continue...\n"); + + WaitButtons(); + + /* Prompt menu again */ + Menu_FatDevice(); +} + +void Menu_NandDevice(void) +{ + int ret, selected = 0; + + /* Disable NAND emulator */ + if (ndev) { + Nand_Unmount(ndev); + Nand_Disable(); + } + + /* Select source device */ + if (gConfig.nandDeviceIndex < 0) + { + for (;;) { + /* Clear console */ + Con_Clear(); + + /* Selected device */ + ndev = &ndevList[selected]; + + printf("\t>> Select NAND emulator device: < %s >\n\n", ndev->name); + + printf("\t Press LEFT/RIGHT to change the selected device.\n\n"); + + printf("\t Press A button to continue.\n"); + printf("\t Press HOME button to restart.\n\n"); + + u32 buttons = WaitButtons(); + + /* LEFT/RIGHT buttons */ + if (buttons & WPAD_BUTTON_LEFT) { + if ((--selected) <= -1) + selected = (NB_NAND_DEVICES - 1); + } + if (buttons & WPAD_BUTTON_RIGHT) { + if ((++selected) >= NB_NAND_DEVICES) + selected = 0; + } + + /* HOME button */ + if (buttons & WPAD_BUTTON_HOME) + Restart(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + } + } + else + { + ndev = &ndevList[gConfig.nandDeviceIndex]; + } + + /* No NAND device */ + if (!ndev->mode) + return; + + printf("[+] Enabling NAND emulator..."); + fflush(stdout); + + /* Mount NAND device */ + ret = Nand_Mount(ndev); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto err; + } + + /* Enable NAND emulator */ + ret = Nand_Enable(ndev); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto err; + } else + printf(" OK!\n"); + + return; + +err: + printf("\n"); + printf(" Press any button to continue...\n"); + + WaitButtons(); + + /* Prompt menu again */ + Menu_NandDevice(); +} + +char gTmpFilePath[MAX_FILE_PATH_LEN]; +/* Install and/or Uninstall multiple WADs - Leathl */ +int Menu_BatchProcessWads(fatFile *files, int fileCount, char *inFilePath, int installCnt, int uninstallCnt) +{ + int count; + + for (;;) + { + Con_Clear(); + + if ((installCnt > 0) & (uninstallCnt == 0)) { + printf("[+] %d file%s marked for installation.\n", installCnt, (installCnt == 1) ? "" : "s"); + printf(" Do you want to proceed?\n"); + } + else if ((installCnt == 0) & (uninstallCnt > 0)) { + printf("[+] %d file%s marked for uninstallation.\n", uninstallCnt, (uninstallCnt == 1) ? "" : "s"); + printf(" Do you want to proceed?\n"); + } + else { + printf("[+] %d file%s marked for installation and %d file%s for uninstallation.\n", installCnt, (installCnt == 1) ? "" : "s", uninstallCnt, (uninstallCnt == 1) ? "" : "s"); + printf(" Do you want to proceed?\n"); + } + + printf("\n\n Press A to continue.\n"); + printf(" Press B to go back to the menu.\n\n"); + + u32 buttons = WaitButtons(); + + if (buttons & WPAD_BUTTON_A) + break; + + if (buttons & WPAD_BUTTON_B) + return 0; + } + + WiiLightControl (WII_LIGHT_ON); + int errors = 0; + int success = 0; + s32 ret; + + for (count = 0; count < fileCount; count++) + { + fatFile *thisFile = &files[count]; + + if ((thisFile->install == 1) | (thisFile->install == 2)) { + int mode = thisFile->install; + Con_Clear(); + printf("[+] Opening \"%s\", please wait...\n\n", thisFile->filename); + + sprintf(gTmpFilePath, "%s/%s", inFilePath, thisFile->filename); + + FILE *fp = fopen(gTmpFilePath, "rb"); + if (!fp) { + printf(" ERROR!\n"); + errors += 1; + continue; + } + + printf("[+] %s WAD, please wait...\n", (mode == 2) ? "Uninstalling" : "Installing"); + if (mode == 2) { + ret = Wad_Uninstall(fp); + } + else { + ret = Wad_Install(fp); + } + + if (ret < 0) errors += 1; + else success += 1; + + thisFile->installstate = ret; + + if (fp) + fclose(fp); + } + } + + WiiLightControl (WII_LIGHT_OFF); + + printf("\n"); + printf(" %d titles succeeded and %d failed...\n", success, errors); + + if (errors > 0) + { + printf("\n Some operations failed"); + printf("\n Press A to list.\n"); + printf(" Press B skip.\n"); + + u32 buttons = WaitButtons(); + + if ((buttons & WPAD_BUTTON_A)) + { + Con_Clear(); + + int i=0; + for (count = 0; count < fileCount; count++) + { + fatFile *thisFile = &files[count]; + + if (thisFile->installstate <0) + { + char str[41]; + strncpy(str, thisFile->filename, 40); //Only 40 chars to fit the screen + str[40]=0; + i++; + if(thisFile->installstate == -999) printf(" %s BRICK BLOCKED\n", str); + else if(thisFile->installstate == -998) printf(" %s Skipped\n", str); + else if(thisFile->installstate == -106) printf(" %s Not installed?\n", str); + else if(thisFile->installstate == -1036 ) printf(" %s Needed IOS missing\n", str); + else if(thisFile->installstate == -4100 ) printf(" %s No trucha bug?\n", str); + else printf(" %s error %d\n", str, thisFile->installstate); + if( i == 17 ) + { + printf("\n Press any button to continue\n"); + WaitButtons(); + i = 0; + } + } + } + } + } + printf("\n Press any button to continue...\n"); + WaitButtons(); + + return 1; +} + +/* File Operations - Leathl */ +int Menu_FileOperations(fatFile *file, char *inFilePath) +{ + f32 filesize = (file->fsize / MB_SIZE); + + for (;;) + { + Con_Clear(); + + printf("[+] WAD Filename : %s\n", file->filename); + printf(" WAD Filesize : %.2f MB\n\n\n", filesize); + + + printf("[+] Select action: < %s WAD >\n\n", "Delete"); //There's yet nothing else than delete + + printf(" Press LEFT/RIGHT to change selected action.\n\n"); + + printf(" Press A to continue.\n"); + printf(" Press B to go back to the menu.\n\n"); + + u32 buttons = WaitButtons(); + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + + /* B button */ + if (buttons & WPAD_BUTTON_B) + return 0; + } + + Con_Clear(); + + printf("[+] Deleting \"%s\", please wait...\n", file->filename); + + sprintf(gTmpFilePath, "%s/%s", inFilePath, file->filename); + int error = remove(gTmpFilePath); + if (error != 0) + printf(" ERROR!"); + else + printf(" Successfully deleted!"); + + printf("\n"); + printf(" Press any button to continue...\n"); + + WaitButtons(); + + return !error; +} + +void Menu_WadManage(fatFile *file, char *inFilePath) +{ + FILE *fp = NULL; + + //char filepath[128]; + f32 filesize; + + u32 mode = 0; + + /* File size in megabytes */ + filesize = (file->fsize / MB_SIZE); + + for (;;) { + /* Clear console */ + Con_Clear(); + + printf("[+] WAD Filename : %s\n", file->filename); + printf(" WAD Filesize : %.2f MB\n\n\n", filesize); + + + printf("[+] Select action: < %s WAD >\n\n", (!mode) ? "Install" : "Uninstall"); + + printf(" Press LEFT/RIGHT to change selected action.\n\n"); + + printf(" Press A to continue.\n"); + printf(" Press B to go back to the menu.\n\n"); + + u32 buttons = WaitButtons(); + + /* LEFT/RIGHT buttons */ + if (buttons & (WPAD_BUTTON_LEFT | WPAD_BUTTON_RIGHT)) + mode ^= 1; + + /* A button */ + if (buttons & WPAD_BUTTON_A) + break; + + /* B button */ + if (buttons & WPAD_BUTTON_B) + return; + } + + /* Clear console */ + Con_Clear(); + + printf("[+] Opening \"%s\", please wait...", file->filename); + fflush(stdout); + + /* Generate filepath */ + // sprintf(filepath, "%s:" WAD_DIRECTORY "/%s", fdev->mount, file->filename); + sprintf(gTmpFilePath, "%s/%s", inFilePath, file->filename); // wiiNinja + + /* Open WAD */ + fp = fopen(gTmpFilePath, "rb"); + if (!fp) { + printf(" ERROR!\n"); + goto out; + } else + printf(" OK!\n\n"); + + printf("[+] %s WAD, please wait...\n", (!mode) ? "Installing" : "Uninstalling"); + + /* Do install/uninstall */ + WiiLightControl (WII_LIGHT_ON); + if (!mode) + Wad_Install(fp); + else + Wad_Uninstall(fp); + WiiLightControl (WII_LIGHT_OFF); + +out: + /* Close file */ + if (fp) + fclose(fp); + + printf("\n"); + printf(" Press any button to continue...\n"); + + /* Wait for button */ + WaitButtons(); +} + +void Menu_WadList(void) +{ + char str [100]; + fatFile *fileList = NULL; + u32 fileCnt; + int ret, selected = 0, start = 0; + char *tmpPath = malloc (MAX_FILE_PATH_LEN); + int installCnt = 0; + int uninstallCnt = 0; + + //fatFile *installFiles = malloc(sizeof(fatFile) * 50); + //int installCount = 0; + + // wiiNinja: check for malloc error + if (tmpPath == NULL) + { + ret = -997; // What am I gonna use here? + printf(" ERROR! Out of memory (ret = %d)\n", ret); + return; + } + + printf("[+] Retrieving file list..."); + fflush(stdout); + + gDirLevel = 0; + + // push root dir as base folder + sprintf(tmpPath, "%s:%s", fdev->mount, WAD_DIRECTORY); + PushCurrentDir(tmpPath,0,0); + // if user provides startup directory, try it out first + if (strcmp (WAD_DIRECTORY, gConfig.startupPath) != 0) + { + // replace root dir with provided startup directory + sprintf(tmpPath, "%s:%s", fdev->mount, gConfig.startupPath); + // If the directory can be successfully opened, it must exists + DIR *tmpDirPtr = opendir(tmpPath); + if (tmpDirPtr) + { + closedir (tmpDirPtr); + PushCurrentDir(tmpPath,0,0); + } + else // unable to open provided dir, stick with root dir + sprintf(tmpPath, "%s:%s", fdev->mount, WAD_DIRECTORY); + } + + /* Retrieve filelist */ +getList: + free (fileList); + fileList = NULL; + + ret = __Menu_RetrieveList(tmpPath, &fileList, &fileCnt); + if (ret < 0) { + printf(" ERROR! (ret = %d)\n", ret); + goto err; + } + + /* No files */ + if (!fileCnt) { + printf(" No files found!\n"); + goto err; + } + + /* Set install-values to 0 - Leathl */ + int counter; + for (counter = 0; counter < fileCnt; counter++) { + fatFile *file = &fileList[counter]; + file->install = 0; + } + + for (;;) + { + u32 cnt; + s32 index; + + /* Clear console */ + Con_Clear(); + + /** Print entries **/ + cnt = strlen(tmpPath); + if(cnt>30) + index = cnt-30; + else + index = 0; + + printf("[+] WAD files on [%s]:\n\n", tmpPath+index); + + /* Print entries */ + for (cnt = start; cnt < fileCnt; cnt++) + { + fatFile *file = &fileList[cnt]; + f32 filesize = file->fsize / MB_SIZE; + + /* Entries per page limit */ + if ((cnt - start) >= ENTRIES_PER_PAGE) + break; + + strncpy(str, file->filename, 40); //Only 40 chars to fit the screen + str[40]=0; + + /* Print filename */ + //printf("\t%2s %s (%.2f MB)\n", (cnt == selected) ? ">>" : " ", file->filename, filesize); + if (file->isdir) // wiiNinja + printf("\t%2s [%s]\n", (cnt == selected) ? ">>" : " ", str); + else + printf("\t%2s%s%s (%.2f MB)\n", (cnt == selected) ? ">>" : " ", (file->install == 1) ? "+" : ((file->install == 2) ? "-" : " "), str, filesize); + + } + + printf("\n"); + + printf("[+] Press A to (un)install."); + if(gDirLevel>1) + printf(" Press B to go up-level DIR.\n"); + else + printf(" Press B to select a device.\n"); + printf(" Use +/X and -/Y to (un)mark. Press 1/Z/ZR for delete menu.\n"); + + printf(" Press 2 to launch app.\n"); + + /** Controls **/ + u32 buttons = WaitButtons(); + + if (buttons & WPAD_BUTTON_2) + { + if (!LoadApp(tmpPath)) + { + printf(" Failed to load app.\n"); + goto err; + } + + Fat_Unmount(fdev); + //SetIos(36); + + LaunchApp(); + } + + /* DPAD buttons */ + if (buttons & WPAD_BUTTON_UP) { + selected--; + + if (selected <= -1) + selected = (fileCnt - 1); + } + if (buttons & WPAD_BUTTON_LEFT) { + selected = selected + ENTRIES_PER_PAGE; + + if (selected >= fileCnt) + selected = 0; + } + if (buttons & WPAD_BUTTON_DOWN) { + selected ++; + + if (selected >= fileCnt) + selected = 0; + } + if (buttons & WPAD_BUTTON_RIGHT) { + selected = selected - ENTRIES_PER_PAGE; + + if (selected <= -1) + selected = (fileCnt - 1); + } + + /* HOME button */ + if (buttons & WPAD_BUTTON_HOME) + Restart(); + + /* Plus Button - Leathl */ + if (buttons & WPAD_BUTTON_PLUS) + { + if(Wpad_TimeButton()) + { + installCnt = 0; + int i = 0; + while( i < fileCnt) + { + fatFile *file = &fileList[i]; + if (((file->isdir) == false) & (file->install == 0)) { + file->install = 1; + + installCnt += 1; + } + else if (((file->isdir) == false) & (file->install == 1)) { + file->install = 0; + + installCnt -= 1; + } + else if (((file->isdir) == false) & (file->install == 2)) { + file->install = 1; + + installCnt += 1; + uninstallCnt -= 1; + } + i++; + } + + } + else + { + fatFile *file = &fileList[selected]; + if (((file->isdir) == false) & (file->install == 0)) { + file->install = 1; + + installCnt += 1; + } + else if (((file->isdir) == false) & (file->install == 1)) { + file->install = 0; + + installCnt -= 1; + } + else if (((file->isdir) == false) & (file->install == 2)) { + file->install = 1; + + installCnt += 1; + uninstallCnt -= 1; + } + selected++; + + if (selected >= fileCnt) + selected = 0; + } + } + + /* Minus Button - Leathl */ + if (buttons & WPAD_BUTTON_MINUS) + { + if(Wpad_TimeButton()) + { + installCnt = 0; + int i = 0; + while( i < fileCnt) + { + fatFile *file = &fileList[i]; + if (((file->isdir) == false) & (file->install == 0)) { + file->install = 2; + + uninstallCnt += 1; + } + else if (((file->isdir) == false) & (file->install == 1)) { + file->install = 2; + + uninstallCnt += 1; + installCnt -= 1; + } + else if (((file->isdir) == false) & (file->install == 2)) { + file->install = 0; + + uninstallCnt -= 1; + } + i++; + } + + } + else + { + fatFile *file = &fileList[selected]; + if (((file->isdir) == false) & (file->install == 0)) { + file->install = 2; + + uninstallCnt += 1; + } + else if (((file->isdir) == false) & (file->install == 1)) { + file->install = 2; + + uninstallCnt += 1; + installCnt -= 1; + } + else if (((file->isdir) == false) & (file->install == 2)) { + file->install = 0; + + uninstallCnt -= 1; + } + selected++; + + if (selected >= fileCnt) + selected = 0; + } + } + + /* 1 Button - Leathl */ + if (buttons & WPAD_BUTTON_1) + { + fatFile *tmpFile = &fileList[selected]; + char *tmpCurPath = PeekCurrentDir (); + if (tmpCurPath != NULL) { + int res = Menu_FileOperations(tmpFile, tmpCurPath); + if (res != 0) + goto getList; + } + } + + + /* A button */ + if (buttons & WPAD_BUTTON_A) + { + fatFile *tmpFile = &fileList[selected]; + char *tmpCurPath; + if (tmpFile->isdir) // wiiNinja + { + if (strcmp (tmpFile->filename, "..") == 0) + { + selected = 0; + start = 0; + + // Previous dir + tmpCurPath = PopCurrentDir(&selected, &start); + if (tmpCurPath != NULL) + sprintf(tmpPath, "%s", tmpCurPath); + + installCnt = 0; + uninstallCnt = 0; + + goto getList; + } + else if (IsListFull () == true) + { + WaitPrompt ("Maximum number of directory levels is reached.\n"); + } + else + { + tmpCurPath = PeekCurrentDir (); + if (tmpCurPath != NULL) + { + if(gDirLevel>1) + sprintf(tmpPath, "%s/%s", tmpCurPath, tmpFile->filename); + else + sprintf(tmpPath, "%s%s", tmpCurPath, tmpFile->filename); + } + // wiiNinja: Need to PopCurrentDir + PushCurrentDir (tmpPath, selected, start); + selected = 0; + start = 0; + + installCnt = 0; + uninstallCnt = 0; + + goto getList; + } + } + else + { + //If at least one WAD is marked, goto batch screen - Leathl + if ((installCnt > 0) | (uninstallCnt > 0)) { + char *thisCurPath = PeekCurrentDir (); + if (thisCurPath != NULL) { + int res = Menu_BatchProcessWads(fileList, fileCnt, thisCurPath, installCnt, uninstallCnt); + + if (res == 1) { + int counter; + for (counter = 0; counter < fileCnt; counter++) { + fatFile *temp = &fileList[counter]; + temp->install = 0; + } + + installCnt = 0; + uninstallCnt = 0; + } + } + } + //else use standard wadmanage menu - Leathl + else { + tmpCurPath = PeekCurrentDir (); + if (tmpCurPath != NULL) + Menu_WadManage(tmpFile, tmpCurPath); + } + } + } + + /* B button */ + if (buttons & WPAD_BUTTON_B) + { + if(gDirLevel<=1) + { + return; + } + + char *tmpCurPath; + selected = 0; + start = 0; + // Previous dir + tmpCurPath = PopCurrentDir(&selected, &start); + if (tmpCurPath != NULL) + sprintf(tmpPath, "%s", tmpCurPath); + goto getList; + //return; + } + + /** Scrolling **/ + /* List scrolling */ + index = (selected - start); + + if (index >= ENTRIES_PER_PAGE) + start += index - (ENTRIES_PER_PAGE - 1); + if (index <= -1) + start += index; + } + +err: + printf("\n"); + printf(" Press any button to continue...\n"); + + free (tmpPath); + + /* Wait for button */ + WaitButtons(); +} + + +void Menu_Loop(void) +{ + u8 iosVersion; + if(AHBPROT_DISABLED) + IOSPATCH_Apply(); + else + { + /* Select IOS menu */ + Menu_SelectIOS(); + } + + /* Retrieve IOS version */ + iosVersion = IOS_GetVersion(); + + ndev = &ndevList[0]; + + /* NAND device menu */ + if ((iosVersion == CIOS_VERSION || iosVersion == 250) && IOS_GetRevision() >13) + { + Menu_NandDevice(); + } + for (;;) { + /* FAT device menu */ + Menu_FatDevice(); + + /* WAD list menu */ + Menu_WadList(); + } +} + +// Start of wiiNinja's added routines + +int PushCurrentDir (char *dirStr, int Selected, int Start) +{ + int retval = 0; + + // Store dirStr into the list and increment the gDirLevel + // WARNING: Make sure dirStr is no larger than MAX_FILE_PATH_LEN + if (gDirLevel < MAX_DIR_LEVELS) + { + strcpy (gDirList [gDirLevel], dirStr); + gSeleted[gDirLevel]=Selected; + gStart[gDirLevel]=Start; + gDirLevel++; + //if (gDirLevel >= MAX_DIR_LEVELS) + // gDirLevel = 0; + } + else + retval = -1; + + return (retval); +} + +char *PopCurrentDir(int *Selected, int *Start) +{ + if (gDirLevel > 1) + gDirLevel--; + else { + gDirLevel = 0; + } + *Selected = gSeleted[gDirLevel]; + *Start = gStart[gDirLevel]; + return PeekCurrentDir(); +} + +bool IsListFull (void) +{ + if (gDirLevel < MAX_DIR_LEVELS) + return (false); + else + return (true); +} + +char *PeekCurrentDir (void) +{ + // Return the current path + if (gDirLevel > 0) + return (gDirList [gDirLevel-1]); + else + return (NULL); +} + +void WaitPrompt (char *prompt) +{ + printf("\n%s", prompt); + printf(" Press any button to continue...\n"); + + /* Wait for button */ + WaitButtons(); +} + +u32 Pad_GetButtons(void) +{ + u32 buttons = 0, cnt; + + /* Scan pads */ + PAD_ScanPads(); + + /* Get pressed buttons */ + //for (cnt = 0; cnt < MAX_WIIMOTES; cnt++) + for (cnt = 0; cnt < 4; cnt++) + buttons |= PAD_ButtonsDown(cnt); + + return buttons; +} + +u32 WiiDRC_GetButtons(void) +{ + if(!WiiDRC_Inited() || !WiiDRC_Connected()) + return 0; + + /* Scan pads */ + WiiDRC_ScanPads(); + + /* Get pressed buttons */ + return WiiDRC_ButtonsDown(); +} + +// Routine to wait for a button from either the Wiimote or a gamecube +// controller. The return value will mimic the WPAD buttons to minimize +// the amount of changes to the original code, that is expecting only +// Wiimote button presses. Note that the "HOME" button on the Wiimote +// is mapped to the "SELECT" button on the Gamecube Ctrl. (wiiNinja 5/15/2009) +u32 WaitButtons(void) +{ + u32 buttons = 0; + u32 buttonsGC = 0; + u32 buttonsDRC = 0; + + /* Wait for button pressing */ + while (!buttons && !buttonsGC && !buttonsDRC) + { + // Wii buttons + buttons = Wpad_GetButtons(); + + // GC buttons + buttonsGC = Pad_GetButtons(); + + // DRC buttons + buttonsDRC = WiiDRC_GetButtons(); + + VIDEO_WaitVSync(); + } + + if(buttons & WPAD_CLASSIC_BUTTON_A) + buttons |= WPAD_BUTTON_A; + else if(buttons & WPAD_CLASSIC_BUTTON_B) + buttons |= WPAD_BUTTON_B; + else if(buttons & WPAD_CLASSIC_BUTTON_LEFT) + buttons |= WPAD_BUTTON_LEFT; + else if(buttons & WPAD_CLASSIC_BUTTON_RIGHT) + buttons |= WPAD_BUTTON_RIGHT; + else if(buttons & WPAD_CLASSIC_BUTTON_DOWN) + buttons |= WPAD_BUTTON_DOWN; + else if(buttons & WPAD_CLASSIC_BUTTON_UP) + buttons |= WPAD_BUTTON_UP; + else if(buttons & WPAD_CLASSIC_BUTTON_HOME) + buttons |= WPAD_BUTTON_HOME; + else if(buttons & (WPAD_CLASSIC_BUTTON_X | WPAD_CLASSIC_BUTTON_PLUS)) + buttons |= WPAD_BUTTON_PLUS; + else if(buttons & (WPAD_CLASSIC_BUTTON_Y | WPAD_CLASSIC_BUTTON_MINUS)) + buttons |= WPAD_BUTTON_MINUS; + else if(buttons & WPAD_CLASSIC_BUTTON_ZR) + buttons |= WPAD_BUTTON_1; + + if (buttonsGC) + { + if(buttonsGC & PAD_BUTTON_A) + buttons |= WPAD_BUTTON_A; + else if(buttonsGC & PAD_BUTTON_B) + buttons |= WPAD_BUTTON_B; + else if(buttonsGC & PAD_BUTTON_LEFT) + buttons |= WPAD_BUTTON_LEFT; + else if(buttonsGC & PAD_BUTTON_RIGHT) + buttons |= WPAD_BUTTON_RIGHT; + else if(buttonsGC & PAD_BUTTON_DOWN) + buttons |= WPAD_BUTTON_DOWN; + else if(buttonsGC & PAD_BUTTON_UP) + buttons |= WPAD_BUTTON_UP; + else if(buttonsGC & PAD_BUTTON_START) + buttons |= WPAD_BUTTON_HOME; + else if(buttonsGC & PAD_BUTTON_X) + buttons |= WPAD_BUTTON_PLUS; + else if(buttonsGC & PAD_BUTTON_Y) + buttons |= WPAD_BUTTON_MINUS; + else if(buttonsGC & PAD_TRIGGER_Z) + buttons |= WPAD_BUTTON_1; + } + + if (buttonsDRC) + { + if(buttonsDRC & WIIDRC_BUTTON_A) + buttons |= WPAD_BUTTON_A; + else if(buttonsDRC & WIIDRC_BUTTON_B) + buttons |= WPAD_BUTTON_B; + else if(buttonsDRC & WIIDRC_BUTTON_LEFT) + buttons |= WPAD_BUTTON_LEFT; + else if(buttonsDRC & WIIDRC_BUTTON_RIGHT) + buttons |= WPAD_BUTTON_RIGHT; + else if(buttonsDRC & WIIDRC_BUTTON_DOWN) + buttons |= WPAD_BUTTON_DOWN; + else if(buttonsDRC & WIIDRC_BUTTON_UP) + buttons |= WPAD_BUTTON_UP; + else if(buttonsDRC & WIIDRC_BUTTON_HOME) + buttons |= WPAD_BUTTON_HOME; + else if(buttonsDRC & (WIIDRC_BUTTON_X | WIIDRC_BUTTON_PLUS)) + buttons |= WPAD_BUTTON_PLUS; + else if(buttonsDRC & (WIIDRC_BUTTON_Y | WIIDRC_BUTTON_MINUS)) + buttons |= WPAD_BUTTON_MINUS; + else if(buttonsDRC & WIIDRC_BUTTON_ZR) + buttons |= WPAD_BUTTON_1; + } + + return buttons; +} // WaitButtons + + +void WiiLightControl (int state) +{ + switch (state) + { + case WII_LIGHT_ON: + /* Turn on Wii Light */ + WIILIGHT_SetLevel(255); + WIILIGHT_TurnOn(); + break; + + case WII_LIGHT_OFF: + default: + /* Turn off Wii Light */ + WIILIGHT_SetLevel(0); + WIILIGHT_TurnOn(); + WIILIGHT_Toggle(); + break; + } +} // WiiLightControl + diff --git a/data/appboot.bin b/data/appboot.bin index d8712d215441d0e5b52d0ed2be053eb6b76d4067..5adac459423dac4432305c19396a35d0625a020f 100644 GIT binary patch literal 182208 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

(bF{UfXUO<@_zJND=d1Zsy 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ÍPrk2D6E4r5>|eKXA9vMwHe=_n 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 diff --git a/source/appboot.c b/source/appboot.c index 25f5c0a..fac9f41 100644 --- a/source/appboot.c +++ b/source/appboot.c @@ -4,11 +4,15 @@ #include #include #include +#include #include +#include "appboot.h" #include "fat.h" #include "sys.h" #include "appmetadata.h" +#include "iospatch.h" +#include "video.h" extern void __exception_closeall(); @@ -20,35 +24,83 @@ u32 metaSize = 0; u8* appBuffer = NULL; u32 appSize = 0; - -typedef void (*entrypoint)(); u32 appEntry = 0; +u32 appIos = 0; + #include "appboot_bin.h" -static void Jump(entrypoint EntryPoint) -{ - appEntry = (u32)EntryPoint; +#define MEM2PROT 0x0D8B420A +#define ESMODULESTART (u16*)0x939F0000 - u32 level = IRQ_Disable(); - __IOS_ShutdownSubsystems(); - __exception_closeall(); - __lwp_thread_closeall(); - asm volatile ( - "lis %r3, appEntry@h\n" - "ori %r3, %r3, appEntry@l\n" - "lwz %r3, 0(%r3)\n" - "mtlr %r3\n" - "blr\n" - ); - IRQ_Restore(level); +static const u16 ticket[] = { + 0x685B, // ldr r3,[r3,#4] ; get TMD pointer + 0x22EC, 0x0052, // movls r2, 0x1D8 + 0x189B, // adds r3, r3, r2; add offset of access rights field in TMD + 0x681B, // ldr r3, [r3] ; load access rights (haxxme!) + 0x4698, // mov r8, r3 ; store it for the DVD video bitcheck later + 0x07DB // lsls r3, r3, #31; check AHBPROT bit +}; + +static bool patchahbprot(void) +{ + u16* patch; + + if ((read32(0x0D800064) == 0xFFFFFFFF) ? 1 : 0) + { + write16(MEM2PROT, 2); + for (patch = ESMODULESTART; patch < ESMODULESTART + 0x4000; ++patch) { + if (!memcmp(patch, ticket, sizeof(ticket))) + { + patch[4] = 0x23FF; + DCFlushRange(patch + 4, 2); + return 0; + } + } + return -1; + } + else { + return -2; + } } bool LoadApp(const char* path) { + Con_Clear(); + appBuffer = (u8*)0x92000000; char currentPath[256]; + snprintf(currentPath, sizeof(currentPath), "%s/meta.xml", path); + u16 argumentsSize = 0; + char* Arguments = LoadArguments(currentPath, &argumentsSize); + + if (Arguments) + { + *(vu32*)0x91000000 = argumentsSize; + memcpy((void*)0x91000020, Arguments, argumentsSize); + DCFlushRange((void*)0x91000020, argumentsSize); + ICInvalidateRange((void*)0x91000020, argumentsSize); + free(Arguments); + } + else + { + *(vu32*)0x91000000 = 0; + } + + struct MetaData* appData = LoadMetaData(currentPath); + + if (appData) + { + printf("-> App title: %s version %s\n", appData->name, appData->version); + printf("-> Coder(s): %s\n", appData->coder); + if (appData->releaseDate != NULL) + printf("-> Release date: %s\n", appData->releaseDate); + FreeMetaData(appData); + + printf("\n"); + } + snprintf(currentPath, sizeof(currentPath), "%s/boot.dol", path); FILE* f = fopen(currentPath, "rb"); @@ -62,6 +114,8 @@ bool LoadApp(const char* path) return false; } + printf("-> Load: %s\n", currentPath); + fseek(f, 0, SEEK_END); appSize = ftell(f); rewind(f); @@ -73,22 +127,21 @@ bool LoadApp(const char* path) } u32 ret = fread(appBuffer, 1, appSize, f); - DCFlushRange(appBuffer, (appSize + 31) & (~31)); - - fclose(f); - - snprintf(currentPath, sizeof(currentPath), "%s/meta.xml", path); - u16 argumentsSize = 0; - char* Arguments = LoadArguments(currentPath, &argumentsSize); - - if (Arguments) + if (ret != appSize) { - *(vu32*)0x91000000 = argumentsSize; - memcpy((void*)0x91000020, Arguments, argumentsSize); - DCFlushRange((u8*)0x91000020, argumentsSize); - free(Arguments); + printf("Failed to read file: %s (0x%X -> 0x%X)\n", currentPath, ret, appSize ); + fclose(f); + return false; + } + else + { + printf("-> App size: 0x%X\n\n", appSize); } + DCFlushRange(appBuffer, appSize); + ICInvalidateRange(appBuffer, appSize); + + fclose(f); return (ret == appSize); } @@ -99,13 +152,74 @@ u8* GetApp(u32* size) } void LaunchApp(void) -{ - entrypoint entry; +{ + entrypoint entry = NULL; + + LoadBooter(&entry); + + if (!appIos) + appIos = IOS_GetPreferredVersion(); + + if (AHBPROT_DISABLED) + { + if (appIos > 0 && appIos < 200) + { + printf("-> Patch IOS for AHB access\n"); + patchahbprot(); + } + } - memcpy((u8*)0x93000000, appboot_bin, appboot_bin_size); - DCFlushRange((u8*)0x93000000, appboot_bin_size); - entry = (entrypoint)0x93000000; + if (appIos > 0) + __IOS_LaunchNewIOS(appIos); - Jump(entry); + if (AHBPROT_DISABLED) + { + printf("-> Reenable DVD access\n"); + mask32(0x0D800180, 1 << 21, 0); + } + + printf("-> And we're outta here!\n"); + + *(vu32*)0x800000F8 = 0x0E7BE2C0; // Bus Speed + *(vu32*)0x800000FC = 0x2B73A840; // CPU Speed + + //SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); + __exception_closeall(); + entry(); + + printf("--> Well.. this shouldn't happen\n"); Sys_LoadMenu(); -} \ No newline at end of file +} + +void SetIos(int ios) +{ + appIos = ios; +} + +bool LoadBooter(entrypoint* entry) +{ + dolhdr* dol = (dolhdr*)appboot_bin; + + u32 i; + for (i = 0; i < 7; i++) + { + if (dol->sizeText[i] == 0 || dol->addressText[i] < 0x100) + continue; + + memmove((void*)dol->addressText[i], appboot_bin + dol->offsetText[i], dol->sizeText[i]); + DCFlushRange((void*)dol->addressText[i], dol->sizeText[i]); + } + + for (i = 0; i < 11; i++) + { + if (dol->sizeData[i] == 0) + continue; + + memmove((void*)dol->addressData[i], appboot_bin + dol->offsetData[i], dol->sizeData[i]); + DCFlushRange((void*)dol->addressData[i], dol->sizeData[i]); + } + + *entry = (entrypoint)dol->entrypoint; + + return true; +} diff --git a/source/appboot.h b/source/appboot.h index b5808af..212bc26 100644 --- a/source/appboot.h +++ b/source/appboot.h @@ -1,8 +1,25 @@ #ifndef __APPBOOT_H__ #define __APPBOOT_H__ +typedef void (*entrypoint)(); + +typedef struct _dolhdr +{ + u32 offsetText[7]; + u32 offsetData[11]; + u32 addressText[7]; + u32 addressData[11]; + u32 sizeText[7]; + u32 sizeData[11]; + u32 addressBSS; + u32 sizeBSS; + u32 entrypoint; +} dolhdr; + bool LoadApp(const char* path); u8* GetApp(u32* size); void LaunchApp(void); +void SetIos(int ios); +bool LoadBooter(entrypoint* entry); #endif \ No newline at end of file diff --git a/source/appmetadata.c b/source/appmetadata.c index ad74a52..1d5e4a6 100644 --- a/source/appmetadata.c +++ b/source/appmetadata.c @@ -74,14 +74,21 @@ struct MetaData* LoadMetaData(const char* path) metaData->shortDescription = strdup(GetStringValue(app, "short_description")); metaData->longDescription = strdup(GetStringValue(app, "long_description")); - char release[40]; + char release[20]; memset(release, 0, sizeof(release)); snprintf(release, sizeof(release), "%s", GetStringValue(app, "release_date")); - + + metaData->releaseDate = NULL; if (strlen(release) == 14) + { snprintf(release, sizeof(release), "%c%c/%c%c/%c%c%c%c", release[4], release[5], release[6], release[7], release[0], release[1], release[2], release[3]); - else if (strlen(release) == 14) + metaData->releaseDate = strdup(release); + } + else if (strlen(release) == 12) + { snprintf(release, sizeof(release), "%c%c/%c%c%c%c", release[4], release[5], release[0], release[1], release[2], release[3]); + metaData->releaseDate = strdup(release); + } metaData->releaseDate = strdup(release); mxmlDelete(meta); @@ -122,9 +129,9 @@ char* LoadArguments(const char* path, u16* length) return NULL; } - app = mxmlFindElement(app, app, "arguments", NULL, NULL, MXML_DESCEND_FIRST); + mxml_node_t* arguments = mxmlFindElement(app, app, "arguments", NULL, NULL, MXML_DESCEND_FIRST); - if (!app) + if (!arguments) { mxmlDelete(meta); return NULL; @@ -133,7 +140,7 @@ char* LoadArguments(const char* path, u16* length) mxml_node_t* arg; u16 size = 0; - for (arg = mxmlFindElement(app, app, "arg", NULL, NULL, MXML_DESCEND_FIRST); arg != NULL; arg = mxmlFindElement(arg, app, "arg", NULL, NULL, MXML_NO_DESCEND)) + for (arg = mxmlFindElement(arguments, arguments, "arg", NULL, NULL, MXML_DESCEND_FIRST); arg != NULL; arg = mxmlFindElement(arg, arguments, "arg", NULL, NULL, MXML_NO_DESCEND)) { char* current = GetArgumentValue(arg); @@ -156,7 +163,7 @@ char* LoadArguments(const char* path, u16* length) char* argStr = malloc(size); size = 0; - for (arg = mxmlFindElement(app, app, "arg", NULL, NULL, MXML_DESCEND_FIRST); arg != NULL; arg = mxmlFindElement(arg, app, "arg", NULL, NULL, MXML_NO_DESCEND)) + for (arg = mxmlFindElement(arguments, arguments, "arg", NULL, NULL, MXML_DESCEND_FIRST); arg != NULL; arg = mxmlFindElement(arg, arguments, "arg", NULL, NULL, MXML_NO_DESCEND)) { char* current = GetArgumentValue(arg); @@ -172,6 +179,8 @@ char* LoadArguments(const char* path, u16* length) size += strlen(current); } } + + mxmlDelete(meta); argStr[size] = 0; *length = size; diff --git a/source/boot/Makefile b/source/boot/Makefile index 61bee48..954bd22 100644 --- a/source/boot/Makefile +++ b/source/boot/Makefile @@ -1,46 +1,152 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- -PREFIX = $(DEVKITPPC)/bin/powerpc-eabi- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif -AR = $(PREFIX)ar -AS = $(PREFIX)as -CC = $(PREFIX)gcc -CXX = $(PREFIX)g++ -LD = $(PREFIX)ld -OBJCOPY = $(PREFIX)objcopy -RANLIB = $(PREFIX)ranlib -STRIP = $(PREFIX)strip +include $(DEVKITPPC)/wii_rules -MACHDEP = -mcpu=750 -meabi -mhard-float -CFLAGS = $(MACHDEP) -O0 -s -Werror -Wall -fdata-sections -ffunction-sections -LDFLAGS = $(MACHDEP) -n -nostartfiles -nostdlib -Wl,--gc-sections,-T,openstub.ld -L. -ASFLAGS = -D_LANGUAGE_ASSEMBLY -DHW_RVL +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := appboot +BUILD := build +SOURCES := source +DATA := data +INCLUDES := -TARGET_LINKED = patcher.elf -TARGET = ../../data/appboot.bin +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- -CFILES = main.c utils.c loaddol.c loadelf.c -OBJS = main.o utils.o loaddol.o loadelf.o +CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) -DEPDIR = .deps +LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map -Wl,--section-start,.init=0x81330000 -LIBS = +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lwiiuse -lbte -lfat -logc -lm -all: $(TARGET) +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES), -iquote $(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + @mv $(OUTPUT).dol $(OUTPUT).bin + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +#--------------------------------------------------------------------------------- +clean: + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).bin + +#--------------------------------------------------------------------------------- +run: + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .jpg extension +#--------------------------------------------------------------------------------- +%.bin: %.elf + @echo "output ... $(TARGET).bin" + $(Q)$(OBJCOPY) -O binary $< $@ + +%.bin: %.dol + @echo "output ... $(TARGET).bin" + $(Q)$(OBJCOPY) -O binary $< $@ + +%.elf: link.ld $(OFILES) + @echo "linking ... $(TARGET).elf" + $(Q)$(CC) -n -T $^ $(LDFLAGS) -o $@ + +%.o: %.c + @echo "$@" + $(Q)$(CC) $(CFLAGS) -c $< -o $@ %.o: %.s - @$(CC) $(CFLAGS) $(DEFINES) $(ASFLAGS) -c $< -o $@ + @echo "$@" + $(Q)$(CC) $(CFLAGS) -c $< -o $@ -%.o: %.S - @$(CC) $(CFLAGS) $(DEFINES) $(ASFLAGS) -c $< -o $@ +-include $(DEPENDS) -%.o: %.c - @$(CC) $(CFLAGS) $(DEFINES) -c $< -o $@ - -$(TARGET_LINKED): $(OBJS) - @$(CC) -g -o $@ $(LDFLAGS) $(OBJS) $(LIBS) - -$(TARGET): $(TARGET_LINKED) - @$(OBJCOPY) -O binary -S $< $@ - -clean: - @-$(RM) -rf $(TARGET_LINKED) $(OBJS) $(DEPDIR) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/source/boot/loaddol.c b/source/boot/loaddol.c deleted file mode 100644 index 555621a..0000000 --- a/source/boot/loaddol.c +++ /dev/null @@ -1,32 +0,0 @@ - -#include "loaddol.h" - -static void memcopy(void* address, void* buffer, u32 size) -{ - _memcpy(address, buffer, size); - sync_after_write(address, (size + 31) & (~31)); -} - -u32 LoadDol(void* buffer) -{ - u32 i; - struct dolhdr* dol = (struct dolhdr*)buffer; - - for (i = 0; i < 7; i++) - { - if (dol->sizeText[i] == 0) - continue; - - memcopy((void*)dol->addressText[i], buffer + dol->offsetText[i], dol->sizeText[i]); - } - - for (i = 0; i < 11; i++) - { - if (dol->sizeData[i] == 0) - continue; - - memcopy((void*)dol->addressData[i], buffer + dol->offsetData[i], dol->sizeData[i]); - } - - return dol->entrypoint; -} \ No newline at end of file diff --git a/source/boot/loaddol.h b/source/boot/loaddol.h deleted file mode 100644 index 4a12007..0000000 --- a/source/boot/loaddol.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __DOL_H__ -#define __DOL_H__ - -#include "utils.h" - -struct dolhdr -{ - u32 offsetText[7]; - u32 offsetData[11]; - u32 addressText[7]; - u32 addressData[11]; - u32 sizeText[7]; - u32 sizeData[11]; - u32 addressBSS; - u32 sizeBSS; - u32 entrypoint; -}; - - -u32 LoadDol(void* buffer); - -#endif \ No newline at end of file diff --git a/source/boot/loadelf.c b/source/boot/loadelf.c deleted file mode 100644 index d1ea085..0000000 --- a/source/boot/loadelf.c +++ /dev/null @@ -1,47 +0,0 @@ - -#include "loadelf.h" - -bool ExecIsElf(void* address) -{ - struct Elf32_Ehdr* ehdr = (struct Elf32_Ehdr*)address; - - if (*(u8*)address + 0 != 0x7F || *(u8*)address + 1 != 'E' || *(u8*)address + 2 != 'L' || *(u8*)address + 3 != 'F') - return false; - - if (ehdr->e_type != 2) // Executable - return false; - - if (ehdr->e_machine != 20) // PowerPC - return false; - - return true; -} - -u32 LoadElf(void* address) -{ - int i; - - struct Elf32_Ehdr* ehdr = (struct Elf32_Ehdr*)address; - struct Elf32_Shdr* shdr = (struct Elf32_Shdr*)(address + ehdr->e_shoff + (ehdr->e_shstrndx * sizeof(struct Elf32_Shdr))); - - for (i = 0; i < ehdr->e_shnum; i++) - { - shdr = (struct Elf32_Shdr*)(address + ehdr->e_shoff + (i * sizeof(struct Elf32_Shdr))); - - if (!(shdr->sh_flags & 0x02) || shdr->sh_addr == 0 || shdr->sh_size == 0) - continue; - - shdr->sh_addr &= 0x3FFFFFFF; - shdr->sh_addr |= 0x80000000; - - if (shdr->sh_type == 8) - _memset32((void*)shdr->sh_addr, 0, shdr->sh_size); - else - _memcpy((void*)shdr->sh_addr, (void*)(address + shdr->sh_offset), shdr->sh_size); - - sync_after_write((void*)shdr->sh_addr, (shdr->sh_size + 31) & (~31)); - - } - - return (ehdr->e_entry & 0x3FFFFFFF) | 0x80000000; -} \ No newline at end of file diff --git a/source/boot/loadelf.h b/source/boot/loadelf.h deleted file mode 100644 index 22e6ef1..0000000 --- a/source/boot/loadelf.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef __ELF_H__ -#define __ELF_H__ - -#include "utils.h" - -#define EI_NIDENT 16 - -struct Elf32_Ehdr -{ - u8 e_ident[EI_NIDENT]; - u16 e_type; - u16 e_machine; - u32 e_version; - u32 e_entry; - u32 e_phoff; - u32 e_shoff; - u32 e_flags; - u16 e_ehsize; - u16 e_phentsize; - u16 e_phnum; - u16 e_shentsize; - u16 e_shnum; - u16 e_shstrndx; -}; - -struct Elf32_Shdr -{ - u32 sh_name; - u32 sh_type; - u32 sh_flags; - u32 sh_addr; - u32 sh_offset; - u32 sh_size; - u32 sh_link; - u32 sh_info; - u32 sh_addralign; - u32 sh_entsize; -}; - -struct Elf32_Phdr -{ - u32 p_type; - u32 p_offset; - u32 p_vaddr; - u32 p_paddr; - u32 p_filesz; - u32 p_memsz; - u32 p_flags; - u32 p_align; -}; - -bool ExecIsElf(void* address); -u32 LoadElf(void* address); - -#endif \ No newline at end of file diff --git a/source/boot/main.c b/source/boot/main.c deleted file mode 100644 index 9fb1c9e..0000000 --- a/source/boot/main.c +++ /dev/null @@ -1,40 +0,0 @@ - -#include "loaddol.h" -#include "loadelf.h" -#include "utils.h" - -typedef void (*entrypoint)(); - -void _main(void) -{ - void* buffer = (void*)0x92000000; - entrypoint entry; - - u32 argumentsSize = *(vu32*)0x91000000; - - if (ExecIsElf(buffer)) - entry = (entrypoint)LoadElf(buffer); - else - entry = (entrypoint)LoadDol(buffer); - - if (!entry) - return; - - if (argumentsSize > 0) - { - u32* ptr = (u32*)entry; - - if (ptr[1] == 0x5F617267) - { - struct Arguments* argv = (struct Arguments*)&ptr[2]; - - argv->magic = 0x5F617267; - argv->cmdLine = (char*)0x91000020; - argv->length = argumentsSize; - - sync_after_write(&ptr[2], 4); - } - } - - entry(); -} \ No newline at end of file diff --git a/source/boot/openstub.ld b/source/boot/openstub.ld deleted file mode 100644 index 165168b..0000000 --- a/source/boot/openstub.ld +++ /dev/null @@ -1,15 +0,0 @@ -/* - TinyLoad - a simple region free (original) game launcher in 4k - -# This code is licensed to you under the terms of the GNU GPL, version 2; -# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt -*/ - -OUTPUT_FORMAT("elf32-powerpc") -OUTPUT_ARCH(powerpc:common) - -ENTRY(_main) - -SECTIONS { - . = 0x93000000; -} \ No newline at end of file diff --git a/source/boot/source/crt0.s b/source/boot/source/crt0.s new file mode 100644 index 0000000..7a68d74 --- /dev/null +++ b/source/boot/source/crt0.s @@ -0,0 +1,22 @@ +# Copyright 2008-2009 Segher Boessenkool +# This code is licensed to you under the terms of the GNU GPL, version 2; +# see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +.extern _main + .globl _start +_start: + + # Disable interrupts, enable FP. + mfmsr 3 ; rlwinm 3,3,0,17,15 ; ori 3,3,0x2000 ; mtmsr 3 ; isync + + # Setup stack. + lis 1,_stack_top@ha ; addi 1,1,_stack_top@l ; li 0,0 ; stwu 0,-64(1) + + # Clear BSS. + lis 3,__bss_start@ha ; addi 3,3,__bss_start@l + li 4,0 + lis 5,__bss_end@ha ; addi 5,5,__bss_end@l ; sub 5,5,3 + bl _memset32 + + # Go! + bl _main diff --git a/source/boot/source/elf_abi.h b/source/boot/source/elf_abi.h new file mode 100644 index 0000000..4d763a1 --- /dev/null +++ b/source/boot/source/elf_abi.h @@ -0,0 +1,593 @@ +/* + * Copyright (c) 1995, 1996, 2001, 2002 + * Erik Theisen. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is the ELF ABI header file + * formerly known as "elf_abi.h". + */ + +#ifndef _ELF_ABI_H +#define _ELF_ABI_H + +#include + +/* + * This version doesn't work for 64-bit ABIs - Erik. + */ + +/* + * These typedefs need to be handled better. + */ +typedef u32 Elf32_Addr; /* Unsigned program address */ +typedef u32 Elf32_Off; /* Unsigned file offset */ +typedef s32 Elf32_Sword; /* Signed large integer */ +typedef u32 Elf32_Word; /* Unsigned large integer */ +typedef u16 Elf32_Half; /* Unsigned medium integer */ + +/* e_ident[] identification indexes */ +#define EI_MAG0 0 /* file ID */ +#define EI_MAG1 1 /* file ID */ +#define EI_MAG2 2 /* file ID */ +#define EI_MAG3 3 /* file ID */ +#define EI_CLASS 4 /* file class */ +#define EI_DATA 5 /* data encoding */ +#define EI_VERSION 6 /* ELF header version */ +#define EI_OSABI 7 /* OS/ABI specific ELF extensions */ +#define EI_ABIVERSION 8 /* ABI target version */ +#define EI_PAD 9 /* start of pad bytes */ +#define EI_NIDENT 16 /* Size of e_ident[] */ + +/* e_ident[] magic number */ +#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */ +#define ELFMAG1 'E' /* e_ident[EI_MAG1] */ +#define ELFMAG2 'L' /* e_ident[EI_MAG2] */ +#define ELFMAG3 'F' /* e_ident[EI_MAG3] */ +#define ELFMAG "\177ELF" /* magic */ +#define SELFMAG 4 /* size of magic */ + +/* e_ident[] file class */ +#define ELFCLASSNONE 0 /* invalid */ +#define ELFCLASS32 1 /* 32-bit objs */ +#define ELFCLASS64 2 /* 64-bit objs */ +#define ELFCLASSNUM 3 /* number of classes */ + +/* e_ident[] data encoding */ +#define ELFDATANONE 0 /* invalid */ +#define ELFDATA2LSB 1 /* Little-Endian */ +#define ELFDATA2MSB 2 /* Big-Endian */ +#define ELFDATANUM 3 /* number of data encode defines */ + +/* e_ident[] OS/ABI specific ELF extensions */ +#define ELFOSABI_NONE 0 /* No extension specified */ +#define ELFOSABI_HPUX 1 /* Hewlett-Packard HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD */ +#define ELFOSABI_LINUX 3 /* Linux */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris */ +#define ELFOSABI_AIX 7 /* AIX */ +#define ELFOSABI_IRIX 8 /* IRIX */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD */ +/* 64-255 Architecture-specific value range */ + +/* e_ident[] ABI Version */ +#define ELFABIVERSION 0 + +/* e_ident */ +#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ + (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ + (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ + (ehdr).e_ident[EI_MAG3] == ELFMAG3) + +/* ELF Header */ +typedef struct elfhdr{ + unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ + Elf32_Half e_type; /* object file type */ + Elf32_Half e_machine; /* machine */ + Elf32_Word e_version; /* object file version */ + Elf32_Addr e_entry; /* virtual entry point */ + Elf32_Off e_phoff; /* program header table offset */ + Elf32_Off e_shoff; /* section header table offset */ + Elf32_Word e_flags; /* processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size */ + Elf32_Half e_phentsize; /* program header entry size */ + Elf32_Half e_phnum; /* number of program header entries */ + Elf32_Half e_shentsize; /* section header entry size */ + Elf32_Half e_shnum; /* number of section header entries */ + Elf32_Half e_shstrndx; /* section header table's "section + header string table" entry offset */ +} Elf32_Ehdr; + +/* e_type */ +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* relocatable file */ +#define ET_EXEC 2 /* executable file */ +#define ET_DYN 3 /* shared object file */ +#define ET_CORE 4 /* core file */ +#define ET_NUM 5 /* number of types */ +#define ET_LOOS 0xfe00 /* reserved range for operating */ +#define ET_HIOS 0xfeff /* system specific e_type */ +#define ET_LOPROC 0xff00 /* reserved range for processor */ +#define ET_HIPROC 0xffff /* specific e_type */ + +/* e_machine */ +#define EM_NONE 0 /* No Machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola 68000 */ +#define EM_88K 5 /* Motorola 88000 */ +#if 0 +#define EM_486 6 /* RESERVED - was Intel 80486 */ +#endif +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */ +#define EM_S370 9 /* IBM System/370 Processor */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ +#if 0 +#define EM_SPARC64 11 /* RESERVED - was SPARC v9 + 64-bit unoffical */ +#endif +/* RESERVED 11-14 for future use */ +#define EM_PARISC 15 /* HPPA */ +/* RESERVED 16 for future use */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* 64-bit PowerPC */ +#define EM_S390 22 /* IBM System/390 Processor */ +/* RESERVED 23-35 for future use */ +#define EM_V800 36 /* NEC V800 */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* Advanced Risc Machines ARM */ +#define EM_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC Version 9 */ +#define EM_TRICORE 44 /* Siemens TriCore embedded processor */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_PDSP 63 /* Sony DSP Processor */ +/* RESERVED 64,65 for future use */ +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CHRIS 76 /* Axis Communications embedded proc. */ +#define EM_JAVELIN 77 /* Infineon Technologies emb. proc. */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's edu 64-bit proc. */ +#define EM_HUANY 81 /* Harvard University mach-indep objs */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi DV10V */ +#define EM_D30V 86 /* Mitsubishi DV30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10200 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_NUM 92 /* number of machine types */ + +/* Version */ +#define EV_NONE 0 /* Invalid */ +#define EV_CURRENT 1 /* Current */ +#define EV_NUM 2 /* number of versions */ + +/* Section Header */ +typedef struct { + Elf32_Word sh_name; /* name - index into section header + string table section */ + Elf32_Word sh_type; /* type */ + Elf32_Word sh_flags; /* flags */ + Elf32_Addr sh_addr; /* address */ + Elf32_Off sh_offset; /* file offset */ + Elf32_Word sh_size; /* section size */ + Elf32_Word sh_link; /* section header table index link */ + Elf32_Word sh_info; /* extra information */ + Elf32_Word sh_addralign; /* address alignment */ + Elf32_Word sh_entsize; /* section entry size */ +} Elf32_Shdr; + +/* Special Section Indexes */ +#define SHN_UNDEF 0 /* undefined */ +#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */ +#define SHN_LOPROC 0xff00 /* reserved range for processor */ +#define SHN_HIPROC 0xff1f /* specific section indexes */ +#define SHN_LOOS 0xff20 /* reserved range for operating */ +#define SHN_HIOS 0xff3f /* specific semantics */ +#define SHN_ABS 0xfff1 /* absolute value */ +#define SHN_COMMON 0xfff2 /* common symbol */ +#define SHN_XINDEX 0xffff /* Index is an extra table */ +#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */ + +/* sh_type */ +#define SHT_NULL 0 /* inactive */ +#define SHT_PROGBITS 1 /* program defined information */ +#define SHT_SYMTAB 2 /* symbol table section */ +#define SHT_STRTAB 3 /* string table section */ +#define SHT_RELA 4 /* relocation section with addends*/ +#define SHT_HASH 5 /* symbol hash table section */ +#define SHT_DYNAMIC 6 /* dynamic section */ +#define SHT_NOTE 7 /* note section */ +#define SHT_NOBITS 8 /* no space section */ +#define SHT_REL 9 /* relation section without addends */ +#define SHT_SHLIB 10 /* reserved - purpose unknown */ +#define SHT_DYNSYM 11 /* dynamic symbol table section */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* number of section types */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_HIOS 0x6fffffff /* End OS-specific */ +#define SHT_LOPROC 0x70000000 /* reserved range for processor */ +#define SHT_HIPROC 0x7fffffff /* specific section header types */ +#define SHT_LOUSER 0x80000000 /* reserved range for application */ +#define SHT_HIUSER 0xffffffff /* specific indexes */ + +/* Section names */ +#define ELF_BSS ".bss" /* uninitialized data */ +#define ELF_COMMENT ".comment" /* version control information */ +#define ELF_DATA ".data" /* initialized data */ +#define ELF_DATA1 ".data1" /* initialized data */ +#define ELF_DEBUG ".debug" /* debug */ +#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */ +#define ELF_DYNSTR ".dynstr" /* dynamic string table */ +#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */ +#define ELF_FINI ".fini" /* termination code */ +#define ELF_FINI_ARRAY ".fini_array" /* Array of destructors */ +#define ELF_GOT ".got" /* global offset table */ +#define ELF_HASH ".hash" /* symbol hash table */ +#define ELF_INIT ".init" /* initialization code */ +#define ELF_INIT_ARRAY ".init_array" /* Array of constuctors */ +#define ELF_INTERP ".interp" /* Pathname of program interpreter */ +#define ELF_LINE ".line" /* Symbolic line numnber information */ +#define ELF_NOTE ".note" /* Contains note section */ +#define ELF_PLT ".plt" /* Procedure linkage table */ +#define ELF_PREINIT_ARRAY ".preinit_array" /* Array of pre-constructors */ +#define ELF_REL_DATA ".rel.data" /* relocation data */ +#define ELF_REL_FINI ".rel.fini" /* relocation termination code */ +#define ELF_REL_INIT ".rel.init" /* relocation initialization code */ +#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */ +#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */ +#define ELF_REL_TEXT ".rel.text" /* relocation code */ +#define ELF_RODATA ".rodata" /* read-only data */ +#define ELF_RODATA1 ".rodata1" /* read-only data */ +#define ELF_SHSTRTAB ".shstrtab" /* section header string table */ +#define ELF_STRTAB ".strtab" /* string table */ +#define ELF_SYMTAB ".symtab" /* symbol table */ +#define ELF_SYMTAB_SHNDX ".symtab_shndx"/* symbol table section index */ +#define ELF_TBSS ".tbss" /* thread local uninit data */ +#define ELF_TDATA ".tdata" /* thread local init data */ +#define ELF_TDATA1 ".tdata1" /* thread local init data */ +#define ELF_TEXT ".text" /* code */ + +/* Section Attribute Flags - sh_flags */ +#define SHF_WRITE 0x1 /* Writable */ +#define SHF_ALLOC 0x2 /* occupies memory */ +#define SHF_EXECINSTR 0x4 /* executable */ +#define SHF_MERGE 0x10 /* Might be merged */ +#define SHF_STRINGS 0x20 /* Contains NULL terminated strings */ +#define SHF_INFO_LINK 0x40 /* sh_info contains SHT index */ +#define SHF_LINK_ORDER 0x80 /* Preserve order after combining*/ +#define SHF_OS_NONCONFORMING 0x100 /* Non-standard OS specific handling */ +#define SHF_GROUP 0x200 /* Member of section group */ +#define SHF_TLS 0x400 /* Thread local storage */ +#define SHF_MASKOS 0x0ff00000 /* OS specific */ +#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */ + /* specific section attributes */ + +/* Section Group Flags */ +#define GRP_COMDAT 0x1 /* COMDAT group */ +#define GRP_MASKOS 0x0ff00000 /* Mask OS specific flags */ +#define GRP_MASKPROC 0xf0000000 /* Mask processor specific flags */ + +/* Symbol Table Entry */ +typedef struct elf32_sym { + Elf32_Word st_name; /* name - index into string table */ + Elf32_Addr st_value; /* symbol value */ + Elf32_Word st_size; /* symbol size */ + unsigned char st_info; /* type and binding */ + unsigned char st_other; /* 0 - no defined meaning */ + Elf32_Half st_shndx; /* section header index */ +} Elf32_Sym; + +/* Symbol table index */ +#define STN_UNDEF 0 /* undefined */ + +/* Extract symbol info - st_info */ +#define ELF32_ST_BIND(x) ((x) >> 4) +#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf) +#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) +#define ELF32_ST_VISIBILITY(x) ((x) & 0x3) + +/* Symbol Binding - ELF32_ST_BIND - st_info */ +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* like global - lower precedence */ +#define STB_NUM 3 /* number of symbol bindings */ +#define STB_LOOS 10 /* reserved range for operating */ +#define STB_HIOS 12 /* system specific symbol bindings */ +#define STB_LOPROC 13 /* reserved range for processor */ +#define STB_HIPROC 15 /* specific symbol bindings */ + +/* Symbol type - ELF32_ST_TYPE - st_info */ +#define STT_NOTYPE 0 /* not specified */ +#define STT_OBJECT 1 /* data object */ +#define STT_FUNC 2 /* function */ +#define STT_SECTION 3 /* section */ +#define STT_FILE 4 /* file */ +#define STT_NUM 5 /* number of symbol types */ +#define STT_TLS 6 /* Thread local storage symbol */ +#define STT_LOOS 10 /* reserved range for operating */ +#define STT_HIOS 12 /* system specific symbol types */ +#define STT_LOPROC 13 /* reserved range for processor */ +#define STT_HIPROC 15 /* specific symbol types */ + +/* Symbol visibility - ELF32_ST_VISIBILITY - st_other */ +#define STV_DEFAULT 0 /* Normal visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Symbol unavailable in other mods */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation entry with implicit addend */ +typedef struct +{ + Elf32_Addr r_offset; /* offset of relocation */ + Elf32_Word r_info; /* symbol table index and type */ +} Elf32_Rel; + +/* Relocation entry with explicit addend */ +typedef struct +{ + Elf32_Addr r_offset; /* offset of relocation */ + Elf32_Word r_info; /* symbol table index and type */ + Elf32_Sword r_addend; +} Elf32_Rela; + +/* Extract relocation info - r_info */ +#define ELF32_R_SYM(i) ((i) >> 8) +#define ELF32_R_TYPE(i) ((unsigned char) (i)) +#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t)) + +/* Program Header */ +typedef struct { + Elf32_Word p_type; /* segment type */ + Elf32_Off p_offset; /* segment offset */ + Elf32_Addr p_vaddr; /* virtual address of segment */ + Elf32_Addr p_paddr; /* physical address - ignored? */ + Elf32_Word p_filesz; /* number of bytes in file for seg. */ + Elf32_Word p_memsz; /* number of bytes in mem. for seg. */ + Elf32_Word p_flags; /* flags */ + Elf32_Word p_align; /* memory alignment */ +} Elf32_Phdr; + +/* Segment types - p_type */ +#define PT_NULL 0 /* unused */ +#define PT_LOAD 1 /* loadable segment */ +#define PT_DYNAMIC 2 /* dynamic linking section */ +#define PT_INTERP 3 /* the RTLD */ +#define PT_NOTE 4 /* auxiliary information */ +#define PT_SHLIB 5 /* reserved - purpose undefined */ +#define PT_PHDR 6 /* program header */ +#define PT_TLS 7 /* Thread local storage template */ +#define PT_NUM 8 /* Number of segment types */ +#define PT_LOOS 0x60000000 /* reserved range for operating */ +#define PT_HIOS 0x6fffffff /* system specific segment types */ +#define PT_LOPROC 0x70000000 /* reserved range for processor */ +#define PT_HIPROC 0x7fffffff /* specific segment types */ + +/* Segment flags - p_flags */ +#define PF_X 0x1 /* Executable */ +#define PF_W 0x2 /* Writable */ +#define PF_R 0x4 /* Readable */ +#define PF_MASKOS 0x0ff00000 /* OS specific segment flags */ +#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */ + /* specific segment flags */ +/* Dynamic structure */ +typedef struct +{ + Elf32_Sword d_tag; /* controls meaning of d_val */ + union + { + Elf32_Word d_val; /* Multiple meanings - see d_tag */ + Elf32_Addr d_ptr; /* program virtual address */ + } d_un; +} Elf32_Dyn; + +extern Elf32_Dyn _DYNAMIC[]; + +/* Dynamic Array Tags - d_tag */ +#define DT_NULL 0 /* marks end of _DYNAMIC array */ +#define DT_NEEDED 1 /* string table offset of needed lib */ +#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */ +#define DT_PLTGOT 3 /* address PLT/GOT */ +#define DT_HASH 4 /* address of symbol hash table */ +#define DT_STRTAB 5 /* address of string table */ +#define DT_SYMTAB 6 /* address of symbol table */ +#define DT_RELA 7 /* address of relocation table */ +#define DT_RELASZ 8 /* size of relocation table */ +#define DT_RELAENT 9 /* size of relocation entry */ +#define DT_STRSZ 10 /* size of string table */ +#define DT_SYMENT 11 /* size of symbol table entry */ +#define DT_INIT 12 /* address of initialization func. */ +#define DT_FINI 13 /* address of termination function */ +#define DT_SONAME 14 /* string table offset of shared obj */ +#define DT_RPATH 15 /* string table offset of library + search path */ +#define DT_SYMBOLIC 16 /* start sym search in shared obj. */ +#define DT_REL 17 /* address of rel. tbl. w addends */ +#define DT_RELSZ 18 /* size of DT_REL relocation table */ +#define DT_RELENT 19 /* size of DT_REL relocation entry */ +#define DT_PLTREL 20 /* PLT referenced relocation entry */ +#define DT_DEBUG 21 /* bugger */ +#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */ +#define DT_JMPREL 23 /* add. of PLT's relocation entries */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used. */ +#define DT_LOOS 0x60000000 /* reserved range for OS */ +#define DT_HIOS 0x6fffffff /* specific dynamic array tags */ +#define DT_LOPROC 0x70000000 /* reserved range for processor */ +#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ + +/* Dynamic Tag Flags - d_un.d_val */ +#define DF_ORIGIN 0x01 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x02 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x04 /* Object contains text relocations */ +#define DF_BIND_NOW 0x08 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x10 /* Static thread local storage */ + +/* Standard ELF hashing function */ +unsigned long elf_hash(const unsigned char *name); + +#define ELF_TARG_VER 1 /* The ver for which this code is intended */ + +/* + * XXX - PowerPC defines really don't belong in here, + * but we'll put them in for simplicity. + */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 +/* Keep this the last entry. */ +#define R_PPC_NUM 37 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +#endif /* _ELF_H */ diff --git a/source/boot/source/link.ld b/source/boot/source/link.ld new file mode 100644 index 0000000..d144f55 --- /dev/null +++ b/source/boot/source/link.ld @@ -0,0 +1,27 @@ +/* Copyright 2008-2009 Segher Boessenkool + This code is licensed to you under the terms of the GNU GPL, version 2; + see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ + +OUTPUT_FORMAT("elf32-powerpc") +OUTPUT_ARCH(powerpc:common) + +ENTRY(_start) + +SECTIONS { + . = 0x93300000; + + .start : { crt0.o(*) } + .text : { *(.text) } + .rodata : { *(.rodata .rodata.*)} + .data : { *(.data) } + + __bss_start = .; + .bss : { *(.bss) } + __bss_end = .; + + . = ALIGN(0x40); + .stack : { + . += 0x8000; + _stack_top = .; + } +} diff --git a/source/boot/source/loader.c b/source/boot/source/loader.c new file mode 100644 index 0000000..46e7700 --- /dev/null +++ b/source/boot/source/loader.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include + +#include "loader.h" +#include "elf_abi.h" + +bool ExecIsElf(const u8* buffer) +{ + Elf32_Ehdr* ehdr = (Elf32_Ehdr*)buffer; + + if (!IS_ELF(*ehdr)) + return false; + + if (ehdr->e_ident[EI_CLASS] != ELFCLASS32) + return false; + + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) + return false; + + if (ehdr->e_ident[EI_VERSION] != EV_CURRENT) + return false; + + if (ehdr->e_type != ET_EXEC) + return false; + + if (ehdr->e_machine != EM_PPC) + return false; + + return true; +} + +bool LoadElf(entrypoint* entry, const u8* buffer) +{ + int i; + + Elf32_Ehdr* ehdr = (Elf32_Ehdr*)buffer; + + if (ehdr->e_phoff == 0 || ehdr->e_phnum == 0) + return false; + + if (ehdr->e_phentsize != (sizeof(Elf32_Phdr))) + return false; + + Elf32_Phdr* phdrs = (Elf32_Phdr*)(buffer + ehdr->e_phoff); + + for (i = 0; i < ehdr->e_phnum; i++) + { + if (phdrs[i].p_type == PT_LOAD) + { + phdrs[i].p_paddr &= 0x3FFFFFFF; + phdrs[i].p_paddr |= 0x80000000; + + if (phdrs[i].p_filesz > phdrs[i].p_memsz) + return false; + + if (phdrs[i].p_filesz) + { + memmove((void*)phdrs[i].p_paddr, (void*)(buffer + phdrs[i].p_offset), phdrs[i].p_filesz); + DCFlushRange((void*)phdrs[i].p_paddr, phdrs[i].p_memsz); + + if (phdrs[i].p_flags & PF_X) + ICInvalidateRange((void*)phdrs[i].p_paddr, phdrs[i].p_memsz); + } + } + } + + *entry = (entrypoint)((ehdr->e_entry & 0x3FFFFFFF) | 0x80000000); + return true; +} + +bool LoadDol(entrypoint* entry, const u8* buffer) +{ + u32 i; + dolhdr* dol = (dolhdr*)buffer; + + for (i = 0; i < 7; i++) + { + if (dol->sizeText[i] == 0 || dol->addressText[i] < 0x100) + continue; + + //printf(" Move text section %u @ 0x%08x -> 0x%08x (0x%0X bytes)\n", i, (u32)(buffer + dol->offsetText[i]), dol->addressText[i], dol->sizeText[i]); + memmove((void*)dol->addressText[i], buffer + dol->offsetText[i], dol->sizeText[i]); + DCFlushRange((void*)dol->addressText[i], dol->sizeText[i]); + ICInvalidateRange((void*)dol->addressText[i], dol->sizeText[i]); + + } + + for (i = 0; i < 11; i++) + { + if (dol->sizeData[i] == 0) + continue; + + //printf(" Move data section %u @ 0x%08x -> 0x%08x (0x%0X bytes)\n", i, (u32)(buffer + dol->offsetData[i]), dol->addressData[i], dol->sizeData[i]); + memmove((void*)dol->addressData[i], buffer + dol->offsetData[i], dol->sizeData[i]); + DCFlushRange((void*)dol->addressData[i], dol->sizeData[i]); + } + + *entry = (entrypoint)dol->entrypoint; + + return true; +} + diff --git a/source/boot/source/loader.h b/source/boot/source/loader.h new file mode 100644 index 0000000..88f75ab --- /dev/null +++ b/source/boot/source/loader.h @@ -0,0 +1,26 @@ +#ifndef _LOADER_H_ +#define _LOADER_H_ + +#include + +typedef void (*entrypoint) (void); + +typedef struct _dolhdr +{ + u32 offsetText[7]; + u32 offsetData[11]; + u32 addressText[7]; + u32 addressData[11]; + u32 sizeText[7]; + u32 sizeData[11]; + u32 addressBSS; + u32 sizeBSS; + u32 entrypoint; +} dolhdr; + +bool ExecIsElf(const u8* buffer); +bool LoadElf(entrypoint* entry, const u8* buffer); +bool LoadDol(entrypoint* entry, const u8* buffer); + + +#endif \ No newline at end of file diff --git a/source/boot/source/main.c b/source/boot/source/main.c new file mode 100644 index 0000000..43c02d4 --- /dev/null +++ b/source/boot/source/main.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "loader.h" + +extern void __exception_setreload(int t); +static void* xfb = NULL; +static GXRModeObj* rmode = NULL; + +struct Arguments +{ + int magic; + char* cmdLine; + int length; +}; + +void VideoInit(void) +{ + VIDEO_Init(); + rmode = VIDEO_GetPreferredMode(NULL); + xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); + VIDEO_Configure(rmode); + VIDEO_SetNextFramebuffer(xfb); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if(rmode->viTVMode&VI_NON_INTERLACE) + VIDEO_WaitVSync(); + + printf("\x1b[2;0H"); +} + +int main(void) +{ + VideoInit(); + + u8* buffer = (u8*)0x92000000; + entrypoint entry; + bool execLoaded = false; + + if (ExecIsElf(buffer)) + { + //printf(" Loading ELF @ address 0x%08X:\n\n", (u32)buffer); + execLoaded = LoadElf(&entry, buffer); + } + else + { + //printf(" Loading DOL @ address 0x%08X::\n\n", (u32)buffer); + execLoaded = LoadDol(&entry, buffer); + } + + if (!execLoaded) + return -1; + + u8* execPtr = (u8*)entry; + + if (execPtr[0x20] == 0x41) + { + execPtr[0x21] = 0x40; + DCFlushRange(&execPtr[0x20], 1); + } + + u32 argumentsSize = *(vu32*)0x91000000; + if (argumentsSize > 0) + { + u32* ptr = (u32*)entry; + + if (ptr[1] == 0x5F617267) + { + struct Arguments* argv = (struct Arguments*)&ptr[2]; + + argv->magic = 0x5F617267; + argv->cmdLine = (char*)0x91000020; + argv->length = argumentsSize; + + DCFlushRange(&ptr[2], 4); + } + } + + entry(); + return 0; +} diff --git a/source/boot/utils.c b/source/boot/utils.c deleted file mode 100644 index 05d3540..0000000 --- a/source/boot/utils.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "utils.h" - -void sync_before_read(void *ptr, u32 len) -{ - u32 a, b; - - a = (u32)ptr & ~0x1f; - b = ((u32)ptr + len + 0x1f) & ~0x1f; - - for ( ; a < b; a += 32) - asm("dcbi 0,%0" : : "b"(a) : "memory"); - - asm("sync ; isync"); -} - -void sync_after_write(const void *ptr, u32 len) -{ - u32 a, b; - - a = (u32)ptr & ~0x1f; - b = ((u32)ptr + len + 0x1f) & ~0x1f; - - for ( ; a < b; a += 32) - asm("dcbf 0,%0" : : "b"(a)); - - asm("sync ; isync"); -} - -void _memcpy(void *ptr, const void *src, u32 size) -{ - char *ptr2 = ptr; - u32 bsize = size; - const char* src2 = src; - while(size--) *ptr2++ = *src2++; - - sync_after_write(ptr, bsize); -} - -void _memset32(u32 *address, u32 data, u32 length) -{ - while(length--) - *address++ = data; -} - -int _memcmp(const void *s1, const void *s2, size_t n) -{ - unsigned char *us1 = (unsigned char *) s1; - unsigned char *us2 = (unsigned char *) s2; - while(n-- != 0) - { - if (*us1 != *us2) - return (*us1 < *us2) ? -1 : +1; - us1++; - us2++; - } - return 0; -} - -size_t strnlen(const char *s, size_t count) -{ - const char *sc; - - for(sc = s; count-- && *sc != '\0'; ++sc) - /* nothing */; - return sc - s; -} - -inline void write32(u32 dest, u32 value) -{ - *(u32*)dest = value; - sync_after_write((void*)dest, 0x20); -} - -inline u32 read32(u32 src) -{ - return *(u32*)src; -} \ No newline at end of file diff --git a/source/boot/utils.h b/source/boot/utils.h deleted file mode 100644 index bc6b61c..0000000 --- a/source/boot/utils.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef __UTILS_H__ -#define __UTILS_H__ - -typedef unsigned char u8; -typedef unsigned char uint8_t; -typedef unsigned short u16; -typedef unsigned short uint16_t; -typedef unsigned int u32; -typedef unsigned long long u64; - -typedef int bool; -typedef unsigned int sec_t; - -typedef signed char s8; -typedef signed short s16; -typedef signed int s32; -typedef signed long long s64; - -typedef volatile unsigned char vu8; -typedef volatile unsigned short vu16; -typedef volatile unsigned int vu32; -typedef volatile unsigned long long vu64; - -typedef volatile signed char vs8; -typedef volatile signed short vs16; -typedef volatile signed int vs32; -typedef volatile signed long long vs64; - -typedef s32 size_t; -typedef u32 u_int32_t; - - -struct Arguments -{ - int magic; - char* cmdLine; - int length; -}; - -#define NULL ((void*)0) -#define true 1 -#define false 0 -#define IsWiiU ((*(u32*)0xcd8005A0 >> 16 ) == 0xCAFE) - -void sync_before_read(void* ptr, u32 len); -void sync_after_write(const void* ptr, u32 len); -void _memcpy(void *ptr, const void *src, u32 size); -void _memset32(unsigned int *addr, unsigned int data, unsigned int count); -int _memcmp(const void *s1, const void *s2, size_t n); -size_t strnlen(const char *s, size_t count); -void write32(u32 value, u32 dest); -u32 read32(u32 src); - -#endif \ No newline at end of file