mini/powerpc_elf.c

228 lines
5.3 KiB
C
Raw Normal View History

/*
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
PowerPC ELF file loading
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
2009-04-13 22:13:45 +02:00
Copyright (C) 2009 Andre Heider "dhewg" <dhewg@wiibrew.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 2.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2008-12-28 14:35:37 +01:00
#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"
2009-03-31 19:36:07 +02:00
#include "memory.h"
2008-12-28 14:35:37 +01:00
#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)
2008-12-28 14:35:37 +01:00
#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];
2008-12-28 14:35:37 +01:00
int powerpc_boot_file(const char *path)
2008-12-28 14:35:37 +01:00
{
u32 read;
2009-04-13 20:22:32 +02:00
FIL fd;
2008-12-28 14:35:37 +01:00
FRESULT fres;
2009-04-13 20:22:32 +02:00
2008-12-28 14:35:37 +01:00
fres = f_open(&fd, path, FA_READ);
if (fres != FR_OK)
2008-12-28 14:35:37 +01:00
return -fres;
fres = f_read(&fd, &elfhdr, sizeof(elfhdr), &read);
if (fres != FR_OK)
2008-12-28 14:35:37 +01:00
return -fres;
if (read != sizeof(elfhdr))
2008-12-28 14:35:37 +01:00
return -100;
if (memcmp("\x7F" "ELF\x01\x02\x01\x00\x00",elfhdr.e_ident,9)) {
2008-12-28 14:35:37 +01:00
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);
2008-12-28 14:35:37 +01:00
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) {
2008-12-28 14:35:37 +01:00
gecko_printf("ELF has too many (%d) program headers!\n", elfhdr.e_phnum);
return -104;
2008-12-28 14:35:37 +01:00
}
2008-12-28 14:35:37 +01:00
fres = f_lseek(&fd, elfhdr.e_phoff);
if (fres != FR_OK)
2008-12-28 14:35:37 +01:00
return -fres;
2008-12-28 14:35:37 +01:00
fres = f_read(&fd, phdrs, sizeof(phdrs[0])*elfhdr.e_phnum, &read);
if (fres != FR_OK)
2008-12-28 14:35:37 +01:00
return -fres;
if (read != sizeof(phdrs[0])*elfhdr.e_phnum)
return -105;
u16 count = elfhdr.e_phnum;
2008-12-28 14:35:37 +01:00
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);
2008-12-28 14:35:37 +01:00
} 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);
2008-12-28 14:35:37 +01:00
fres = f_lseek(&fd, phdr->p_offset);
if (fres != FR_OK)
2008-12-28 14:35:37 +01:00
return -fres;
fres = f_read(&fd, dst, phdr->p_filesz, &read);
if (fres != FR_OK)
2008-12-28 14:35:37 +01:00
return -fres;
if (read != phdr->p_filesz)
return -107;
2008-12-28 14:35:37 +01:00
}
phdr++;
}
2009-03-31 19:36:07 +02:00
dc_flushall();
gecko_printf("ELF load done, booting PPC...\n");
powerpc_upload_stub(elfhdr.e_entry);
2008-12-28 14:35:37 +01:00
powerpc_reset();
gecko_printf("PPC booted!\n");
2008-12-28 14:35:37 +01:00
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",
2009-04-13 20:22:32 +02:00
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;
}
2009-04-13 20:22:32 +02:00