mirror of
https://github.com/fail0verflow/babelfish.git
synced 2024-11-16 15:49:20 +01:00
initial commit
This commit is contained in:
commit
4b65f3b500
40
Makefile
Normal file
40
Makefile
Normal file
@ -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
|
96
README
Normal file
96
README
Normal file
@ -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.
|
729
babelfish.c
Normal file
729
babelfish.c
Normal file
@ -0,0 +1,729 @@
|
||||
/*
|
||||
babelfish - self-propagating Just-In-Time IOS patcher
|
||||
|
||||
Copyright (C) 2008-2011 Haxx Enterprises <bushing@gmail.com>
|
||||
|
||||
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;
|
||||
|
||||
}
|
118
babelfish.ld
Normal file
118
babelfish.ld
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
babelfish - self-propagating Just-In-Time IOS patcher
|
||||
|
||||
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
Copyright (C) 2008-2011 Haxx Enterprises <bushing@gmail.com>
|
||||
|
||||
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);
|
||||
|
59
common.mk
Normal file
59
common.mk
Normal file
@ -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 $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(<F) | tr . _)`.h
|
||||
@echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(<F) | tr . _)`.h
|
||||
@echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(<F) | tr . _)`.h
|
||||
endef
|
||||
|
||||
-include $(DEPDIR)/*
|
||||
|
||||
.PHONY: clean
|
58
elf.h
Normal file
58
elf.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||
|
||||
ELF loader: ELF structures
|
||||
|
||||
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
|
||||
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
|
||||
|
105
gecko.c
Normal file
105
gecko.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
babelfish - self-propagating Just-In-Time IOS patcher
|
||||
|
||||
Copyright (C) 2008-2011 Haxx Enterprises <bushing@gmail.com>
|
||||
|
||||
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;
|
||||
}
|
21
gecko.h
Normal file
21
gecko.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
babelfish - self-propagating Just-In-Time IOS patcher
|
||||
|
||||
Copyright (C) 2008-2011 Haxx Enterprises <bushing@gmail.com>
|
||||
|
||||
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
|
45
hollywood.h
Normal file
45
hollywood.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
babelfish - self-propagating Just-In-Time IOS patcher
|
||||
|
||||
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
Copyright (C) 2008-2011 Haxx Enterprises <bushing@gmail.com>
|
||||
|
||||
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
|
||||
|
210
memory.c
Normal file
210
memory.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
babelfish - self-propagating Just-In-Time IOS patcher
|
||||
|
||||
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
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);
|
||||
}
|
118
memory.h
Normal file
118
memory.h
Normal file
@ -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" <marcan@marcansoft.com>
|
||||
|
||||
# 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
|
||||
|
61
memory_asm.S
Normal file
61
memory_asm.S
Normal file
@ -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" <marcan@marcansoft.com>
|
||||
|
||||
# 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
|
||||
|
11
starlet.mk
Normal file
11
starlet.mk
Normal file
@ -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
|
166
start.S
Normal file
166
start.S
Normal file
@ -0,0 +1,166 @@
|
||||
/* babelfish - self-propagating Just-In-Time IOS patcher
|
||||
|
||||
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
Copyright (C) 2008-2011 Haxx Enterprises <bushing@gmail.com>
|
||||
|
||||
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
|
50
types.h
Normal file
50
types.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
|
||||
|
||||
ELF loader: types
|
||||
|
||||
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
|
||||
|
||||
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
|
||||
|
145
utils.c
Normal file
145
utils.c
Normal file
@ -0,0 +1,145 @@
|
||||
/* babelfish -- misc (C) utility functions */
|
||||
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
#include "hollywood.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
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;
|
||||
}
|
78
utils.h
Normal file
78
utils.h
Normal file
@ -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
|
||||
|
77
vectors.s
Normal file
77
vectors.s
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user