/* mini - a Free Software replacement for the Nintendo/BroadOn IOS. PowerPC ELF file loading Copyright (C) 2008, 2009 Hector Martin "marcan" Copyright (C) 2009 Andre Heider "dhewg" # 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 */ #include "types.h" #include "powerpc.h" #include "hollywood.h" #include "utils.h" #include "start.h" #include "gecko.h" #include "ff.h" #include "powerpc_elf.h" #include "elf.h" #include "memory.h" #include "string.h" extern u8 __mem2_area_start[]; #define PPC_MEM1_END (0x017fffff) #define PPC_MEM2_START (0x10000000) #define PPC_MEM2_END ((u32) __mem2_area_start) #define PHDR_MAX 10 static int _check_physaddr(u32 addr) { if ((addr >= PPC_MEM2_START) && (addr <= PPC_MEM2_END)) return 2; if (addr < PPC_MEM1_END) return 1; return -1; } static int _check_physrange(u32 addr, u32 len) { switch (_check_physaddr(addr)) { case 1: if ((addr + len) < PPC_MEM1_END) return 1; break; case 2: if ((addr + len) < PPC_MEM2_END) return 2; break; } return -1; } static Elf32_Ehdr elfhdr; static Elf32_Phdr phdrs[PHDR_MAX]; int powerpc_boot_file(const char *path) { u32 read; FIL fd; FRESULT fres; fres = f_open(&fd, path, FA_READ); if (fres != FR_OK) return -fres; fres = f_read(&fd, &elfhdr, sizeof(elfhdr), &read); if (fres != FR_OK) return -fres; if (read != sizeof(elfhdr)) return -100; if (memcmp("\x7F" "ELF\x01\x02\x01\x00\x00",elfhdr.e_ident,9)) { gecko_printf("Invalid ELF header! 0x%02x 0x%02x 0x%02x 0x%02x\n",elfhdr.e_ident[0], elfhdr.e_ident[1], elfhdr.e_ident[2], elfhdr.e_ident[3]); return -101; } if (_check_physaddr(elfhdr.e_entry) < 0) { gecko_printf("Invalid entry point! 0x%08x\n", elfhdr.e_entry); return -102; } if (elfhdr.e_phoff == 0 || elfhdr.e_phnum == 0) { gecko_printf("ELF has no program headers!\n"); return -103; } if (elfhdr.e_phnum > PHDR_MAX) { gecko_printf("ELF has too many (%d) program headers!\n", elfhdr.e_phnum); return -104; } fres = f_lseek(&fd, elfhdr.e_phoff); if (fres != FR_OK) return -fres; fres = f_read(&fd, phdrs, sizeof(phdrs[0])*elfhdr.e_phnum, &read); if (fres != FR_OK) return -fres; if (read != sizeof(phdrs[0])*elfhdr.e_phnum) return -105; u16 count = elfhdr.e_phnum; Elf32_Phdr *phdr = phdrs; powerpc_hang(); while (count--) { if (phdr->p_type != PT_LOAD) { gecko_printf("Skipping PHDR of type %d\n", phdr->p_type); } else { if (_check_physrange(phdr->p_paddr, phdr->p_memsz) < 0) { gecko_printf("PHDR out of bounds [0x%08x...0x%08x]\n", phdr->p_paddr, phdr->p_paddr + phdr->p_memsz); return -106; } void *dst = (void *) phdr->p_paddr; gecko_printf("LOAD 0x%x @0x%08x [0x%x]\n", phdr->p_offset, phdr->p_paddr, phdr->p_filesz); fres = f_lseek(&fd, phdr->p_offset); if (fres != FR_OK) return -fres; fres = f_read(&fd, dst, phdr->p_filesz, &read); if (fres != FR_OK) return -fres; if (read != phdr->p_filesz) return -107; } phdr++; } dc_flushall(); gecko_printf("ELF load done, booting PPC...\n"); powerpc_upload_stub(elfhdr.e_entry); powerpc_reset(); gecko_printf("PPC booted!\n"); return 0; } int powerpc_boot_mem(const u8 *addr, u32 len) { if (len < sizeof(Elf32_Ehdr)) return -100; Elf32_Ehdr *ehdr = (Elf32_Ehdr *) addr; if (memcmp("\x7F" "ELF\x01\x02\x01\x00\x00", ehdr->e_ident, 9)) { gecko_printf("Invalid ELF header! 0x%02x 0x%02x 0x%02x 0x%02x\n", ehdr->e_ident[0], ehdr->e_ident[1], ehdr->e_ident[2], ehdr->e_ident[3]); return -101; } if (_check_physaddr(ehdr->e_entry) < 0) { gecko_printf("Invalid entry point! 0x%08x\n", ehdr->e_entry); return -102; } if (ehdr->e_phoff == 0 || ehdr->e_phnum == 0) { gecko_printf("ELF has no program headers!\n"); return -103; } if (ehdr->e_phnum > PHDR_MAX) { gecko_printf("ELF has too many (%d) program headers!\n", ehdr->e_phnum); return -104; } u16 count = ehdr->e_phnum; if (len < ehdr->e_phoff + count * sizeof(Elf32_Phdr)) return -105; Elf32_Phdr *phdr = (Elf32_Phdr *) &addr[ehdr->e_phoff]; // TODO: add more checks here // - loaded ELF overwrites itself? powerpc_hang(); while (count--) { if (phdr->p_type != PT_LOAD) { gecko_printf("Skipping PHDR of type %d\n", phdr->p_type); } else { if (_check_physrange(phdr->p_paddr, phdr->p_memsz) < 0) { gecko_printf("PHDR out of bounds [0x%08x...0x%08x]\n", phdr->p_paddr, phdr->p_paddr + phdr->p_memsz); return -106; } gecko_printf("LOAD 0x%x @0x%08x [0x%x]\n", phdr->p_offset, phdr->p_paddr, phdr->p_filesz); memcpy((void *) phdr->p_paddr, &addr[phdr->p_offset], phdr->p_filesz); } phdr++; } dc_flushall(); gecko_printf("ELF load done, booting PPC...\n"); powerpc_upload_stub(ehdr->e_entry); powerpc_reset(); gecko_printf("PPC booted!\n"); return 0; }