mirror of
https://github.com/fail0verflow/babelfish.git
synced 2024-11-16 23:59:21 +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