commit 4b65f3b500b5ffe777f74d7d654aa64cb312c3cf Author: bushing Date: Mon Jun 20 16:11:10 2011 -0700 initial commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1191303 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +include starlet.mk + +CFLAGS = -fpic -mbig-endian -mthumb -march=armv5t -mthumb-interwork -fomit-frame-pointer -Os +LDFLAGS += -fpic -mbig-endian -mthumb -march=armv5t -mthumb-interwork -fomit-frame-pointer +LDSCRIPT = babelfish.ld +LIBS = -lgcc + +TARGET = babelfish.elf +TARGET_BIN = babelfish.bin +DATA = vectors.bin.o +OBJS = start.o babelfish.o utils.o gecko.o memory.o memory_asm.o $(DATA) + +include common.mk + +all: $(TARGET_BIN) + +babelfish.o: babelfish.c vectors.bin.o + +%.bin.o: %.bin + @$(bin2o) + +# vectors contains our SWI replacement that outputs debug stuff over USBGecko +vectors.bin: vectors.elf + $(OBJCOPY) -O binary vectors.elf vectors.bin + +vectors.elf: vectors.o + $(LD) -EB -Ttext=0 -o vectors.elf vectors.o + +vectors.o: vectors.s + $(AS) -o vectors.o vectors.s + + +$(TARGET_BIN): $(TARGET) + @echo " OBJCPY $@" + @$(OBJCOPY) -O binary $< $@ + +clean: myclean + +myclean: + -rm -f $(TARGET_BIN) vectors.elf vectors.bin vectors_bin.h vectors.o babelfish.map diff --git a/README b/README new file mode 100644 index 0000000..15f86cf --- /dev/null +++ b/README @@ -0,0 +1,96 @@ +This is babelfish, a just-in-time self-propagting IOS patcher for the Nintendo Wii. + +Background: + +The Wii poses a peculiar architectural challenge when it comes to +modifying firmware. Instead of having one firmware binary to patch, +the Wii keeps 20+ slightly incompatible versions of IOS around in +NAND; it reloads the firmware entirely (often to a different version) +when a new title is launched. This makes it difficult to permanently +modify the behavior (e.g. preventing a bootloader update) because +firmware is frequently reloaded. + +The most naive approach to solving this is to patch every single +version of the firmware in NAND; this is done by e.g. CIOSCORP. This +approach is unacceptable for two reasons: + +1) Any failure in patching could either leave a system unbootable (and +bricked), or could fail to apply the desired protection + +2) Any update will wipe out some of the patched firmwares. It's +possible to disable firmware updates by various methods, but it's +still ugly. + +A better solution was necessary, so I started working on a piece of +code that could stay resident between IOS reloads -- it would have to +transfer itself along with each new version of IOS. "IOS Virus" was +the first term that came to mind, but that has a negative connotation; +I chose "Babelfish" to express the benevolent intent of this software. (See +http://en.wikipedia.org/wiki/Babel_fish_(The_Hitchhiker%27s_Guide_to_the_Galaxy)) + +Theory of operation: +In order for this to work, we need two things: +1) An ability to consistently patch any IOS +2) An ability to patch any IOS when it gets loaded + +To simplify this task, a few assumptions have been made: + +1) All versions of IOS have an ELF loader in the beginning of the + image, which we may safely replace with our own loader + +2) All versions of IOS have a predictable syscall structure, which + allows us to hook the right events + +3) There is one safe, unused area of memory that we can "hide" a copy + of ourself without getting overwritten when IOS is reloaded + +4) We can make patches that are generic enough that they work on all + versions of IOS. + +As long as these are all true, then we can implement our code as a +replacement ELF loader; our loader also has the capability to patch +binaries as it loads them. One of the patches it does is ... to hook +something* in the IOS reload path to replace the new IOS's loader with +a stashed copy of ourself before jumping to it. + +The other patches it makes are just there to make life more +interesting; the most useful one is a patch to SVC call #4 to redirect +the normally /dev/nulled IOS debug output to USBGecko. + +Usage: + +After compiling Babelfish, you will end up with a babelfish.bin, which +is a replacement ELF loader that can be used with any version of IOS, +including boot2. One easy way to use it is to take boot2v4, replace +the ELF loader with babelfish and use BootMii to load it as +armboot.bin. After that, if all works well, it will stay resident in +memory until the system is powered down. + +Known limitations and bugs: + +1) This code is somewhat old, and was written before we found + HW_AHBPROT; some of this may no longer be necessary. + +2) There are several fragile points highlighted in the code, such as + the maximum code size / stack size, reload_ios/xchange_osvers, + syscall numbering, etc. + +3) PPC patching "almost" works. + +4) It won't survive a reboot to GameCube mode or through BootMii. + +5) You probably have to have a USBGecko to get any use out of this. + +6) There are several different patching strategies employed; it might + be nice to clean up and/or refactor that code. + +Safety notes: + +Despite the Rube-Goldberg patching mechanism involved here, Babelfish +should pratically always be safer for your Wii than patching firmware +in flash. The only dangerous scenario I can think of is that any +patches you make to the FFS driver may cause NAND to be corrupted in a +way that "vanilla" IOS doesn't undestand, which will cause IOS to +break if Babelfish fails to load. Just be extra careful when touching +any code that deals with NAND unless you have BootMii/boot2 installed +with a recent NAND flash backup.bin. diff --git a/babelfish.c b/babelfish.c new file mode 100644 index 0000000..c7a16b2 --- /dev/null +++ b/babelfish.c @@ -0,0 +1,729 @@ +/* +babelfish - self-propagating Just-In-Time IOS patcher + +Copyright (C) 2008-2011 Haxx Enterprises + +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 + +This code lives at http://gitweb.bootmii.org/?p=babelfish.git +*/ + +#include "types.h" +#include "utils.h" +#include "hollywood.h" +#include "elf.h" +#include "gecko.h" +#include "vectors_bin.h" +#define DEBUG 1 + + +/* Known issues: + MEMHOLE_ADDR was picked by looking at the ELF headers for a few versions of IOS + some spot that wasn't used; it would be better to programmatically determine + this while reloading IOS, and move it around as necessary. + + PPC patching doesn't work, see below. Half of the code here is for PPC patching, + it'd sure be nice if it worked (or we should get rid of it if we can't fix it) + + syscall numbering might change between IOS versions, need to check this + + Need to hook two different places for patching, depending on the IOS version -- + we should either find a better place, or automatically choose the correct one + + We freak out if we try to reload MINI. + + Big chunks of this code could be cleaned up and/or refactored, probably. +*/ + + +/* CONFIG OPTIONS */ +#define USE_RELOAD_IOS /* as opposed to hooking xchange_osvers -- only works on newer versions of IOS? */ + +/* XXX this shouldn't be hardcoded, and if you change it it must also change in start.S and babelfish.ld! */ +/* This is where we save a copy of ourselves to use when we reload into a new IOS */ +#define MEMHOLE_ADDR 0x13A80000 + +/* We should be able to use this framework to patch PPC code, but I can't get it to work. + Either it thinks it's patched code (but the PPC doesn't see the updated code), or it hangs the PPC. + I spent a couple of weeks trying to get this to work, but failed -- + I'd really like it if someone fixed this. :) +*/ +// #define PPCHAX +/* END CONFIG OPTIONS */ + +#ifdef PPCHAX // save space if we're not going to try to patch PPC code + +/* This is a patch to __fwrite in the Nintendo SDK to redirect all output to USBGecko */ +static u32 fwrite_patch[] = { + 0x7c8429d6, 0x39400000, 0x9421fff0, 0x93e1000c, 0x7f8a2000, 0x409c0064, + 0x3d00cd00, 0x3d60cd00, 0x3d20cd00, 0x61086814, 0x616b6824, 0x61296820, + 0x398000d0, 0x38c00019, 0x38e00000, 0x91880000, 0x7c0350ae, 0x5400a016, + 0x6400b000, 0x900b0000, 0x90c90000, 0x80090000, 0x701f0001, 0x4082fff8, + 0x800b0000, 0x90e80000, 0x540037fe, 0x7d4a0214, 0x7f8a2000, 0x419cffc8, + 0x7ca32b78, 0x83e1000c, 0x38210010, 0x4e800020 +}; + +static u32 sig_fwrite[] = { + 0x9421FFD0, + 0x7C0802A6, + 0x90010034, + 0xBF210014, + 0x7C9B2378, + 0x7CDC3378, + 0x7C7A1B78, + 0x7CB92B78 +}; +#endif +// keep track of how many times we think we patched the code +static u32 fwrite_count = 0; + +/* these are the syscalls we want to hook, either for debug spew or to actually change behavior */ +// FIXME -- can these change between IOS versions? +#define SYSCALL_NEW_THREAD 0x0 +#define SYSCALL_OPEN 0x1c +#define SYSCALL_PPCBOOT 0x41 +#define SYSCALL_ARMBOOT 0x42 +#define SYSCALL_LOADELF 0x5a + +#ifdef DEBUG +#define dprintf printf +#else +void dprintf(char *fmt, ...) {} +#endif + +/* these are defined in start.s */ +extern void dc_flush(void); +extern s32 irq_kill(void); +extern void disable_icache_dcache_mmu(void); +extern void jump_to_r0(u32 *target); +extern void debug_output(u8 byte); + +void do_kd_patch(u8 *buffer, u32 size); +void * find_stuff_EXI_stub(void); +u32 * find_ppcreset(void); +void find_powerpc_reset(void); +void replace_ios_loader(u32 *buffer); + +extern u32 __got_start, __got_end, _start, __bss_end; + +// we don't actually use this struct, but we probably should :( +typedef struct { + u32 hdrsize; + u32 loadersize; + u32 elfsize; + u32 argument; +} ioshdr; + +/* these are the function pointers we will use to hook IOS syscalls - + they will point at the real (old) IOS syscall handlers. Make a new type + for each syscall you hook */ + +typedef u32(*new_thread_func)(u32 (*proc)(void* arg), u8 priority, u32* stack, u32 stacksize, void* arg, u8 autostart); +new_thread_func new_thread = NULL; + +typedef s32(*device_open_func)(char *, s32); +device_open_func device_open = NULL; + +typedef s32(*ppcboot_func)(char *); +ppcboot_func ppcboot = NULL; + +typedef s32(*loadelf_func)(char *); +loadelf_func loadelf = NULL; + +typedef s32(*armboot_func)(char *, u32, u32); +armboot_func armboot = NULL; + +typedef void(*reload_ios_func)(u32 *, u32); +reload_ios_func reload_ios = NULL; + +typedef void(*stuff_EXI_stub_func)(u32, u32 *, u32); +stuff_EXI_stub_func stuff_EXI_stub = NULL; + +typedef void(*powerpc_reset_func)(void); +powerpc_reset_func powerpc_reset = NULL; + +/* wrapper functions for hooked syscalls */ +u32 new_thread_wrapper(u32 (*proc)(void* arg), u8 priority, u32* stack, u32 stacksize, void* arg, u8 autostart) { + s32 retval; + char buf[128]; + + /* gross hack -- on later modular IOSes, we can't patch KD when it's loaded + because only the kernel gets loaded by our ELF loader/patcher -- so instead, + we wait until the module is actually started to do our patch */ + if ((((u32)proc) >> 16) == 0x13db) do_kd_patch((u8 *)0x13db0000, 0x57000); + retval = new_thread(proc, priority, stack, stacksize, arg, autostart); + printf("new_thread(%x,%d,%x,%x,%x,%x)=%d\n", (u32)proc, priority, (u32)stack, stacksize, (u32)arg, autostart, retval); + return retval; +} + +s32 device_open_wrapper(char *filename, s32 mode) { + s32 retval; + char buf[128]; + /* This is really just for debug spew */ + retval = device_open(filename, mode); + printf("open(%s,%d)=%d\n", filename, mode, retval); + return retval; +} + +/* it would be nice to figure out how to do patching of the loaded PPC content here */ +s32 ppcboot_wrapper(char *filename) { + s32 retval; + + printf("ppcboot(%s)\n",filename); + + retval = ppcboot(filename); + + printf("ppcboot returned %d fwrite_count=%d\n", retval,fwrite_count); + return retval; +} + +/* This is called by modular IOS's kernel to load each module from NAND -- could probably + do patching here instead of in new_thread, but you have to figure out whether or not + this is the right module to patch somehow */ +s32 loadelf_wrapper(char *filename) { + s32 retval; + + printf("loadelf(%s)\n", filename); + + retval = loadelf(filename); + + printf("loadelf returned %d\n", retval); + return retval; +} + +// mostly just for logging at this point, but maybe would be better to do patches here? +s32 armboot_wrapper(char *filename, u32 r1, u32 filesize) { + s32 retval; + + printf("armboot(%s, %x, %x)\n", filename, r1, filesize); + + retval = armboot(filename, r1, filesize); + printf("armboot returned %d\n", retval); + + return retval; +} + + +/* here is where the magic happens -- note that this is really just reimplementing most of reload_ios + from IOS, because there was no easy way to just hook it */ + +/* option 1 */ +/* here is where the magic happens -- note that this is really just reimplementing most of reload_ios + from IOS, because there was no easy way to just hook it. This option is better than option 2, + because we don't need to hardcode the value of buffer; IIRC, this code is not present on early versions of IOS. */ +#ifdef USE_RELOAD_IOS +void reload_ios_wrapper(u32 *buffer, u32 version) { + printf("reload_ios(%x, %x)\n",(u32)buffer, version); + + irq_kill(); + set32(HW_ARMIRQMASK, 0); + + replace_ios_loader(buffer); + + printf("Here goes nothing...\n"); +/* magic pokes from reload_ios() */ + dc_flush(); + disable_icache_dcache_mmu(); + write32(0x3118, 0x04000000); + write32(0x311C, 0x04000000); + write32(0x3120, 0x93400000); + write32(0x3124, 0x90000800); + write32(0x3128, 0x933E0000); + write32(0x3130, 0x933E0000); + write32(0x3134, 0x93400000); + write32(0x3140, version); + write32(0x3148, 0x93400000); + write32(0x314C, 0x93420000); + write32(0x0d010018, 1); + jump_to_r0(buffer); + printf("wtf am I here\n"); // should not be reached + panic(0x14); +} +#else // !USE_RELOAD_IOS +/* option 2 */ +/* we really need to figure out how to find the buffer we need at runtime if we intend on using this */ +u32 xchange_osvers_and_patch(u32 version) { + u32 *buffer = (u32 *)0x10100000; // safe to hardcode? NO -- + printf("xchange_osvers_and_patch(%x)\n", version); + + u32 oldvers = read32(0x3140); + if (version && version != oldvers) { + printf("! Set OS version to %u !\n", version); + write32(0x3140, version); +// dc_flush(); + } + + replace_ios_loader(buffer); + + printf("! Returning old OS version %x !\n", oldvers); +// jump_to_r0(buffer); // I don't remember why I tried this, but this would immediately execute the new IOS + return oldvers; +} +#endif + +void replace_ios_loader(u32 *buffer) { + printf("new hdr length = %x\n",buffer[0]); + printf("new ELF offset = %x\n",buffer[1]); + printf("new ELF length = %x\n",buffer[2]); + printf("new param = %x\n", buffer[3]); + + u32 *me = (u32 *)MEMHOLE_ADDR; + + printf("our hdr length = %x\n",me[0]); + printf("our ELF offset = %x\n",me[1]); + printf("our ELF length = %x\n",me[2]); + printf("our param = %x\n", me[3]); + + /* problem: when IOS is loaded from NAND, it's 3 parts: IOS header, ELF loader, ELF + we want to overwrite the ELF loader with our own code, but it's probably bigger + than the existing IOS ELF loader + solution: move the ELF file over in memory and modify the IOS header to give ourselves enough + space to copy in babelfish as the new ELF loader */ + + // buffer[1] = ELF loader size -- we use this to detect if we already patched ourselves into this or whatever + if (buffer[1] > me[1]) { // should never happen, but can happen with e.g. MINI + printf("wtf, their loader is bigger than ours :(\n"); + panic(0x40); + } + if (buffer[1] == me[1]) { + printf("already using our loader, nothing to hax\n"); + } else { // buffer[1] < me[1] + u32 *old_elf_start = buffer + (buffer[1] + buffer[0])/4; + u32 *new_elf_start = buffer + (me[1] + me[0])/4; + printf("moving %x bytes from %x to %x\n", buffer[2], (u32)old_elf_start, (u32)new_elf_start); + + u32 i; + // copy in backwards order because these buffers overlap + memcpyr(new_elf_start, old_elf_start, buffer[2]); + + printf("move complete\n"); + printf("copying in loader from %x to %x\n",(u32)&me[4],(u32)&buffer[4]); + + memcpyr(&buffer[4], &me[4], me[1]); + printf("done\n"); + + // copy over ELF offset and size + buffer[1] = me[1]; + buffer[2] = me[2]; + printf("pwnt\n"); + } +} + +#ifdef PPCHAX +/* This code is a mess because I was flailing around trying to get PPC patching + to work, sorry ... this code *doesn't* work. */ +void stuff_EXI_stub_wrapper(u32 which_stub, u32 *insns, u32 len) { + printf("stuff_EXI_stub(%x, %x, %x)\n", which_stub, (u32) insns, len); + printf("Looking for fwrite\n"); + u32 addr; + u32 *mem1 = (u32 *)0x1330000; + printf("81330000: %08x %08x %08x %08x\n", mem1[0], mem1[1], mem1[2], mem1[3]); + for (addr = 0; addr < 0x1800000; addr += 4) { + if (!memcmp((void *)addr, sig_fwrite, sizeof sig_fwrite)) { + printf("found fwrite at %x, patching\n", addr); + u32 *ptr = (u32 *)addr; + int i; + for (i = 0; i < 16; i+=4) + printf("%08x: %08x %08x %08x %08x\n", + ((u32)addr) + i*4, ptr[i], ptr[i+1], ptr[i+2], ptr[i+3]); +// memcpyr((void *)addr, fwrite_patch, sizeof fwrite_patch); +// break; + } + } + stuff_EXI_stub(which_stub, insns, len); + printf("stuff_EXI_stub done\n"); +} + +void powerpc_reset_wrapper(void) { +// printf("powerpc_reset()\n"); +// printf("Looking for fwrite\n"); + u32 addr; + u32 *mem1 = (u32 *)0x1330000; +// printf("81330000: %08x %08x %08x %08x\n", mem1[0], mem1[1], mem1[2], mem1[3]); +// powerpc_reset(); + for (addr = 0; addr < 0x1800000; addr += 4) { + if (!memcmp((void *)addr, sig_fwrite, sizeof sig_fwrite)) { + fwrite_count++; +// printf("found fwrite at %x, patching\n", addr); + u32 *ptr = (u32 *)addr; + int i; + // for (i = 0; i < 16; i+=4) + // printf("%08x: %08x %08x %08x %08x\n", + // ((u32)addr) + i*4, ptr[i], ptr[i+1], ptr[i+2], ptr[i+3]); +// memcpyr((void *)addr, fwrite_patch, sizeof fwrite_patch); + for (i=0; i < sizeof(fwrite_patch)/4; i++) ptr[i] = fwrite_patch[i]; + break; + } + } +// dc_flushall(); + u32* hw_ppcirqmask = (u32*)0x0D800034; + u32* hw_reset = (u32*)0x0D800194; + u32* hw_timer = (u32*)0x0D800010; + u32 time_next; + + *hw_ppcirqmask = 0x40000000; + *hw_reset &= ~0x30; + // sleep. this isn't exactly right... + for (time_next = *hw_timer+0xF; time_next < *hw_timer;); + *hw_reset |= 0x20; + // sleep + for (time_next = *hw_timer+0x96; time_next < *hw_timer;); + *hw_reset |= 0x10; +// printf("powerpc_reset done\n"); +} +#endif + +#ifdef USE_RELOAD_IOS +/* look for the reload_ios function in a newly-loaded IOS kernel */ +u32 * find_reload_ios(void) { +// these numbers are pretty much guaranteed to exist -- see above implementation of reload_ios + u32 magic[] = {0x93400000, 0x93420000}; + u32 *kernel = (u32 *)0xFFFF0000; + int i; + printf("Looking for reload_ios\n"); + for(i = 0; i < 0x10000 / 4; i++) { + if (kernel[i] == magic[0] && kernel[i+1] == magic[1]) { + printf("Found reload_ios marker at %x\n", 0xFFFF0000 + i * 4); + for (; i >=0; i--) { + if ((kernel[i] >> 16) == 0xB570) { + printf("Found function prolog at %x\n", 0xFFFF0000 + i * 4); + return (u32 *)(&kernel[i]); + } + } + printf("sry, i haz fail\n"); // could not find reload_ios, so we're screwed + return NULL; + } + } +} +#else // !USE_RELOAD_IOS +/* look for the xchange_os_version function in a newly-loaded IOS kernel */ +// kernel:FFFF5A24 B5 30 PUSH {R4,R5,LR} +// kernel:FFFF5A26 23 C4 01 9B MOVS R3, 0x3100 +// kernel:FFFF5A2A 6C 1D LDR R5, [R3,#0x40] + +u32 * find_xchange_osvers(void) { + u32 magic[] = {0xB53023C4}; + u32 *kernel = (u32 *)0xFFFF0000; + int i; + printf("Looking for xchange_osvers\n"); + for(i = 0; i < 0x10000 / 4; i++) { + if (kernel[i] == magic[0]) { + printf("Found xchange_osvers start at %x\n",0xFFFF0000 + i * 4); + return (u32 *)(0xFFFF0000 + i*4); + } + } + printf("sry, I haz fail! :(\n"); + return NULL; +} +#endif + +// this is where we patch teh kernel to add our hooks +void do_kernel_patches(u32 size) { + printf("do_kernel_patches(%x)\n",size); + +// this is just to make life easier + u32 *kernel_mem32 = (u32 *)0xFFFF0000; + u16 *kernel_mem16 = (u16 *)0xFFFF0000; + +// sanity check of the syscall vector table + if (kernel_mem32[1] != 0xe59ff018) { + printf("ohnoes, unexpected offset to starlet_syscall_handler %x\n",kernel_mem32[1]); + return; + } + if (kernel_mem32[2] != 0xe59ff018) { + printf("ohnoes, unexpected offset to arm_syscall_handler %x\n",kernel_mem32[2]); + return; + } + printf("starlet_syscall_handler vector: %x\n",kernel_mem32[9]); + printf("svc_handler vector2: %x\n",kernel_mem32[10]); + +/* SVC patch to get debug output over USBGecko -- we copy the code from vectors.s over some unused + code present in all IOSes to make life easier -- SVC 4 is the only one used, but they include + functions to call over SVC handlers which we can just blow away. thanks ninty */ + +/* scan from 0xFFFF0000 ... 0xFFFFFFFF looking for SVC 05 instruction */ + u32 i; + for (i=0; i < 0x10000/2; i++) { + if (kernel_mem16[i+0] == 0x4672 && + kernel_mem16[i+1] == 0x1c01 && + kernel_mem16[i+2] == 0x2005) { + dprintf("SVC 5 caller found at %0x\n",0xffff0000 + i*2); + + // copy in SVC vector code + memcpyr(&kernel_mem16[i], vectors_bin, vectors_bin_size); + // change SVC vector pointer to point to this new code + kernel_mem32[10] = (u32) &kernel_mem16[i]; + dprintf("patch done\n"); + } + // while we're here, look for the mem2 protection code and disable it + if (kernel_mem16[i+0] == 0xB500 && + kernel_mem16[i+1] == 0x4B09 && + kernel_mem16[i+2] == 0x2201 && + kernel_mem16[i+3] == 0x801A && + kernel_mem16[i+4] == 0x22F0) { + dprintf("Found MEM2 patch at %x\n",0xffff0000); + kernel_mem16[i+2] = 0x2200; + } + } + +#ifdef USE_RELOAD_IOS +/* patch reload_ios so that we can infect any IOS we reload */ + u32 *addr = find_reload_ios(); + if (addr) { // overwrite reload_ios with a jump to our wrapper + addr[0] = 0x4B004718; // ldr r3, $+4 / bx r3 + addr[1] = (u32)reload_ios_wrapper; + } +#else // !USE_RELOAD_IOS +/* patch xchange_osvers so that we can infect any IOS we reload */ + u32 *addr = find_xchange_osvers(); + if (addr) { // overwrite xchange_osvers with a jump to our wrapper + addr[0] = 0x4B004718; // ldr r3, $+4 / bx r3 + addr[1] = (u32)xchange_osvers_and_patch; +// dprintf("wrote %08x %08x to %08x\n", addr[0], addr[1], (u32)addr); + } +#endif + +#ifdef PPCHAX + find_powerpc_reset(); + addr = find_ppcreset(); + printf("ppcreset = %x\n", (u32)addr); + if (addr) { // overwrite ppcreboot with a jump to our wrapper + addr[0] = 0x4B004718; + addr[1] = (u32)powerpc_reset_wrapper; + } +#endif + dprintf("\ndo_kernel_patches done\n"); +} + +/* perform patching of syscall table to install our hooks */ +void handle_syscall_table(u32 *syscall_table, u32 size) { + u32 i, num_syscalls; + for (i = 0; i < size/4; i++) if ((syscall_table[i] >> 16) == 0) break; + num_syscalls = i; + printf("Syscall table @ 0x%x: %x entries\n",(u32) syscall_table, num_syscalls); + + /* We assume a specifc syscall ordering here, oops. grab the existing function pointers */ + device_open = (void *)syscall_table[SYSCALL_OPEN]; + new_thread = (void *)syscall_table[SYSCALL_NEW_THREAD]; + ppcboot = (void *)syscall_table[SYSCALL_PPCBOOT]; + armboot = (void *)syscall_table[SYSCALL_ARMBOOT]; + loadelf = (void *)syscall_table[SYSCALL_LOADELF]; + + /* modify the table to point to our wrapper functions */ + syscall_table[SYSCALL_OPEN] = (u32)device_open_wrapper; + syscall_table[SYSCALL_NEW_THREAD] = (u32)new_thread_wrapper; + syscall_table[SYSCALL_PPCBOOT] = (u32)ppcboot_wrapper; + syscall_table[SYSCALL_ARMBOOT] = (u32)armboot_wrapper; + syscall_table[SYSCALL_LOADELF] = (u32)loadelf_wrapper; + + printf("\nnew device_open: %x\n", syscall_table[SYSCALL_OPEN]); // this is just to give me the warm fuzzies + + printf("\nNew syscall table:\n"); + for(i=0; i < num_syscalls; i++) { + printf("%x: %x\n", i, syscall_table[i]); + } +} + +#ifdef PPCHAX +void * find_stuff_EXI_stub(void) { + u32 magic[] = {0x7C631A78, 0x6463D7B0}; + u32 *kernel = (u32 *)0xFFFF0000; + u32 ppc_stub1_addr = 0, stuff_EXI_stub_addr = 0; + int i; + printf("Looking for ppc_stub1\n\n"); + for(i = 0; i < 0x10000 / 4; i++) { + if (kernel[i] == magic[0] && kernel[i+1] == magic[1]) { + printf("Found ppc_stub1 at %x\n",0xFFFF0000 + i * 4); + break; + } + } + if (i == 0x10000/4) { + printf("Couldn't find ppc_stub1\n"); + return NULL; + } + ppc_stub1_addr = 0xFFFF0000 + i*4; + for(i = 0; i < 0x10000 / 4; i++) { + if (kernel[i] == ppc_stub1_addr) { + printf("Found ppc_stub1 reference at %x\n",0xFFFF0000 + i * 4); + break; + } + } + if (i == 0x10000/4) { + printf("Couldn't find ppc_stub ref\n"); + return NULL; + } + for(; i > 0; i--) { + if (kernel[i] == 0xB5001C03) { + printf("Found stuff_EXI_stub start at %x\n", 0xFFFF0000 + i * 4); + break; + } + } + if (i == 0x10000) { + printf("Couldn't find stuff_EXI_stub start\n"); + return NULL; + } + stuff_EXI_stub = (void *)(0xFFFF0000 + i * 4 + 1); // thumb offset + for(i = 0; i < 0x10000 / 4; i++) { + if (kernel[i] == (u32)stuff_EXI_stub) { + printf("Found stuff_EXI_stub_addr reference at %x\n", 0xFFFF0000 + i * 4); + break; + } + } + if (i == 0x10000) { + printf("Couldn't find stuff_EXI_stub reference\n"); + return NULL; + } + kernel[i] = (u32)&stuff_EXI_stub_wrapper; + printf("Replaced stuff_EXI_stub reference with %x\n", kernel[i]); + return NULL; +} + +void find_powerpc_reset(void) { + u32 magic[] = {0x0d800034, 0x0d800194}; + u32 *kernel = (u32 *)0xFFFF0000; + u32 ppc_stub1_addr = 0, stuff_EXI_stub_addr = 0; + int i; + printf("Looking for powerpc_reset magic\n\n"); + for(i = 0; i < 0x10000 / 4; i++) { + if (kernel[i] == magic[0] && kernel[i+1] == magic[1]) { + printf("Found powerpc_reset magic at %x\n",0xFFFF0000 + i * 4); + break; + } + } + if (i == 0x10000/4) { + printf("Couldn't find powerpc_reset magic\n"); + return; + } + for(; i > 0; i--) { + if (kernel[i] == 0xB5704646) { + printf("Found powerpc_reset start at %x\n", 0xFFFF0000 + i * 4); + break; + } + } + if (i == 0x10000) { + printf("Couldn't find powerpc_reset start\n"); + return; + } + powerpc_reset = (void *)(0xFFFF0000 + i*4 + 1); + for(i = 0; i < 0x10000 / 4; i++) { + if (kernel[i] == (u32)powerpc_reset) { + printf("Found powerpc_reset reference at %x\n", 0xFFFF0000 + i * 4); + break; + } + } + if (i == 0x10000) { + printf("Couldn't find powerpc_reset reference\n"); + return; + } +// kernel[i] = (u32)&powerpc_reset_wrapper; + printf("Replaced powerpc_reset reference with %x\n", kernel[i]); +} + +u32 * find_ppcreset(void) { + u32 magic[] = {0x0d800034, 0x0d800194}; + u32 *kernel = (u32*)0xFFFF0000; + int i; + for(i=0; i < 0x10000 / 4; i++) { + if (kernel[i] == magic[0] && kernel[i+1] == magic[1]) { + for (; i >=0; i--) { + if ((kernel[i] >> 16) == 0xB570) // push {R4-R6,LR}, prolog + return kernel+i; + } + return NULL; + } + } + return NULL; +} +#endif + +/* this gross patch is necessary to get WC24 debug spew */ +void do_kd_patch(u8 *buffer, u32 size) { + int i; +// I don't remember what this first patch was, sorry +// u8 match[] = {0x13, 0xdf, 0x5f, 0xcd, 0x13, 0xdf, 0x5f, 0xa9}; +// u8 replace[] = {0x13, 0xdf, 0x5f, 0x45, 0x13, 0xdf, 0x5f, 0x29}; + u8 match[] = {0x30, 0x01, 0x28, 0x7f, 0xd9, 0x01, 0x23, 0x00}; + u8 replace[] = {0x49, 0x16, 0x20, 0x04, 0xdf, 0xab, 0x23, 0x00}; + + dprintf("Looking for can_haz_debug(%x, %x)\n",(u32)buffer, size); + for(i=0; i < (size-16); i++) { + if (!memcmp(match, buffer+i, 8)) { + dprintf("Found match @ %x\n",(u32)&buffer[i]); + memcpyr(buffer + i, replace, 8); + return; + } + } +} + +/* ye olde ELF loader, written in C for great justice + also patches too */ +void *_loadelf(const u8 *elf) { + if(memcmp("\x7F" "ELF\x01\x02\x01",elf,7)) { + panic(0xE3); + } + printf("ELF magic ok\n"); + + Elf32_Ehdr *ehdr = (Elf32_Ehdr*)elf; + if(ehdr->e_phoff == 0) { + panic(0xE4); + } + dprintf("e_phoff=%x\n",ehdr->e_phoff); + + int count = ehdr->e_phnum; + Elf32_Phdr *phdr = (Elf32_Phdr*)(elf + ehdr->e_phoff); + while(count--) + { + dprintf("count=%x phdr type=%x paddr=%x offset=%x filesz=%x\n", + count, (u32)phdr->p_type, (u32)phdr->p_paddr, phdr->p_offset, (u32)phdr->p_filesz); + + if(phdr->p_type == PT_LOAD && phdr->p_filesz != 0) { + const void *src = elf + phdr->p_offset; + memcpyr(phdr->p_paddr, src, phdr->p_filesz); // could be memcpy, but trying to save code space + if (phdr->p_paddr == (u32 *)0xFFFF0000) do_kernel_patches(phdr->p_filesz); + if (count == 1) handle_syscall_table(phdr->p_paddr, phdr->p_filesz); // assumes syscall table will always be last phdr +// this patch needs to be done when the kernel loads the module, not when loading the kernel + do_kd_patch(phdr->p_paddr, phdr->p_filesz); + } + phdr++; + } + dprintf("done, entrypt = %x\n", (u32)ehdr->e_entry); + +#ifdef PPCHAX + find_stuff_EXI_stub(); +#endif + + return ehdr->e_entry; +} + +static inline void disable_boot0(void) +{ + set32(HW_BOOT0, 0x1000); +} + +static inline void mem_setswap(void) +{ + set32(HW_MEMMIRR, 0x20); +} + +void *_main(void *base) +{ + ioshdr *hdr = (ioshdr*)base; + u8 *elf; + void *entry; + + elf = (u8*) base; + elf += hdr->hdrsize + hdr->loadersize; + + debug_output(0xF1); + mem_setswap(); + disable_boot0(); + gecko_init(); + + printf("elfloader elf=%x\n",(u32)elf); + entry = _loadelf(elf); + printf("loadelf done\n"); + debug_output(0xC1); + return entry; + +} diff --git a/babelfish.ld b/babelfish.ld new file mode 100644 index 0000000..ab13bbf --- /dev/null +++ b/babelfish.ld @@ -0,0 +1,118 @@ +/* + babelfish - self-propagating Just-In-Time IOS patcher + +Copyright (C) 2008, 2009 Hector Martin "marcan" +Copyright (C) 2008-2011 Haxx Enterprises + +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 + +This code lives at http://gitweb.bootmii.org/?p=babelfish.git +*/ + +OUTPUT_FORMAT("elf32-bigarm") +OUTPUT_ARCH(arm) +EXTERN(_start) +ENTRY(_start) + +__base_addr = 0; +/* this must match the value in babelfish.c and start.S, we shouldn't need to hardcode this? */ +__hiding_spot = 0x13A80000; + +SECTIONS +{ + . = __base_addr; + .header : /* build the 0x10-byte "IOS header" */ + { + __header = .; + /* Entry point (offset) */ + LONG(__code_start); + /* Loader size */ + LONG(__loader_size); + /* ELF size */ + LONG(0); + /* Boot argument? */ + LONG(0); + . = ALIGN(16); + } + + __code_start = .; + + .init : + { + *(.init) + . = ALIGN(4); + } + + .got : + { + __got_start = .; + LONG(0); + *(.got.*) + *(.got) + . = ALIGN(4); + __got_end = . ; + } + + .text : + { + *(.text.*) + *(.gnu.warning) + *(.gnu.linkonce.t*) + *(.glue_7) + *(.glue_7t) + . = ALIGN(4); + } + + __text_end = . ; + + .rodata : + { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r*) + . = ALIGN(4); + } + + .data : + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + . = ALIGN(4); + } + + .bss : + { + __bss_start = . ; + *(.dynbss) + *(.gnu.linkonce.b*) + *(.bss*) + *(.sbss*) + *(COMMON) + . = ALIGN(32); + __bss_end = . ; + } + +} + +/* make sure you leave enough room for the stack -- I had major problems + with printf until I realized I was exhausting my stack. Changed this + from 0x100 to 0x800 bytes */ + +__stack_end = (__bss_end); +__stack_addr = (__bss_end + 0x800); + +__end = __stack_addr ; +__loader_size = __end - __code_start; + +PROVIDE (__stack_end = __stack_end); +PROVIDE (__stack_addr = __stack_addr); +PROVIDE (__got_start = __got_start); +PROVIDE (__got_end = __got_end); +PROVIDE (__bss_start = __bss_start); +PROVIDE (__bss_end = __bss_end); +PROVIDE (__hiding_spot = __hiding_spot); + diff --git a/common.mk b/common.mk new file mode 100644 index 0000000..fa14285 --- /dev/null +++ b/common.mk @@ -0,0 +1,59 @@ +AR = $(PREFIX)ar +AS = $(PREFIX)as +CC = $(PREFIX)gcc +CXX = $(PREFIX)g++ +LD = $(PREFIX)ld +OBJCOPY = $(PREFIX)objcopy +RANLIB = $(PREFIX)ranlib +STRIP = $(PREFIX)strip + +BIN2S = $(DEVKITPPC)/bin/bin2s + +ifeq ($(NOMAPFILE),) +LDFLAGS += -Wl,-Map,$(TARGET).map +endif + +ifneq ($(LDSCRIPT),) +LDFLAGS += -Wl,-T$(LDSCRIPT) +endif + +DEPDIR = .deps + +all: $(TARGET) + +$(TARGET): $(OBJS) + @echo " LINK $@" + @$(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ + +ifneq ($(LDSCRIPT),) +$(TARGET): $(LDSCRIPT) +endif + +%.o: %.c + @echo " COMPILE $<" + @mkdir -p $(DEPDIR) + @$(CC) $(CFLAGS) $(DEFINES) -Wp,-MMD,$(DEPDIR)/$(*F).d,-MQ,"$@",-MP -c $< -o $@ + +%.o: %.s + @echo " ASSEMBLE $<" + @$(CC) $(CFLAGS) $(DEFINES) $(ASFLAGS) -c $< -o $@ + +%.o: %.S + @echo " ASSEMBLE $<" + @$(CC) $(CFLAGS) $(DEFINES) $(ASFLAGS) -c $< -o $@ + +clean: + rm -rf $(DEPDIR) + rm -f $(TARGET) $(TARGET).map $(OBJS) + +define bin2o + @echo " BIN2S $(notdir $<)" + @$(BIN2S) -a 32 $< | $(AS) -o $(@) + @echo "extern const u8" `(echo $( `(echo $(> `(echo $(> `(echo $( + +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 + +This code lives at http://gitweb.bootmii.org/?p=babelfish.git +*/ + +#ifndef __ELF_H__ +#define __ELF_H__ + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + void *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_shtrndx; +} Elf32_Ehdr; + +typedef struct { + u32 p_type; + u32 p_offset; + void *p_vaddr; + void *p_paddr; + u32 p_filesz; + u32 p_memsz; + u32 p_flags; + u32 p_align; +} Elf32_Phdr; + +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 + +#endif + diff --git a/gecko.c b/gecko.c new file mode 100644 index 0000000..527a877 --- /dev/null +++ b/gecko.c @@ -0,0 +1,105 @@ +/* +babelfish - self-propagating Just-In-Time IOS patcher + +Copyright (C) 2008-2011 Haxx Enterprises + +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 + +This code lives at http://gitweb.bootmii.org/?p=babelfish.git +*/ + +#include "types.h" +#include "utils.h" +#include "hollywood.h" +#include "gecko.h" + +/* some of this code is redundant with that in vectors.s */ +static int gecko_active = 0; + +static u32 _gecko_command(u32 command) +{ + u32 i; + // Memory Card Port B (Channel 1, Device 0, Frequency 3 (32Mhz Clock)) + write32(EXI1_CSR, 0xd0); + write32(EXI1_DATA, command); + write32(EXI1_CR, 0x19); + while (read32(EXI1_CR) & 1); + i = read32(EXI1_DATA); + write32(EXI1_CSR, 0); + return i; +} + +static u32 _gecko_sendbyte(char sendbyte) +{ + u32 i = 0; + i = _gecko_command(0xB0000000 | (sendbyte<<20)); + if (i&0x04000000) + return 1; // Return 1 if byte was sent + return 0; +} + +static u32 _gecko_checksend(void) +{ + u32 i = 0; + i = _gecko_command(0xC0000000); + if (i&0x04000000) + return 1; // Return 1 if safe to send + return 0; +} + +int gecko_isalive(void) +{ + u32 i; + + i = _gecko_command(0x90000000); + if ((i&0xFFFF0000) != 0x04700000) + return 0; + return 1; +} + +void gecko_init(void) +{ + write32(EXI0_CSR, 0); + write32(EXI1_CSR, 0); + write32(EXI2_CSR, 0); + write32(EXI0_CSR, 0x2000); + write32(EXI0_CSR, 3<<10); + write32(EXI1_CSR, 3<<10); + + gecko_active = 0; + if(gecko_isalive()) + gecko_active = 1; +} + +int gecko_putc(int c) { + if (!gecko_active) return -1; + int tries = 10000; // about 200ms of tries; if we fail, fail gracefully instead of just hanging + // this makes CCC demos less embarassing + + if((read32(HW_EXICTRL) & EXICTRL_ENABLE_EXI) == 0) + return 0; + + if(_gecko_checksend()) { + if(!_gecko_sendbyte(c)) + return 0; + } else { + // if gecko is hung, time out and disable further attempts + // only affects gecko users without an active terminal + if(tries-- == 0) { + gecko_active = 0; + return 0; + } + } + return 1; +} + +int gecko_puts(const char *s) { + int bytes_written = 0; + + while(*s) { + if (!gecko_putc(*s++)) break; + bytes_written++; + } + return bytes_written; +} diff --git a/gecko.h b/gecko.h new file mode 100644 index 0000000..25c8255 --- /dev/null +++ b/gecko.h @@ -0,0 +1,21 @@ +/* +babelfish - self-propagating Just-In-Time IOS patcher + +Copyright (C) 2008-2011 Haxx Enterprises + +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 + +This code lives at http://gitweb.bootmii.org/?p=babelfish.git +*/ + +#ifndef __GECKO_H__ +#define __GECKO_H__ + +#include "types.h" + +int gecko_isalive(void); +int gecko_putc(int c); +int gecko_puts(const char *s); +void gecko_init(void); +#endif diff --git a/hollywood.h b/hollywood.h new file mode 100644 index 0000000..21428a3 --- /dev/null +++ b/hollywood.h @@ -0,0 +1,45 @@ +/* + babelfish - self-propagating Just-In-Time IOS patcher + +Copyright (C) 2008, 2009 Hector Martin "marcan" +Copyright (C) 2008-2011 Haxx Enterprises + +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 + +This code lives at http://gitweb.bootmii.org/?p=babelfish.git +*/ +#ifndef __HOLLYWOOD_H__ +#define __HOLLYWOOD_H__ + +#define HW_REG_BASE 0xd800000 +#define HW_TIMER (HW_REG_BASE + 0x010) +#define HW_ARMIRQMASK (HW_REG_BASE + 0x03c) + +#define HW_MEMMIRR (HW_REG_BASE + 0x060) +#define HW_BOOT0 (HW_REG_BASE + 0x18c) + +#define HW_EXICTRL (HW_REG_BASE + 0x070) +#define EXICTRL_ENABLE_EXI 1 + +#define HW_VERSION (HW_REG_BASE + 0x214) +#define MEM_REG_BASE 0xd8b4000 + +#define MEM_FLUSHREQ (MEM_REG_BASE + 0x228) +#define MEM_FLUSHACK (MEM_REG_BASE + 0x22a) + +#define EXI_REG_BASE 0xd806800 +#define EXI0_REG_BASE (EXI_REG_BASE + 0x000) +#define EXI2_REG_BASE (EXI_REG_BASE + 0x028) + +#define EXI0_CSR (EXI0_REG_BASE + 0x000) +#define EXI2_CSR (EXI2_REG_BASE + 0x000) + +#define EXI1_REG_BASE (EXI_REG_BASE + 0x014) +#define EXI1_CSR (EXI1_REG_BASE + 0x000) +#define EXI1_MAR (EXI1_REG_BASE + 0x004) +#define EXI1_LENGTH (EXI1_REG_BASE + 0x008) +#define EXI1_CR (EXI1_REG_BASE + 0x00c) +#define EXI1_DATA (EXI1_REG_BASE + 0x010) +#endif + diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..d060916 --- /dev/null +++ b/memory.c @@ -0,0 +1,210 @@ +/* +babelfish - self-propagating Just-In-Time IOS patcher + +Copyright (C) 2008, 2009 Hector Martin "marcan" +Copyright (C) 2008-2011 Haxx Enterprises + +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 + +This code lives at http://gitweb.bootmii.org/?p=babelfish.git +*/ + +#include "types.h" +#include "memory.h" +#include "utils.h" +#include "gecko.h" +#include "hollywood.h" + +void _dc_inval_entries(void *start, int count); +void _dc_flush_entries(const void *start, int count); +void _dc_flush(void); +void _ic_inval(void); +void _drain_write_buffer(void); + +#define LINESIZE 0x20 +#define CACHESIZE 0x4000 + +#define CR_MMU (1 << 0) +#define CR_DCACHE (1 << 2) +#define CR_ICACHE (1 << 12) + +// TODO: move to hollywood.h once we figure out WTF +#define HW_100 (HW_REG_BASE + 0x100) +#define HW_104 (HW_REG_BASE + 0x104) +#define HW_108 (HW_REG_BASE + 0x108) +#define HW_10c (HW_REG_BASE + 0x10c) +#define HW_110 (HW_REG_BASE + 0x110) +#define HW_114 (HW_REG_BASE + 0x114) +#define HW_118 (HW_REG_BASE + 0x118) +#define HW_11c (HW_REG_BASE + 0x11c) +#define HW_120 (HW_REG_BASE + 0x120) +#define HW_124 (HW_REG_BASE + 0x124) +#define HW_130 (HW_REG_BASE + 0x130) +#define HW_134 (HW_REG_BASE + 0x134) +#define HW_138 (HW_REG_BASE + 0x138) +#define HW_188 (HW_REG_BASE + 0x188) +#define HW_18C (HW_REG_BASE + 0x18c) + +// what is this thing doing anyway? +// and why only on reads? +u32 _mc_read32(u32 addr) +{ + u32 data; + u32 tmp130 = 0; + // this seems to be a bug workaround + if(!(read32(HW_VERSION) & 0xF0)) + { + tmp130 = read32(HW_130); + write32(HW_130, tmp130 | 0x400); + // Dummy reads? + read32(HW_138); + read32(HW_138); + read32(HW_138); + read32(HW_138); + } + data = read32(addr); + read32(HW_VERSION); //??? + + if(!(read32(HW_VERSION) & 0xF0)) + write32(HW_130, tmp130); + + return data; +} + +// this is ripped from IOS, because no one can figure out just WTF this thing is doing +void _ahb_flush_to(enum AHBDEV dev) { + u32 mask = 10; + switch(dev) { + case AHB_STARLET: mask = 0x8000; break; + case AHB_1: mask = 0x4000; break; + //case 2: mask = 0x0001; break; + case AHB_NAND: mask = 0x0002; break; + case AHB_AES: mask = 0x0004; break; + case AHB_SHA1: mask = 0x0008; break; + //case 6: mask = 0x0010; break; + //case 7: mask = 0x0020; break; + //case 8: mask = 0x0040; break; + case AHB_SDHC: mask = 0x0080; break; + //case 10: mask = 0x0100; break; + //case 11: mask = 0x1000; break; + //case 12: mask = 0x0000; break; + default: +// gecko_printf("ahb_invalidate(%d): Invalid device\n", dev); + return; + } + //NOTE: 0xd8b000x, not 0xd8b400x! + u32 val = _mc_read32(0xd8b0008); + if(!(val & mask)) { + switch(dev) { + // 2 to 10 in IOS, add more + case AHB_NAND: + case AHB_AES: + case AHB_SHA1: + case AHB_SDHC: + //0, 1, 11 in IOS, add more + case AHB_STARLET: + case AHB_1: + write32(0xd8b0008, val & (~mask)); + // wtfux + write32(0xd8b0008, val | mask); + write32(0xd8b0008, val | mask); + write32(0xd8b0008, val | mask); + } + } +} + +// invalidate device and then starlet +void ahb_flush_to(enum AHBDEV type) +{ + u32 cookie = irq_kill(); + _ahb_flush_to(type); + if(type != AHB_STARLET) + _ahb_flush_to(AHB_STARLET); + +// irq_restore(cookie); +} + +// flush device and also invalidate memory +void ahb_flush_from(enum AHBDEV dev) +{ + u32 cookie = irq_kill(); + u16 req = 0; + u16 ack; + int i; + + switch(dev) + { + case AHB_STARLET: + case AHB_1: + req = 1; + break; + case AHB_AES: + case AHB_SHA1: + req = 2; + break; + case AHB_NAND: + case AHB_SDHC: + req = 8; + break; + default: +// gecko_printf("ahb_flush(%d): Invalid device\n", dev); + return; + } + + write16(MEM_FLUSHREQ, req); + + for(i=0;i<1000000;i++) { + ack = read16(MEM_FLUSHACK); + _ahb_flush_to(AHB_STARLET); + if(ack == req) + break; + } + write16(MEM_FLUSHREQ, 0); + if(i>=1000000) { +// gecko_printf("ahb_flush(%d): Flush (0x%x) did not ack!\n", dev, req); + } +// irq_restore(cookie); +} + +void dc_flushrange(const void *start, u32 size) +{ + u32 cookie = irq_kill(); + if(size > 0x4000) { + _dc_flush(); + } else { + void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE); + start = ALIGN_BACKWARD(start, LINESIZE); + _dc_flush_entries(start, (end - start) / LINESIZE); + } + _drain_write_buffer(); + ahb_flush_from(AHB_1); +// irq_restore(cookie); +} + +void dc_invalidaterange(void *start, u32 size) +{ + u32 cookie = irq_kill(); + void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE); + start = ALIGN_BACKWARD(start, LINESIZE); + _dc_inval_entries(start, (end - start) / LINESIZE); + ahb_flush_to(AHB_STARLET); +// irq_restore(cookie); +} + +void dc_flushall(void) +{ + u32 cookie = irq_kill(); + _dc_flush(); + _drain_write_buffer(); + ahb_flush_from(AHB_1); +// irq_restore(cookie); +} + +void ic_invalidateall(void) +{ + u32 cookie = irq_kill(); + _ic_inval(); + ahb_flush_to(AHB_STARLET); +// irq_restore(cookie); +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..43a97e1 --- /dev/null +++ b/memory.h @@ -0,0 +1,118 @@ +/* + mini - a Free Software replacement for the Nintendo/BroadOn IOS. + memory management, MMU, caches, and flushing + +Copyright (C) 2008, 2009 Hector Martin "marcan" + +# 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 +*/ + +#ifndef __MEMORY_H__ +#define __MEMORY_H__ + +#include "types.h" + +#define ALIGN_FORWARD(x,align) \ + ((typeof(x))((((u32)(x)) + (align) - 1) & (~(align-1)))) + +#define ALIGN_BACKWARD(x,align) \ + ((typeof(x))(((u32)(x)) & (~(align-1)))) + +enum AHBDEV { + AHB_STARLET = 0, //or MEM2 or some controller or bus or ?? + AHB_1 = 1, //ppc or something else??? + AHB_NAND = 3, + AHB_AES = 4, + AHB_SHA1 = 5, + AHB_SDHC = 9, +}; + +void dc_flushrange(const void *start, u32 size); +void dc_invalidaterange(void *start, u32 size); +void dc_flushall(void); +void ic_invalidateall(void); +void ahb_flush_from(enum AHBDEV dev); +void ahb_flush_to(enum AHBDEV dev); +void mem_protect(int enable, void *start, void *end); +void mem_setswap(int enable); + +void mem_initialize(void); +void mem_shutdown(void); + +u32 dma_addr(void *); + +static inline u32 get_cr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c1, c0, 0" : "=r" (data) ); + return data; +} + +static inline u32 get_ttbr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c2, c0, 0" : "=r" (data) ); + return data; +} + +static inline u32 get_dacr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c3, c0, 0" : "=r" (data) ); + return data; +} + +static inline void set_cr(u32 data) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c1, c0, 0" :: "r" (data) ); +} + +static inline void set_ttbr(u32 data) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c2, c0, 0" :: "r" (data) ); +} + +static inline void set_dacr(u32 data) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c3, c0, 0" :: "r" (data) ); +} + +static inline u32 get_dfsr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c5, c0, 0" : "=r" (data) ); + return data; +} + +static inline u32 get_ifsr(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c5, c0, 1" : "=r" (data) ); + return data; +} + +static inline u32 get_far(void) +{ + u32 data; + __asm__ volatile ( "mrc\tp15, 0, %0, c6, c0, 0" : "=r" (data) ); + return data; +} + +void _ahb_flush_to(enum AHBDEV dev); + +static inline void dc_inval_block_fast(void *block) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c7, c6, 1" :: "r" (block) ); + _ahb_flush_to(AHB_STARLET); //TODO: check if really needed and if not, remove +} + +static inline void dc_flush_block_fast(void *block) +{ + __asm__ volatile ( "mcr\tp15, 0, %0, c7, c10, 1" :: "r" (block) ); + __asm__ volatile ( "mcr\tp15, 0, %0, c7, c10, 4" :: "r" (0) ); + ahb_flush_from(AHB_1); //TODO: check if really needed and if not, remove +} + +#endif + diff --git a/memory_asm.S b/memory_asm.S new file mode 100644 index 0000000..c15fa31 --- /dev/null +++ b/memory_asm.S @@ -0,0 +1,61 @@ +/* + mini - a Free Software replacement for the Nintendo/BroadOn IOS. + memory management, MMU, caches, and flushing + +Copyright (C) 2008, 2009 Hector Martin "marcan" + +# 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 +*/ + +.arm + +.globl _dc_inval_entries +.globl _dc_flush_entries +.globl _dc_flush +.globl _dc_inval +.globl _ic_inval +.globl _drain_write_buffer +.globl _tlb_inval + +.text + +_dc_inval_entries: + mcr p15, 0, r0, c7, c6, 1 + add r0, #0x20 + subs r1, #1 + bne _dc_inval_entries + bx lr + +_dc_flush_entries: + mcr p15, 0, r0, c7, c10, 1 + add r0, #0x20 + subs r1, #1 + bne _dc_flush_entries + bx lr + +_dc_flush: + mrc p15, 0, pc, c7, c10, 3 + bne _dc_flush + bx lr + +_dc_inval: + mov r0, #0 + mcr p15, 0, r0, c7, c6, 0 + bx lr + +_ic_inval: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + bx lr + +_drain_write_buffer: + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 + bx lr + +_tlb_inval: + mov r0, #0 + mcr p15, 0, r0, c8, c7 + bx lr + diff --git a/starlet.mk b/starlet.mk new file mode 100644 index 0000000..38e6d4d --- /dev/null +++ b/starlet.mk @@ -0,0 +1,11 @@ +ifeq ($(strip $(WIIDEV)),) +$(error "Set WIIDEV in your environment.") +endif + +PREFIX = $(WIIDEV)/bin/armeb-eabi- + +CFLAGS = -mbig-endian -mcpu=arm926ej-s +CFLAGS += -fomit-frame-pointer -ffunction-sections +CFLAGS += -Wall -Wextra -Os -pipe +ASFLAGS = +LDFLAGS = -mbig-endian -n -nostartfiles -nodefaultlibs -Wl,-gc-sections diff --git a/start.S b/start.S new file mode 100644 index 0000000..502ba4e --- /dev/null +++ b/start.S @@ -0,0 +1,166 @@ +/* babelfish - self-propagating Just-In-Time IOS patcher + +Copyright (C) 2008, 2009 Hector Martin "marcan" +Copyright (C) 2008-2011 Haxx Enterprises + +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 + +This code lives at http://gitweb.bootmii.org/?p=babelfish.git +*/ +.arm + +.extern _main +.extern __got_start +.extern __got_end +.extern __bss_start +.extern __bss_end +.extern __stack_addr +.extern delay +.globl _start +.globl debug_output +.globl dc_flush +.globl irq_kill +.globl disable_icache_dcache_mmu +.globl jump_to_r0 +.section .init + +_start: + @ Get real address of _start + sub r4, pc, #8 + @ Subtract offset to get the address that we were loaded at + ldr r0, =_start + sub r4, r4, r0 + mov r7, r4 + +@ here's part of the magic: + @ XXX calculate size of code+data -- or just hardcode it :/ + mov r5, #0x4000 + + @ Output 0x42 to the debug port + mov r0, #0x42 + bl debug_output + +@ copy ourselves to hiding spot + mov r2, r4 @ r2 = original loading address + add r5, r5, r4 @ stop copying at r5 +@ ldr r4, =__hiding_spot @ set new loading address + mov r4, #0x13000000 @ ugh we really shouldn't hardcode this - hiding spot = 0x13A80000 + add r4, r4, #0xA80000 + + mov r1, r4 +@ copy ourselves to hiding spot: from r2 to r1 +copy_loop: + ldr r3, [r2] + add r2, r2, #4 + str r3, [r1] + add r1, r1, #4 + cmp r2, r5 + bne copy_loop + +@ the copy_loop code plus these next two instructions are 0x60 bytes long +@ jump to our newly-relocated self -- execution will "seamlessly" resume + add r0, r4, #0x60 + bx r0 + +@ execution is transferred here + +@ the rest of this is just standard ELF loader stuff, except for the step after GOT relocation + @ Set up a stack + ldr sp, =__stack_addr + add sp, r4 + + @ Output 0x43 to the debug port + mov r0, #0x43 + bl debug_output + + @ relocate the GOT entries + ldr r1, =__got_start + add r1, r4 + ldr r2, =__got_end + add r2, r4 + + @ subtract out previous reloc value (first entry in GOT) -- this is necessary to prevent + @ double-fixups of the GOT table, thanks segher + ldr r0, [r1] + mov r5, r4 + sub r5, r0 + +got_loop: + @ check for the end + cmp r1, r2 + beq done_got + @ read the GOT entry + ldr r3, [r1] + @ add our base address + add r3, r5 + str r3, [r1] + @ move on + add r1, r1, #4 + b got_loop + +done_got: + @ clear BSS + ldr r1, =__bss_start + add r1, r4 + ldr r2, =__bss_end + add r2, r4 + mov r3, #0 +bss_loop: + @ check for the end + cmp r1, r2 + beq done_bss + @ clear the word and move on + str r3, [r1] + add r1, r1, #4 + b bss_loop + +done_bss: + mov r0, #0x44 + bl debug_output + @ take the plunge + mov r0, r7 + bl _main + @ _main returned! Go to whatever address it returned... + mov r1, r0 + mov r0, r4 + mov pc, r1 + +.pool +@ misc low-level funcs used by other parts of the code + +debug_output: + @ load address of port + mov r3, #0xd800000 + @ load old value + ldr r2, [r3, #0xe0] + @ clear debug byte + bic r2, r2, #0xFF0000 + @ insert new value + and r0, r0, #0xFF + orr r2, r2, r0, LSL #16 + @ store back + str r2, [r3, #0xe0] + bx lr + +dc_flush: + mrc p15, 0, pc, c7, c10, 3 + bne dc_flush + bx lr + +irq_kill: + mrs r1, cpsr + and r0, r1, #0xc0 + orr r1, r1, #0xc0 + msr cpsr_c, r1 + bx lr + +disable_icache_dcache_mmu: + mrc p15, 0, r0, c1, c0 + bic r0, r0, #0x1000 + bic r0, r0, #0x5 + mcr p15, 0, r0, c1, c0 + bx lr + +jump_to_r0: + mov pc, r0 diff --git a/types.h b/types.h new file mode 100644 index 0000000..837d854 --- /dev/null +++ b/types.h @@ -0,0 +1,50 @@ +/* + mini - a Free Software replacement for the Nintendo/BroadOn IOS. + + ELF loader: types + +Copyright (C) 2008, 2009 Hector Martin "marcan" + +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 + +*/ +#ifndef __TYPES_H__ +#define __TYPES_H__ + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +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; + +#define NULL ((void *)0) + +#endif + diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..0b97507 --- /dev/null +++ b/utils.c @@ -0,0 +1,145 @@ +/* babelfish -- misc (C) utility functions */ + +#include "types.h" +#include "utils.h" +#include "hollywood.h" +#include + +extern void debug_output(u8 byte); // from start.S + +static inline void delay(u32 d) +{ + write32(HW_TIMER, 0); + while(read32(HW_TIMER) < d); +} + +void panic(u8 v) +{ + while(1) { + debug_output(v); + delay(1000000); + debug_output(0); + delay(1000000); + } +} + +/* +void *memcpy(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = 0; i < len; i++) + ((unsigned char *)dst)[i] = ((unsigned char *)src)[i]; + + return dst; +} +*/ +void *memcpyr(void *dst, const void *src, size_t len) +{ + size_t i; + + for (i = len; i > 0; i--) + ((unsigned char *)dst)[i-1] = ((unsigned char *)src)[i-1]; + + return dst; +} + +int memcmp(const void *s1, const void *s2, size_t len) +{ + size_t i; + const unsigned char * p1 = (const unsigned char *) s1; + const unsigned char * p2 = (const unsigned char *) s2; + + for (i = 0; i < len; i++) + if (p1[i] != p2[i]) return p1[i] - p2[i]; + + return 0; +} + +/*size_t strlcat(char *dest, const char *src, size_t maxlen) +{ + size_t len; + maxlen--; + for (len = 0; len < maxlen; len++) if (!dest[len]) break; + for (; len < maxlen && *src; len++) dest[len] = *src++; + dest[len] = '\0'; + return len; +} +*/ +s32 printf (const char* str, ...) { + va_list arp; + u8 c, f, r; + u32 val, pos; + char s[16]; + s32 i, w, res, cc; + + va_start(arp, str); + + for (cc = pos = 0;; ) { + c = *str++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape cahracter */ + gecko_putc(c); + pos++; + continue; + } + w = f = 0; + c = *str++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *str++; + } + while (c >= '0' && c <= '9') { /* Precision */ + w = w * 10 + (c - '0'); + c = *str++; + } + if (c == 's') { /* Type is string */ + char *param = va_arg(arp, char*); + for (i=0; param[i]; i++) { + gecko_putc(param[i]); + pos++; + } + continue; + } + r = 0; + if (c == 'd') r = 10; /* Type is signed decimal */ + if (c == 'u') r = 10; /* Type is unsigned decimal */ + if (c == 'X' || c == 'x') r = 16; /* Type is unsigned hexdecimal */ + if (r == 0) { + break; /* Unknown type */ + } + val = (c == 'd') ? (u32)(long)va_arg(arp, int) : + (u32)va_arg(arp, unsigned int); + /* Put numeral string */ + if (c == 'd') { + if (val & 0x80000000) { + val = 0 - val; + f |= 4; + } + } +// if ((maxlen - pos) <= sizeof(s)) continue; + i = sizeof(s) - 1; s[i] = 0; + do { +// c = (u8)(val % r + '0'); + c = (u8)((val & 15) + '0'); + if (c > '9') c += 7; + s[--i] = c; +// val /= r; + val >>= 4; + } while (i && val); + if (i && (f & 4)) s[--i] = '-'; + w = sizeof(s) - 1 - w; + while (i && i > w) s[--i] = (f & 1) ? '0' : ' '; + for (; s[i] ; i++) { + gecko_putc(s[i]); + pos++; + } + } + va_end(arp); +// gecko_puts(output); + return pos; +} + +int puts(const char *s) { + gecko_puts(s); + return 0; +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..4e1b5b6 --- /dev/null +++ b/utils.h @@ -0,0 +1,78 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ + +static inline u32 read32(u32 addr) +{ + u32 data; + __asm__ volatile ("ldr\t%0, %1" : "=l" (data) : "m"(*(u32 *)addr)); + return data; +} + +static inline void write32(u32 addr, u32 data) +{ + __asm__ volatile ("str\t%1, %0" : "=m"(*(u32 *)addr) : "l"(data)); +} + +static inline u32 set32(u32 addr, u32 set) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\torr\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (set) + ); + return data; +} + +static inline u32 clear32(u32 addr, u32 clear) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\tbic\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (clear) + ); + return data; +} + + +static inline u32 mask32(u32 addr, u32 clear, u32 set) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\tbic\t%0, %3\n" + "\torr\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (set), "r" (clear) + ); + return data; +} + +static inline u16 read16(u32 addr) +{ + u32 data; + __asm__ volatile ("ldrh\t%0, [%1]" : "=l" (data) : "l" (addr)); + return data; +} + +static inline void write16(u32 addr, u16 data) +{ + __asm__ volatile ("strh\t%0, [%1]" : : "l" (data), "l" (addr)); +} + +void panic(u8 v); +size_t strlen(const char *); +void *memset(void *, int, size_t); +//void *memcpy(void *, const void *, size_t); +void *memcpyr(void *, const void *, size_t); +int memcmp(const void *, const void *, size_t); +s32 printf (const char* format, ...); +int puts(const char *); +#endif + diff --git a/vectors.s b/vectors.s new file mode 100644 index 0000000..bb2e973 --- /dev/null +++ b/vectors.s @@ -0,0 +1,77 @@ +@ I believe Sven wrote these? Thanks sven :) + + .ARM + .text + +swi_vector: + + stmfa sp!, {r0-r4, lr} + + @ check SWI number + @ should work for ARM and THUMB (yay bigendian): + @ + @ ARM: + @ OOnnnnNN BBBBBBBB + @ |--| ^--LR + @ ^--halfword retreived, mask last 8 bits to get number + + @ THUMB: + @ xxxx OONN BBBB CCCC + @ |--| ^--LR + @ ^--halfword retreived, mask last 8 bits to get number + + ldrh r3, [lr, #-2] + and r3, r3, #0xFF + cmp r3, #0xAB + bne return + + @ check operation number (4=debug print) + cmp r0, #4 + bne return + + @ gpio port + ldr r3, =0x0d806814 + +loop: + ldrb r2, [r1] + bl send + add r1, #1 + cmp r2, #0x00 + bne loop + +@ optional code to insert linefeed at the end of each print, some IOS modules seem to need this +@ to get sane output +@ mov r2, #0xa +@ bl send + +return: + ldmfa sp!, {r0-r4, lr} + movs pc, lr + +@ send a string over USBGecko +send: + mov r0, #0xd0 + str r0, [r3, #0x00] + + mov r0, #0xB0000000 + orr r0, r0, r2, LSL #20 + str r0, [r3, #0x10] + + mov r0, #0x19 + str r0, [r3, #0x0c] + +sendloop: + ldr r0, [r3, #0x0c] + tst r0, #1 + bne sendloop + + ldr r0, [r3, #0x10] + tst r0, #0x04000000 + + mov r0, #0 + str r0, [r3, #0x00] + beq send + + mov pc, lr + + .POOL