bootmii/loader/main.c
2022-08-08 18:07:27 +09:00

391 lines
8.3 KiB
C

/*
bootmii - a Free Software replacement for the Nintendo/BroadOn boot2.
Main loader code
Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
Copyright (C) 2008, 2009 Sven Peter <sven@svenpeter.dev>
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcan.st>
Copyright (C) 2009 Andre Heider "dhewg" <dhewg@wiibrew.org>
Copyright (C) 2009 John Kelley <wiidev@kelley.ca>
# 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 "utils.h"
#include "start.h"
#include "hollywood.h"
#include "sdhc.h"
#include "string.h"
#include "memory.h"
#include "elf.h"
#include "gecko.h"
#include "ff.h"
#include "lcd.h"
#include "gpio.h"
#define BOOT_FILE "/bootmii/armboot.bin"
extern void *__end;
extern const char *bootmii_version;
static FATFS fatfs;
static void hexline(void *addr, int len)
{
u8 *p = (u8*)addr;
while(len--) {
gecko_printf("%02x",*p++);
}
}
static void printhdr(ioshdr *hdr) {
gecko_printf("ARMBOOT header (@%p):\n",hdr);
gecko_printf(" Header size: %08x\n", hdr->hdrsize);
gecko_printf(" Loader size: %08x\n", hdr->loadersize);
gecko_printf(" ELF size: %08x\n", hdr->elfsize);
gecko_printf(" Argument: %08x\n", hdr->argument);
gecko_printf(" ELF at %p\n", (u8 *) hdr + hdr->hdrsize + hdr->loadersize);
}
static void loadelf(u8 *elf) {
gecko_puts("Loading ELF...\n");
if(memcmp("\x7F" "ELF\x01\x02\x01\x61\x01",elf,9)) {
gecko_printf("Invalid ELF header:\n");
hexline(elf, 9);
gecko_printf("\n");
panic(0xE3);
}
Elf32_Ehdr *ehdr = (Elf32_Ehdr*)elf;
if(ehdr->e_phoff == 0) {
gecko_printf("ELF has no program headers!\n");
panic(0xE4);
}
int count = ehdr->e_phnum;
Elf32_Phdr *phdr = (Elf32_Phdr*)(elf + ehdr->e_phoff);
gecko_printf("PHDRS at %p\n",phdr);
while(count--)
{
if(phdr->p_type != PT_LOAD) {
gecko_printf(" Skipping PHDR of type %d\n",phdr->p_type);
} else {
void *src = elf + phdr->p_offset;
gecko_printf(" LOAD %p -> %x [0x%x]\n",src, phdr->p_paddr, phdr->p_filesz);
memcpy((void *)phdr->p_paddr, src, phdr->p_filesz);
}
phdr++;
}
}
static void turn_stuff_on(void)
{
clear32(HW_GPIO1OUT, 0x10);
udelay(100);
set32(HW_RESETS, 0x7ffffcf);
}
static void reset_audio(void)
{
mask32(HW_DIFLAGS, 0x180, 0x100);
clear32(0xd8001d0, 0x80000000);
udelay(2);
clear32(0xd8001d0, 0x40000000);
clear32(0xd8001d0, 0x10000000);
mask32(0xd8001cc, 0x7ffffff, 0x4b0ffce);
udelay(10);
set32(0xd8001d0, 0x40000000);
udelay(500);
set32(0xd8001d0, 0x80000000);
udelay(2);
}
static void boot2_init1()
{
//func_ffff5d08
u32 reg = read32(0xd8001c8);
if((reg & 0xc0000000) == 0xc0000000)
return;
clear32(0xd8001c8, 0x80000000);
udelay(2);
clear32(0xd8001c8, 0x40000000);
udelay(10);
set32(0xd8001c8, 0x40000000);
udelay(50);
set32(0xd8001c8, 0x80000000);
udelay(2);
}
static void boot2_init2(u32 hlywdVerHi)
{
//func_ffff5c40
write32(0xd800088, 0xFE);
udelay(2);
clear32(0xd8001d8, 0x80000000);
udelay(2);
clear32(0xd8001d8, 0x40000000);
udelay(10);
set32(0xd8001d8, 0x40000000);
udelay(50);
set32(0xd8001d8, 0x80000000);
udelay(2);
write32(0xd800088, 0xF6);
udelay(50);
write32(0xd800088, 0xF4);
udelay(1);
write32(0xd800088, 0xF0);
udelay(1);
write32(0xd800088, 0x70);
udelay(1);
write32(0xd800088, 0x60);
udelay(1);
write32(0xd800088, 0x40);
udelay(1);
write32(0xd800088, 0);
udelay(1);
write32(0xd0400b4, 0x2214);
if (hlywdVerHi)
write32(0xd0400b0, 0x20600);
else
write32(0xd0400b0, 0x20400);
write32(0xd0400a4, 0x26);
udelay(1);
write32(0xd0400a4, 0x2026);
udelay(1);
write32(0xd0400a4, 0x4026);
udelay(20);
write32(0xd0400cc, 0x111);
}
static void setup_gpios()
{
write32(HW_GPIO1OWNER, GP_OWNER_PPC);
mask32(HW_GPIO1OUT, GP_ARM_OUTPUTS, GP_ARM_DEFAULT_ON);
write32(HW_GPIO1DIR, GP_ARM_OUTPUTS);
mask32(HW_GPIO1BOUT, GP_PPC_OUTPUTS, GP_PPC_DEFAULT_ON);
write32(HW_GPIO1BDIR, GP_PPC_OUTPUTS);
write32(HW_GPIO1ENABLE, GP_ALL);
udelay(2000);
write32(HW_GPIO1INTLVL, 0);
write32(HW_GPIO1INTFLAG, GP_ALL);
write32(HW_GPIO1INTENABLE, 0);
write32(HW_GPIO1BINTLVL, 0);
write32(HW_GPIO1BINTFLAG, GP_ALL);
write32(HW_GPIO1BINTENABLE, 0);
}
static void hardware_setup(void)
{
u8 hwood_ver, hwood_hi, hwood_lo;
hwood_ver = read32(HW_VERSION) & 0xFF;
hwood_hi = hwood_ver >> 4;
hwood_lo = hwood_ver & 0xF;
debug_output(0x03);
set32(HW_EXICTRL, EXICTRL_ENABLE_EXI);
mem_protect(1, (void*)0x13420000, (void*)0x1fffffff);
clear32(HW_EXICTRL, 0x10);
if(hwood_hi == 0)
write32(0xd8b0010, 0);
write32(0xd8b0010, 0);
if(hwood_hi == 1 && hwood_lo == 0)
mask32(0xd800140, 0x0000FFF0, 1);
set32(0xd80018c, 0x400);
set32(0xd80018c, 0x800);
reset_audio();
boot2_init1();
boot2_init2(hwood_hi);
setup_gpios();
turn_stuff_on();
write32(0xd8001e0, 0x65244A);
write32(0xd8001e4, 0x46A024);
debug_output(0x73);
clear32(HW_GPIO1OWNER, 0x10);
set32(HW_GPIO1DIR, 0x10);
}
void *_main(void *base)
{
FRESULT fres;
ioshdr *hdr = (ioshdr*)base;
ioshdr *filehdr;
u8 *elf;
u32 bypass = 0;
elf = (u8*) base;
elf += hdr->hdrsize + hdr->loadersize;
debug_output(0xF0);
lcd_init();
hardware_setup();
debug_output(0xF1);
if (read32(0x150effc) == 0x53544655)
write32(0x150effc, 0);
else
gecko_init();
lcd_puts("BootMii\n");
lcd_puts("\n");
gecko_puts("\n\nBootMii v");
gecko_puts(bootmii_version);
gecko_puts("\n");
gecko_puts("Copyright (C) 2008-2010 Team Twiizers.\n");
gecko_puts("Licensed under the GNU GPL version 2.\n");
printhdr(hdr);
if(hdr->argument == 0x42) {
bypass = 1;
goto boot2;
}
if (read32(0x150f000) == 0x424d454d) {
gecko_puts("Trying to boot ELF from memory\n");
filehdr = (ioshdr *) read32(0x150f004);
write32(0x150f000, 0);
write32(0x150f004, 0);
printhdr(filehdr);
void *entry = (void*)(((u32)filehdr) + filehdr->hdrsize);
lcd_puts(" ~ ARMBOOT \n");
gecko_printf("Launching ARMBOOT @ %p\n\n",entry);
debug_output(0xF3);
return entry;
}
gecko_puts("Trying to mount SD...\n");
sdhc_init();
fres = f_mount(0, &fatfs);
if(fres != FR_OK) {
gecko_printf("Error %d while trying to mount SD\n", fres);
debug_output(0x12);
debug_output(fres&0xFF);
goto boot2;
}
//debug_output(0xF2);
gecko_puts("Trying to open SD:" BOOT_FILE "\n");
FIL fd;
u32 read;
fres = f_open(&fd, BOOT_FILE, FA_READ);
if(fres != FR_OK) {
gecko_printf("Error %d while trying to open file\n", fres);
//debug_output(0x13);
//debug_output(fres&0xFF);
goto boot2;
}
lcd_puts(".");
debug_output(0xF2);
filehdr = (ioshdr *)ALIGN_FORWARD(((u32)&__end) + 0x100, 0x100);
gecko_printf("Trying to read ARMBOOT header to %p\n", filehdr);
fres = f_read(&fd, filehdr, sizeof(ioshdr), &read);
if(fres != FR_OK) {
gecko_printf("Error %d while trying to read file header\n", fres);
//debug_output(0x14);
//debug_output(fres&0xFF);
goto boot2;
}
if(read != sizeof(ioshdr)) {
gecko_printf("Got %d bytes, expected %d\n", read, sizeof(ioshdr));
//debug_output(0x24);
goto boot2;
}
lcd_puts(".");
//debug_output(0xF5);
printhdr(filehdr);
u32 totalsize = filehdr->hdrsize + filehdr->loadersize + filehdr->elfsize;
gecko_printf("Total ARMBOOT size: 0x%x\n", totalsize);
gecko_printf("Trying to read ARMBOOT to %p\n", filehdr);
fres = f_read(&fd, filehdr+1, totalsize-sizeof(ioshdr), &read);
if(fres != FR_OK) {
gecko_printf("Error %d while trying to read file header\n", fres);
//debug_output(0x15);
//debug_output(fres&0xFF);
goto boot2;
}
if(read != (totalsize-sizeof(ioshdr))) {
gecko_printf("Got %d bytes, expected %d\n", read, (totalsize-sizeof(ioshdr)));
//debug_output(0x25);
goto boot2;
}
lcd_puts(".");
//debug_output(0xF6);
gecko_puts("ARMBOOT read\n");
void *entry = (void*)(((u32)filehdr) + filehdr->hdrsize);
lcd_puts(" ~ ARMBOOT \n");
gecko_printf("Launching ARMBOOT @ %p\n\n",entry);
debug_output(0xF3);
return entry;
boot2:
debug_output(0xC8);
if(bypass) {
gecko_puts("In bypass mode, loading boot2\n");
} else {
gecko_puts("Couldn't load from SD, falling back to boot2\n");
set32(HW_GPIO1BOUT, GP_SLOTLED);
udelay(30000);
clear32(HW_GPIO1BOUT, GP_SLOTLED);
udelay(50000);
set32(HW_GPIO1BOUT, GP_SLOTLED);
udelay(30000);
clear32(HW_GPIO1BOUT, GP_SLOTLED);
udelay(50000);
}
lcd_puts(" ~ BOOT2 \n");
loadelf(elf);
debug_output(0xC9);
gecko_puts("Starting boot2...\n\n");
return (void *) 0xFFFF0000;
}