2009-04-10 18:15:10 +02:00
|
|
|
/*
|
|
|
|
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>
|
2009-04-10 18:15:10 +02:00
|
|
|
|
|
|
|
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"
|
|
|
|
|
2009-04-17 00:44:52 +02:00
|
|
|
extern u8 __mem2_area_start[];
|
2009-04-16 23:50:12 +02:00
|
|
|
|
|
|
|
#define PPC_MEM1_END (0x017fffff)
|
|
|
|
#define PPC_MEM2_START (0x10000000)
|
2009-04-17 00:44:52 +02:00
|
|
|
#define PPC_MEM2_END ((u32) __mem2_area_start)
|
2009-04-16 23:50:12 +02:00
|
|
|
|
2008-12-28 14:35:37 +01:00
|
|
|
#define PHDR_MAX 10
|
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-03-26 05:16:08 +01:00
|
|
|
static Elf32_Ehdr elfhdr;
|
|
|
|
static Elf32_Phdr phdrs[PHDR_MAX];
|
2008-12-28 14:35:37 +01:00
|
|
|
|
2009-04-08 16:30:32 +02: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);
|
2009-04-16 23:50:12 +02:00
|
|
|
if (fres != FR_OK)
|
2008-12-28 14:35:37 +01:00
|
|
|
return -fres;
|
|
|
|
|
|
|
|
fres = f_read(&fd, &elfhdr, sizeof(elfhdr), &read);
|
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
if (fres != FR_OK)
|
2008-12-28 14:35:37 +01:00
|
|
|
return -fres;
|
2009-04-16 23:50:12 +02:00
|
|
|
|
|
|
|
if (read != sizeof(elfhdr))
|
2008-12-28 14:35:37 +01:00
|
|
|
return -100;
|
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
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;
|
|
|
|
}
|
2009-04-16 23:50:12 +02:00
|
|
|
|
|
|
|
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);
|
2009-04-16 23:50:12 +02:00
|
|
|
return -104;
|
2008-12-28 14:35:37 +01:00
|
|
|
}
|
2009-04-16 23:50:12 +02:00
|
|
|
|
2008-12-28 14:35:37 +01:00
|
|
|
fres = f_lseek(&fd, elfhdr.e_phoff);
|
2009-04-16 23:50:12 +02:00
|
|
|
if (fres != FR_OK)
|
2008-12-28 14:35:37 +01:00
|
|
|
return -fres;
|
2009-04-08 16:30:32 +02:00
|
|
|
|
2008-12-28 14:35:37 +01:00
|
|
|
fres = f_read(&fd, phdrs, sizeof(phdrs[0])*elfhdr.e_phnum, &read);
|
2009-04-16 23:50:12 +02:00
|
|
|
if (fres != FR_OK)
|
2008-12-28 14:35:37 +01:00
|
|
|
return -fres;
|
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
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();
|
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
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 {
|
2009-04-16 23:50:12 +02:00
|
|
|
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;
|
2009-04-08 16:30:32 +02:00
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
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);
|
2009-04-16 23:50:12 +02:00
|
|
|
if (fres != FR_OK)
|
2008-12-28 14:35:37 +01:00
|
|
|
return -fres;
|
|
|
|
fres = f_read(&fd, dst, phdr->p_filesz, &read);
|
2009-04-16 23:50:12 +02:00
|
|
|
if (fres != FR_OK)
|
2008-12-28 14:35:37 +01:00
|
|
|
return -fres;
|
2009-04-16 23:50:12 +02:00
|
|
|
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();
|
|
|
|
|
2009-04-05 15:43:25 +02:00
|
|
|
gecko_printf("ELF load done, booting PPC...\n");
|
2009-04-07 23:23:58 +02:00
|
|
|
powerpc_upload_stub(elfhdr.e_entry);
|
2008-12-28 14:35:37 +01:00
|
|
|
powerpc_reset();
|
2009-04-05 15:43:25 +02:00
|
|
|
gecko_printf("PPC booted!\n");
|
2008-12-28 14:35:37 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-04-08 16:30:32 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
if (_check_physaddr(ehdr->e_entry) < 0) {
|
|
|
|
gecko_printf("Invalid entry point! 0x%08x\n", ehdr->e_entry);
|
|
|
|
return -102;
|
|
|
|
}
|
|
|
|
|
2009-04-08 16:30:32 +02:00
|
|
|
if (ehdr->e_phoff == 0 || ehdr->e_phnum == 0) {
|
|
|
|
gecko_printf("ELF has no program headers!\n");
|
2009-04-16 23:50:12 +02:00
|
|
|
return -103;
|
2009-04-08 16:30:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2009-04-16 23:50:12 +02:00
|
|
|
return -104;
|
2009-04-08 16:30:32 +02:00
|
|
|
}
|
|
|
|
|
2009-04-16 23:50:12 +02:00
|
|
|
u16 count = ehdr->e_phnum;
|
2009-04-08 16:30:32 +02:00
|
|
|
if (len < ehdr->e_phoff + count * sizeof(Elf32_Phdr))
|
2009-04-16 23:50:12 +02:00
|
|
|
return -105;
|
2009-04-08 16:30:32 +02:00
|
|
|
|
|
|
|
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 {
|
2009-04-16 23:50:12 +02:00
|
|
|
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);
|
2009-04-08 16:30:32 +02:00
|
|
|
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
|
|
|
|