mirror of
https://github.com/Fullmetal5/bootmii-autoloader.git
synced 2024-11-27 04:54:20 +01:00
Initial commit
This commit is contained in:
commit
a14a1984ef
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
font.c
|
||||
version.c
|
||||
|
||||
loader.elf
|
||||
loader.bin
|
66
Makefile
Normal file
66
Makefile
Normal file
@ -0,0 +1,66 @@
|
||||
# Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
# 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
|
||||
|
||||
CROSS ?= powerpc-eabi-
|
||||
|
||||
|
||||
ifeq ($(origin CC), default)
|
||||
CC := $(CROSS)gcc -m32
|
||||
endif
|
||||
ifeq ($(origin LD), default)
|
||||
LD := $(CROSS)ld
|
||||
endif
|
||||
OBJCOPY ?= $(CROSS)objcopy
|
||||
|
||||
|
||||
CFLAGS := -Wall -W -O2 -ffreestanding -std=gnu99 -Wstrict-aliasing=2 \
|
||||
-mno-eabi -mno-sdata -mcpu=750
|
||||
|
||||
|
||||
targets := loader.bin
|
||||
|
||||
objs := crt0.o main.o string.o video.o ios.o sd.o fat.o elf.o sync.o font.o \
|
||||
console.o exception.o exception_2200.o usbgecko.o time.o version.o
|
||||
ppms := font.ppm
|
||||
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q :=
|
||||
else
|
||||
Q := @
|
||||
MAKEFLAGS += --no-print-directory
|
||||
endif
|
||||
|
||||
|
||||
all: $(targets)
|
||||
|
||||
$(targets): %.bin: %.elf
|
||||
@echo " OBJCOPY $@"
|
||||
$(Q)$(OBJCOPY) -O binary $< $@
|
||||
|
||||
elfs := $(targets:.bin=.elf)
|
||||
$(elfs): %.elf: %.lds $(objs)
|
||||
@echo " LINK $@"
|
||||
$(Q)$(LD) $(LDFLAGS) -n -T $^ -o $@
|
||||
|
||||
%.o: %.c loader.h
|
||||
@echo " COMPILE $@"
|
||||
$(Q)$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
crt0.o exception_2200.o: %.o: %.s
|
||||
@echo " ASSEMBLE $@"
|
||||
$(Q)$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(ppms): %.ppm: %.png
|
||||
@echo " PPM $@"
|
||||
$(Q)convert $< $@
|
||||
|
||||
font.c: %.c: %.ppm font2c.pl
|
||||
@echo " FONT2C $@"
|
||||
$(Q)perl font2c.pl < $*.ppm > $@
|
||||
|
||||
FORCE:
|
||||
|
||||
clean:
|
||||
rm -rf $(objs) $(targets) $(elfs) font.c
|
11
arm/Makefile
Normal file
11
arm/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
PREFIX := arm-none-eabi
|
||||
AS := $(PREFIX)-as
|
||||
OBJCOPY := $(PREFIX)-objcopy
|
||||
|
||||
.PHONY: all clean
|
||||
all:
|
||||
$(AS) -mbig-endian -mcpu=arm926ej-s stub.s -o stub.elf
|
||||
$(OBJCOPY) -O binary stub.elf stub.bin
|
||||
xxd -i stub.bin ../stub.h
|
||||
clean:
|
||||
rm -rf *.bin *.elf
|
53
arm/stub.s
Normal file
53
arm/stub.s
Normal file
@ -0,0 +1,53 @@
|
||||
# stub.s
|
||||
# We enter from (eticket) context, in THUMB mode at 0x00010001.
|
||||
|
||||
.thumb
|
||||
.thumb_func
|
||||
__start:
|
||||
add r1, pc, #0x0c
|
||||
bx r1
|
||||
.word 0, 0, 0
|
||||
.arm
|
||||
__arm_start:
|
||||
# Fix the two words we clobbered on the stack
|
||||
ldr r1, =0x1c0
|
||||
str r1, [sp]
|
||||
ldr r1, =0x20100869
|
||||
str r1, [sp, #-4]
|
||||
|
||||
# Make sure we are UID 0
|
||||
mov r0, #1
|
||||
mov r1, #0
|
||||
bl __syscall_set_uid
|
||||
|
||||
# Invalidate the armboot.bin
|
||||
ldr r0, =0x00040000
|
||||
ldr r1, =0x80000
|
||||
bl __syscall_ios_invalidatedcache
|
||||
|
||||
# And go!
|
||||
ldr r0, =0x00040000
|
||||
ldr r1, =0xdeadbeef
|
||||
bl __syscall_boot_new_ios_kernel
|
||||
|
||||
restore_state:
|
||||
# This shouldn't happen but if it does try out best to recover
|
||||
|
||||
# Return -1337 to PPC-land
|
||||
ldr r0, =0xfffffac7
|
||||
|
||||
# Return to the original saved LR that we clobbered
|
||||
ldr r3, =0x20100869
|
||||
mov lr, r3
|
||||
bx lr
|
||||
|
||||
# Syscall table
|
||||
__syscall_set_uid:
|
||||
.word 0xe6000570
|
||||
bx lr
|
||||
__syscall_ios_invalidatedcache:
|
||||
.word 0xe60007f0
|
||||
bx lr
|
||||
__syscall_boot_new_ios_kernel:
|
||||
.word 0xe6000870
|
||||
bx lr
|
153
console.c
Normal file
153
console.c
Normal file
@ -0,0 +1,153 @@
|
||||
// Copyright 2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
|
||||
static void put(char c)
|
||||
{
|
||||
fb_putc(c);
|
||||
usbgecko_console_putc(c);
|
||||
}
|
||||
|
||||
|
||||
// __umoddi3() and friends are very big, and more general than we need:
|
||||
// radix is always (very) small, so we can work by much bigger chunks
|
||||
// than single bits, always.
|
||||
static int extract_dig(u64 *x, u32 radix)
|
||||
{
|
||||
u32 hi = *x >> 32;
|
||||
u32 lo = *x;
|
||||
u32 mod = hi % radix;
|
||||
hi /= radix;
|
||||
u32 n = (mod << 16) | (lo >> 16);
|
||||
mod = n % radix;
|
||||
n /= radix;
|
||||
lo = (mod << 16) | (lo & 0xffff);
|
||||
mod = lo % radix;
|
||||
lo /= radix;
|
||||
lo |= (n << 16);
|
||||
*x = ((u64)hi << 32) | lo;
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
||||
// This implements conversions %{0}{number}{l,ll}[%cdsux] only.
|
||||
// Field length is obeyed for numbers only.
|
||||
// Always returns 0.
|
||||
|
||||
int printf(const char *restrict format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
while (*format) {
|
||||
if (*format != '%') {
|
||||
put(*format++);
|
||||
continue;
|
||||
}
|
||||
format++;
|
||||
|
||||
int zero = 0;
|
||||
int prec = 0;
|
||||
|
||||
if (*format == '0') {
|
||||
zero = 1;
|
||||
format++;
|
||||
}
|
||||
|
||||
while (*format >= '0' && *format <= '9')
|
||||
prec = 10*prec + (*format++ - '0');
|
||||
|
||||
int ll = 0;
|
||||
while (*format == 'l') {
|
||||
ll++;
|
||||
format++;
|
||||
}
|
||||
|
||||
int radix = 10;
|
||||
int is_signed = 1;
|
||||
|
||||
switch (*format++) {
|
||||
case '%':
|
||||
put('%');
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
put(va_arg(ap, int));
|
||||
break;
|
||||
|
||||
case 's':
|
||||
;
|
||||
char *s = va_arg(ap, char *);
|
||||
while (*s)
|
||||
put(*s++);
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
radix = 16;
|
||||
|
||||
case 'u':
|
||||
is_signed = 0;
|
||||
|
||||
case 'd':
|
||||
;
|
||||
u64 x;
|
||||
if (is_signed) {
|
||||
if (ll == 0)
|
||||
x = va_arg(ap, int);
|
||||
else if (ll == 1)
|
||||
x = va_arg(ap, long);
|
||||
else
|
||||
x = va_arg(ap, long long);
|
||||
} else {
|
||||
if (ll == 0)
|
||||
x = va_arg(ap, unsigned int);
|
||||
else if (ll == 1)
|
||||
x = va_arg(ap, unsigned long);
|
||||
else
|
||||
x = va_arg(ap, unsigned long long);
|
||||
}
|
||||
|
||||
if (is_signed) {
|
||||
if ((long long)x < 0)
|
||||
x = -x;
|
||||
else
|
||||
is_signed = 0;
|
||||
}
|
||||
|
||||
char hold[22];
|
||||
char *hld = &hold[sizeof hold];
|
||||
*--hld = 0;
|
||||
|
||||
int len = 0;
|
||||
do {
|
||||
int dig = extract_dig(&x, radix);
|
||||
if (dig >= 10)
|
||||
dig += 'a' - 10;
|
||||
else
|
||||
dig += '0';
|
||||
*--hld = dig;
|
||||
len++;
|
||||
} while (x);
|
||||
if (is_signed)
|
||||
*--hld = '-';
|
||||
|
||||
while (len < prec) {
|
||||
put(zero ? '0' : ' ');
|
||||
len++;
|
||||
}
|
||||
while (*hld)
|
||||
put(*hld++);
|
||||
}
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
|
||||
return 0;
|
||||
}
|
25
crt0.s
Normal file
25
crt0.s
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
# 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
|
||||
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
|
||||
# Disable interrupts, enable FP.
|
||||
mfmsr 3 ; rlwinm 3,3,0,17,15 ; ori 3,3,0x2000 ; mtmsr 3 ; isync
|
||||
|
||||
# Setup stack.
|
||||
lis 1,_stack_top@ha ; addi 1,1,_stack_top@l ; li 0,0 ; stwu 0,-64(1)
|
||||
|
||||
# Clear BSS.
|
||||
lis 3,__bss_start@ha ; addi 3,3,__bss_start@l
|
||||
li 4,0
|
||||
lis 5,__bss_end@ha ; addi 5,5,__bss_end@l ; sub 5,5,3
|
||||
bl memset
|
||||
|
||||
# Go!
|
||||
bl main
|
||||
|
||||
# If it returns, hang. Shouldn't happen.
|
||||
b .
|
47
elf.c
Normal file
47
elf.c
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
|
||||
// Determine if a valid ELF image exists at the given memory location.
|
||||
|
||||
int valid_elf_image(void *addr)
|
||||
{
|
||||
u32 *header = addr;
|
||||
|
||||
return header[0] == 0x7f454c46 // ELF
|
||||
&& header[1] == 0x01020100 // 32-bit, BE, ELF v1, SVR
|
||||
&& header[4] == 0x00020014 // executable, PowerPC
|
||||
&& header[5] == 1 // object file v1
|
||||
&& (header[10] & 0xffff) == 32; // PHDR size
|
||||
}
|
||||
|
||||
|
||||
// Returns the entry point address.
|
||||
|
||||
void *load_elf_image(void *addr)
|
||||
{
|
||||
u32 *header = addr;
|
||||
u32 *phdr = addr + header[7];
|
||||
u32 n = header[11] >> 16;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < n; i++, phdr += 8) {
|
||||
if (phdr[0] != 1) // PT_LOAD
|
||||
continue;
|
||||
|
||||
u32 off = phdr[1];
|
||||
void *dest = (void *)phdr[3];
|
||||
u32 filesz = phdr[4];
|
||||
u32 memsz = phdr[5];
|
||||
|
||||
memcpy(dest, addr + off, filesz);
|
||||
memset(dest + filesz, 0, memsz - filesz);
|
||||
|
||||
sync_before_exec(dest, memsz);
|
||||
}
|
||||
|
||||
return (void *)header[6];
|
||||
}
|
54
exception.c
Normal file
54
exception.c
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
extern char exception_2200_start, exception_2200_end;
|
||||
|
||||
void exception_handler(int exception)
|
||||
{
|
||||
u32 *x;
|
||||
u32 i;
|
||||
|
||||
printf("\nException %04x occurred!\n", exception);
|
||||
|
||||
x = (u32 *)0x80002000;
|
||||
|
||||
printf("\n R0..R7 R8..R15 R16..R23 R24..R31\n");
|
||||
for (i = 0; i < 8; i++) {
|
||||
printf("%08x %08x %08x %08x\n", x[0], x[8], x[16], x[24]);
|
||||
x++;
|
||||
}
|
||||
x += 24;
|
||||
|
||||
printf("\n CR/XER LR/CTR SRR0/SRR1 DAR/DSISR\n");
|
||||
for (i = 0; i < 2; i++) {
|
||||
printf("%08x %08x %08x %08x\n", x[0], x[2], x[4], x[6]);
|
||||
x++;
|
||||
}
|
||||
|
||||
// Hang.
|
||||
for (;;)
|
||||
;
|
||||
}
|
||||
|
||||
void exception_init(void)
|
||||
{
|
||||
u32 vector;
|
||||
u32 len_2200;
|
||||
|
||||
for (vector = 0x100; vector < 0x2000; vector += 0x10) {
|
||||
u32 *insn = (u32 *)(0x80000000 + vector);
|
||||
|
||||
insn[0] = 0xbc002000; // stmw 0,0x2000(0)
|
||||
insn[1] = 0x38600000 | (u32)vector; // li 3,vector
|
||||
insn[2] = 0x48002202; // ba 0x2200
|
||||
insn[3] = 0;
|
||||
}
|
||||
sync_before_exec((void *)0x80000100, 0x1f00);
|
||||
|
||||
len_2200 = &exception_2200_end - &exception_2200_start;
|
||||
memcpy((void *)0x80002200, &exception_2200_start, len_2200);
|
||||
sync_before_exec((void *)0x80002200, len_2200);
|
||||
}
|
24
exception_2200.s
Normal file
24
exception_2200.s
Normal file
@ -0,0 +1,24 @@
|
||||
# Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
# 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
|
||||
|
||||
.globl exception_2200_start, exception_2200_end
|
||||
|
||||
exception_2200_start:
|
||||
# store all interesting regs
|
||||
mfcr 0 ; stw 0,0x2080(0)
|
||||
mfxer 0 ; stw 0,0x2084(0)
|
||||
mflr 0 ; stw 0,0x2088(0)
|
||||
mfctr 0 ; stw 0,0x208c(0)
|
||||
mfsrr0 0 ; stw 0,0x2090(0)
|
||||
mfsrr1 0 ; stw 0,0x2094(0)
|
||||
mfdar 0 ; stw 0,0x2098(0)
|
||||
mfdsisr 0 ; stw 0,0x209c(0)
|
||||
|
||||
# switch on FP, DR, IR
|
||||
mfmsr 0 ; ori 0,0,0x2030 ; mtsrr1 0
|
||||
|
||||
# go to C handler
|
||||
lis 0,exception_handler@h ; ori 0,0,exception_handler@l ; mtsrr0 0
|
||||
rfi
|
||||
exception_2200_end:
|
425
fat.c
Normal file
425
fat.c
Normal file
@ -0,0 +1,425 @@
|
||||
// Copyright 2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
#ifdef FAT_TEST
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define RAW_BUF 0x200
|
||||
static u8 raw_buf[RAW_BUF] __attribute__((aligned(32)));
|
||||
|
||||
static int raw_read(u32 sector)
|
||||
{
|
||||
static u32 current = -1;
|
||||
|
||||
if (current == sector)
|
||||
return 0;
|
||||
current = sector;
|
||||
|
||||
return sd_read_sector(raw_buf, sector);
|
||||
}
|
||||
|
||||
static u64 partition_start_offset;
|
||||
|
||||
static int read(u8 *data, u64 offset, u32 len)
|
||||
{
|
||||
offset += partition_start_offset;
|
||||
|
||||
while (len) {
|
||||
u32 buf_off = offset % RAW_BUF;
|
||||
u32 n;
|
||||
|
||||
n = RAW_BUF - buf_off;
|
||||
if (n > len)
|
||||
n = len;
|
||||
|
||||
int err = raw_read(offset / RAW_BUF);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(data, raw_buf + buf_off, n);
|
||||
|
||||
data += n;
|
||||
offset += n;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static u32 bytes_per_cluster;
|
||||
static u32 root_entries;
|
||||
static u32 clusters;
|
||||
static u32 fat_type; // 12, 16, or 32
|
||||
|
||||
static u64 fat_offset;
|
||||
static u64 root_offset;
|
||||
static u64 data_offset;
|
||||
|
||||
|
||||
static u32 get_fat(u32 cluster)
|
||||
{
|
||||
u8 fat[4];
|
||||
|
||||
u32 offset_bits = cluster*fat_type;
|
||||
int err = read(fat, fat_offset + offset_bits/8, 4);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
u32 res = le32(fat) >> (offset_bits % 8);
|
||||
res &= (1 << fat_type) - 1;
|
||||
res &= 0x0fffffff; // for FAT32
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static u64 extent_offset;
|
||||
static u32 extent_len;
|
||||
static u32 extent_next_cluster;
|
||||
|
||||
static void get_extent(u32 cluster)
|
||||
{
|
||||
extent_len = 0;
|
||||
extent_next_cluster = 0;
|
||||
|
||||
if (cluster == 0) { // Root directory.
|
||||
if (fat_type != 32) {
|
||||
extent_offset = root_offset;
|
||||
extent_len = 0x20*root_entries;
|
||||
|
||||
return;
|
||||
}
|
||||
cluster = root_offset;
|
||||
}
|
||||
|
||||
if (cluster - 2 >= clusters)
|
||||
return;
|
||||
|
||||
extent_offset = data_offset + (u64)bytes_per_cluster*(cluster - 2);
|
||||
|
||||
for (;;) {
|
||||
extent_len += bytes_per_cluster;
|
||||
|
||||
u32 next_cluster = get_fat(cluster);
|
||||
|
||||
if (next_cluster - 2 >= clusters)
|
||||
break;
|
||||
|
||||
if (next_cluster != cluster + 1) {
|
||||
extent_next_cluster = next_cluster;
|
||||
break;
|
||||
}
|
||||
|
||||
cluster = next_cluster;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int read_extent(u8 *data, u32 len)
|
||||
{
|
||||
while (len) {
|
||||
if (extent_len == 0)
|
||||
return -1;
|
||||
|
||||
u32 this = len;
|
||||
if (this > extent_len)
|
||||
this = extent_len;
|
||||
|
||||
int err = read(data, extent_offset, this);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
extent_offset += this;
|
||||
extent_len -= this;
|
||||
|
||||
data += this;
|
||||
len -= this;
|
||||
|
||||
if (extent_len == 0 && extent_next_cluster)
|
||||
get_extent(extent_next_cluster);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int fat_read(void *data, u32 len)
|
||||
{
|
||||
return read_extent(data, len);
|
||||
}
|
||||
|
||||
|
||||
static u8 fat_name[11];
|
||||
|
||||
static u8 ucase(char c)
|
||||
{
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return c - 'a' + 'A';
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static const char *parse_component(const char *path)
|
||||
{
|
||||
u32 i = 0;
|
||||
|
||||
while (*path == '/')
|
||||
path++;
|
||||
|
||||
while (*path && *path != '/' && *path != '.') {
|
||||
if (i < 8)
|
||||
fat_name[i++] = ucase(*path);
|
||||
path++;
|
||||
}
|
||||
|
||||
while (i < 8)
|
||||
fat_name[i++] = ' ';
|
||||
|
||||
if (*path == '.')
|
||||
path++;
|
||||
|
||||
while (*path && *path != '/') {
|
||||
if (i < 11)
|
||||
fat_name[i++] = ucase(*path);
|
||||
path++;
|
||||
}
|
||||
|
||||
while (i < 11)
|
||||
fat_name[i++] = ' ';
|
||||
|
||||
if (fat_name[0] == 0xe5)
|
||||
fat_name[0] = 0x05;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
u32 fat_file_size;
|
||||
|
||||
int fat_open(const char *name)
|
||||
{
|
||||
u32 cluster = 0;
|
||||
|
||||
while (*name) {
|
||||
get_extent(cluster);
|
||||
|
||||
name = parse_component(name);
|
||||
|
||||
while (extent_len) {
|
||||
u8 dir[0x20];
|
||||
|
||||
int err = read_extent(dir, 0x20);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (dir[0] == 0)
|
||||
return -1;
|
||||
|
||||
if (dir[0x0b] & 0x08) // volume label or LFN
|
||||
continue;
|
||||
if (dir[0x00] == 0xe5) // deleted file
|
||||
continue;
|
||||
|
||||
if (!!*name != !!(dir[0x0b] & 0x10)) // dir vs. file
|
||||
continue;
|
||||
|
||||
if (memcmp(fat_name, dir, 11) == 0) {
|
||||
cluster = le16(dir + 0x1a);
|
||||
if (fat_type == 32)
|
||||
cluster |= le16(dir + 0x14) << 16;
|
||||
|
||||
if (*name == 0) {
|
||||
fat_file_size = le32(dir + 0x1c);
|
||||
get_extent(cluster);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef FAT_TEST
|
||||
static void print_dir_entry(u8 *dir)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
if (dir[0x0b] & 0x08) // volume label or LFN
|
||||
return;
|
||||
if (dir[0x00] == 0xe5) // deleted file
|
||||
return;
|
||||
|
||||
if (fat_type == 32) {
|
||||
fprintf(stderr, "#%04x", le16(dir + 0x14));
|
||||
fprintf(stderr, "%04x ", le16(dir + 0x1a));
|
||||
} else
|
||||
fprintf(stderr, "#%04x ", le16(dir + 0x1a)); // start cluster
|
||||
u16 date = le16(dir + 0x18);
|
||||
fprintf(stderr, "%04d-%02d-%02d ", 1980 + (date >> 9), (date >> 5) & 0x0f, date & 0x1f);
|
||||
u16 time = le16(dir + 0x16);
|
||||
fprintf(stderr, "%02d:%02d:%02d ", time >> 11, (time >> 5) & 0x3f, 2*(time & 0x1f));
|
||||
fprintf(stderr, "%10d ", le32(dir + 0x1c)); // file size
|
||||
u8 attr = dir[0x0b];
|
||||
for (i = 0; i < 6; i++)
|
||||
fprintf(stderr, "%c", (attr & (1 << i)) ? "RHSLDA"[i] : ' ');
|
||||
fprintf(stderr, " ");
|
||||
for (n = 8; n && dir[n - 1] == ' '; n--)
|
||||
;
|
||||
for (i = 0; i < n; i++)
|
||||
fprintf(stderr, "%c", dir[i]);
|
||||
for (n = 3; n && dir[8 + n - 1] == ' '; n--)
|
||||
;
|
||||
if (n) {
|
||||
fprintf(stderr, ".");
|
||||
for (i = 0; i < n; i++)
|
||||
fprintf(stderr, "%c", dir[8 + i]);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
|
||||
int print_dir(u32 cluster)
|
||||
{
|
||||
u8 dir[0x20];
|
||||
|
||||
get_extent(cluster);
|
||||
|
||||
while (extent_len) {
|
||||
int err = read_extent(dir, 0x20);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (dir[0] == 0)
|
||||
break;
|
||||
|
||||
print_dir_entry(dir);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int fat_init_fs(const u8 *sb)
|
||||
{
|
||||
u32 bytes_per_sector = le16(sb + 0x0b);
|
||||
u32 sectors_per_cluster = sb[0x0d];
|
||||
bytes_per_cluster = bytes_per_sector * sectors_per_cluster;
|
||||
|
||||
u32 reserved_sectors = le16(sb + 0x0e);
|
||||
u32 fats = sb[0x10];
|
||||
root_entries = le16(sb + 0x11);
|
||||
u32 total_sectors = le16(sb + 0x13);
|
||||
u32 sectors_per_fat = le16(sb + 0x16);
|
||||
|
||||
// For FAT16 and FAT32:
|
||||
if (total_sectors == 0)
|
||||
total_sectors = le32(sb + 0x20);
|
||||
|
||||
// For FAT32:
|
||||
if (sectors_per_fat == 0)
|
||||
sectors_per_fat = le32(sb + 0x24);
|
||||
|
||||
// XXX: For FAT32, we might want to look at offsets 28, 2a
|
||||
// XXX: We _do_ need to look at 2c
|
||||
|
||||
u32 fat_sectors = sectors_per_fat * fats;
|
||||
u32 root_sectors = (0x20*root_entries + bytes_per_sector - 1)
|
||||
/ bytes_per_sector;
|
||||
|
||||
u32 fat_start_sector = reserved_sectors;
|
||||
u32 root_start_sector = fat_start_sector + fat_sectors;
|
||||
u32 data_start_sector = root_start_sector + root_sectors;
|
||||
|
||||
clusters = (total_sectors - data_start_sector) / sectors_per_cluster;
|
||||
|
||||
if (clusters < 0x0ff5)
|
||||
fat_type = 12;
|
||||
else if (clusters < 0xfff5)
|
||||
fat_type = 16;
|
||||
else
|
||||
fat_type = 32;
|
||||
|
||||
fat_offset = (u64)bytes_per_sector*fat_start_sector;
|
||||
root_offset = (u64)bytes_per_sector*root_start_sector;
|
||||
data_offset = (u64)bytes_per_sector*data_start_sector;
|
||||
|
||||
if (fat_type == 32)
|
||||
root_offset = le32(sb + 0x2c);
|
||||
|
||||
#ifdef FAT_TEST
|
||||
fprintf(stderr, "bytes_per_sector = %08x\n", bytes_per_sector);
|
||||
fprintf(stderr, "sectors_per_cluster = %08x\n", sectors_per_cluster);
|
||||
fprintf(stderr, "bytes_per_cluster = %08x\n", bytes_per_cluster);
|
||||
fprintf(stderr, "root_entries = %08x\n", root_entries);
|
||||
fprintf(stderr, "clusters = %08x\n", clusters);
|
||||
fprintf(stderr, "fat_type = %08x\n", fat_type);
|
||||
fprintf(stderr, "fat_offset = %012llx\n", fat_offset);
|
||||
fprintf(stderr, "root_offset = %012llx\n", root_offset);
|
||||
fprintf(stderr, "data_offset = %012llx\n", data_offset);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int is_fat_fs(const u8 *sb)
|
||||
{
|
||||
// Bytes per sector should be 512, 1024, 2048, or 4096
|
||||
u32 bps = le16(sb + 0x0b);
|
||||
if (bps < 0x0200 || bps > 0x1000 || bps & (bps - 1))
|
||||
return 0;
|
||||
|
||||
// Media type should be f0 or f8,...,ff
|
||||
if (sb[0x15] < 0xf8 && sb[0x15] != 0xf0)
|
||||
return 0;
|
||||
|
||||
// If those checks didn't fail, it's FAT. We hope.
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int fat_init(void)
|
||||
{
|
||||
u8 buf[0x200];
|
||||
int err;
|
||||
|
||||
partition_start_offset = 0;
|
||||
err = read(buf, 0, 0x200);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (le16(buf + 0x01fe) != 0xaa55) // Not a DOS disk.
|
||||
return -1;
|
||||
|
||||
if (is_fat_fs(buf))
|
||||
return fat_init_fs(buf);
|
||||
|
||||
// Maybe there's a partition table? Let's try the first partition.
|
||||
if (buf[0x01c2] == 0)
|
||||
return -1;
|
||||
|
||||
partition_start_offset = 0x200ULL*le32(buf + 0x01c6);
|
||||
|
||||
err = read(buf, 0, 0x200);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (is_fat_fs(buf))
|
||||
return fat_init_fs(buf);
|
||||
|
||||
return -1;
|
||||
}
|
60
font2c.pl
Executable file
60
font2c.pl
Executable file
@ -0,0 +1,60 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
|
||||
# Read PPM file.
|
||||
|
||||
$sig = <>; chomp $sig;
|
||||
$sizes = <>; chomp $sizes;
|
||||
$cols = <>; chomp $cols;
|
||||
|
||||
{
|
||||
local $/;
|
||||
$data = <>;
|
||||
}
|
||||
|
||||
|
||||
# Sanity check.
|
||||
|
||||
$sig ne "P6" and die;
|
||||
$sizes ne "90 256" and die;
|
||||
$cols ne "255" and die;
|
||||
(length $data) != 3 * 90 * 256 and die;
|
||||
|
||||
|
||||
# Output header.
|
||||
|
||||
print "// GENERATED FILE DO NOT EDIT\n";
|
||||
print "\n";
|
||||
print "#include \"loader.h\"\n";
|
||||
print "\n";
|
||||
print "const u8 console_font_10x16x4[96*80] = {\n";
|
||||
|
||||
# Output data.
|
||||
|
||||
for my $ch (2..7) {
|
||||
for my $cl (0..15) {
|
||||
printf "\n\t// %x%x\n", $ch, $cl;
|
||||
for my $py (0..15) {
|
||||
print "\t";
|
||||
for my $px (0..9) {
|
||||
my $hor = $px + 10*($ch - 2);
|
||||
my $ver = $py + 16*$cl;
|
||||
my $wot = $hor + 90*$ver;
|
||||
my $bytes = substr($data, 3*$wot, 3);
|
||||
my $nyb = int ((ord $bytes) / 16);
|
||||
if (($px & 1) == 0) {
|
||||
printf "0x%x", $nyb;
|
||||
} else {
|
||||
printf "%x,", $nyb;
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Output footer.
|
||||
|
||||
print "\n";
|
||||
print "};\n";
|
377
ios.c
Normal file
377
ios.c
Normal file
@ -0,0 +1,377 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
|
||||
// Low-level IPC access.
|
||||
|
||||
static u32 ipc_read(u32 reg)
|
||||
{
|
||||
return read32(0x0d000000 + 4*reg);
|
||||
}
|
||||
|
||||
static void ipc_write(u32 reg, u32 value)
|
||||
{
|
||||
write32(0x0d000000 + 4*reg, value);
|
||||
}
|
||||
|
||||
static void ipc_bell(u32 w)
|
||||
{
|
||||
ipc_write(1, (ipc_read(1) & 0x30) | w);
|
||||
}
|
||||
|
||||
static void ipc_wait_ack(void)
|
||||
{
|
||||
while ((ipc_read(1) & 0x22) != 0x22)
|
||||
;
|
||||
}
|
||||
|
||||
static void ipc_wait_reply(void)
|
||||
{
|
||||
while ((ipc_read(1) & 0x14) != 0x14)
|
||||
;
|
||||
}
|
||||
|
||||
static void ipc_irq_ack(void)
|
||||
{
|
||||
ipc_write(12, 0x40000000);
|
||||
}
|
||||
|
||||
|
||||
// Mid-level IPC access.
|
||||
|
||||
static struct {
|
||||
u32 cmd;
|
||||
int result;
|
||||
int fd;
|
||||
u32 arg[5];
|
||||
|
||||
u32 user[8];
|
||||
} ipc __attribute__((aligned(64)));
|
||||
|
||||
static void ipc_send_request(void)
|
||||
{
|
||||
sync_after_write(&ipc, 0x40);
|
||||
|
||||
ipc_write(0, virt_to_phys(&ipc));
|
||||
ipc_bell(1);
|
||||
|
||||
ipc_wait_ack();
|
||||
|
||||
ipc_bell(2);
|
||||
ipc_irq_ack();
|
||||
}
|
||||
|
||||
static void ipc_recv_reply(void)
|
||||
{
|
||||
for (;;) {
|
||||
u32 reply;
|
||||
|
||||
ipc_wait_reply();
|
||||
|
||||
reply = ipc_read(2);
|
||||
ipc_bell(4);
|
||||
|
||||
ipc_irq_ack();
|
||||
ipc_bell(8);
|
||||
|
||||
if (reply == virt_to_phys(&ipc))
|
||||
break;
|
||||
}
|
||||
|
||||
sync_before_read(&ipc, sizeof ipc);
|
||||
}
|
||||
|
||||
|
||||
// High-level IPC access.
|
||||
|
||||
int ios_open(const char *filename, u32 mode)
|
||||
{
|
||||
sync_after_write(filename, strlen(filename) + 1);
|
||||
memset(&ipc, 0, sizeof ipc);
|
||||
|
||||
ipc.cmd = 1;
|
||||
ipc.fd = 0;
|
||||
ipc.arg[0] = virt_to_phys(filename);
|
||||
ipc.arg[1] = mode;
|
||||
|
||||
ipc_send_request();
|
||||
ipc_recv_reply();
|
||||
|
||||
return ipc.result;
|
||||
}
|
||||
|
||||
int ios_close(int fd)
|
||||
{
|
||||
memset(&ipc, 0, sizeof ipc);
|
||||
|
||||
ipc.cmd = 2;
|
||||
ipc.fd = fd;
|
||||
|
||||
ipc_send_request();
|
||||
ipc_recv_reply();
|
||||
|
||||
return ipc.result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int ios_read(int fd, void *data, u32 len)
|
||||
{
|
||||
memset(&ipc, 0, sizeof ipc);
|
||||
|
||||
ipc.cmd = 3;
|
||||
ipc.fd = fd;
|
||||
ipc.arg[0] = virt_to_phys(data);
|
||||
ipc.arg[1] = len;
|
||||
|
||||
ipc_send_request();
|
||||
ipc_recv_reply();
|
||||
|
||||
if (data)
|
||||
sync_before_read(data, len);
|
||||
|
||||
return ipc.result;
|
||||
}
|
||||
|
||||
int ios_seek(int fd, int where, int whence)
|
||||
{
|
||||
memset(&ipc, 0, sizeof ipc);
|
||||
|
||||
ipc.cmd = 5;
|
||||
ipc.fd = fd;
|
||||
ipc.arg[0] = where;
|
||||
ipc.arg[1] = whence;
|
||||
|
||||
ipc_send_request();
|
||||
ipc_recv_reply();
|
||||
|
||||
return ipc.result;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ios_ioctl(int fd, u32 n, const void *in, u32 inlen, void *out, u32 outlen)
|
||||
{
|
||||
memset(&ipc, 0, sizeof ipc);
|
||||
|
||||
if (in)
|
||||
sync_after_write(in, inlen);
|
||||
if (out)
|
||||
sync_after_write(out, outlen);
|
||||
|
||||
ipc.cmd = 6;
|
||||
ipc.fd = fd;
|
||||
ipc.arg[0] = n;
|
||||
ipc.arg[1] = virt_to_phys(in);
|
||||
ipc.arg[2] = inlen;
|
||||
ipc.arg[3] = virt_to_phys(out);
|
||||
ipc.arg[4] = outlen;
|
||||
|
||||
ipc_send_request();
|
||||
ipc_recv_reply();
|
||||
|
||||
if (out)
|
||||
sync_before_read(out, outlen);
|
||||
|
||||
return ipc.result;
|
||||
}
|
||||
|
||||
int __ios_ioctlv(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec, int wait_for_reply) {
|
||||
u32 i;
|
||||
|
||||
memset(&ipc, 0, sizeof ipc);
|
||||
|
||||
for (i = 0; i < in_count + out_count; i++)
|
||||
if (vec[i].data) {
|
||||
sync_after_write(vec[i].data, vec[i].len);
|
||||
vec[i].data = (void *)virt_to_phys(vec[i].data);
|
||||
}
|
||||
|
||||
sync_after_write(vec, (in_count + out_count) * sizeof *vec);
|
||||
|
||||
ipc.cmd = 7;
|
||||
ipc.fd = fd;
|
||||
ipc.arg[0] = n;
|
||||
ipc.arg[1] = in_count;
|
||||
ipc.arg[2] = out_count;
|
||||
ipc.arg[3] = virt_to_phys(vec);
|
||||
|
||||
ipc_send_request();
|
||||
if (wait_for_reply) {
|
||||
ipc_recv_reply();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = in_count; i < in_count + out_count; i++)
|
||||
if (vec[i].data) {
|
||||
vec[i].data = phys_to_virt((u32)vec[i].data);
|
||||
sync_before_read(vec[i].data, vec[i].len);
|
||||
}
|
||||
|
||||
return ipc.result;
|
||||
}
|
||||
|
||||
int ios_ioctlv(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec)
|
||||
{
|
||||
__ios_ioctlv(fd, n, in_count, out_count, vec, 1);
|
||||
}
|
||||
|
||||
// We don't really need the fields but it's just easier to copy them.
|
||||
// Taken from https://github.com/devkitPro/libogc/blob/master/gc/ogc/es.h
|
||||
typedef struct _tiklimit {
|
||||
u32 tag;
|
||||
u32 value;
|
||||
} __attribute__((packed)) tiklimit;
|
||||
|
||||
typedef struct _tikview {
|
||||
u32 view;
|
||||
u64 ticketid;
|
||||
u32 devicetype;
|
||||
u64 titleid;
|
||||
u16 access_mask;
|
||||
u8 reserved[0x3c];
|
||||
u8 cidx_mask[0x40];
|
||||
u16 padding;
|
||||
tiklimit limits[8];
|
||||
} __attribute__((packed)) tikview;
|
||||
|
||||
#define MAX_IPC_RETRIES 400
|
||||
|
||||
// Written from __IOS_LaunchNewIOS of https://github.com/devkitPro/libogc/blob/master/libogc/ios.c
|
||||
int ios_reload(int version) {
|
||||
int ret = 0;
|
||||
int es_fd = ios_open("/dev/es", 0);
|
||||
if (es_fd < 0) {
|
||||
printf("Failed to open ES: %d\n", es_fd);
|
||||
return es_fd;
|
||||
}
|
||||
|
||||
STACK_ALIGN(struct ioctlv, vec, 3, 32);
|
||||
STACK_ALIGN(u64, titleID, 1, 32);
|
||||
STACK_ALIGN(u32, view_count, 1, 32);
|
||||
STACK_ALIGN(tikview, views, 4, 32);
|
||||
|
||||
*titleID = 0x100000000LL | version;
|
||||
|
||||
vec[0].data = titleID;
|
||||
vec[0].len = 8;
|
||||
vec[1].data = view_count;
|
||||
vec[1].len = 4;
|
||||
|
||||
// Do ES_GetNumTicketViews
|
||||
ret = ios_ioctlv(es_fd, 0x12, 1, 1, vec);
|
||||
if (ret < 0) {
|
||||
printf("ios_ioctlv failed on ES_GetNumTicketViews: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (*view_count > 4 || *view_count < 1) {
|
||||
printf("Invalid number of views: %d\n", *view_count);
|
||||
return -1;
|
||||
}
|
||||
|
||||
vec[0].data = titleID;
|
||||
vec[0].len = 8;
|
||||
vec[1].data = view_count;
|
||||
vec[1].len = 4;
|
||||
vec[2].data = views;
|
||||
vec[2].len = sizeof(tikview) * (*view_count);
|
||||
|
||||
// Do ES_GetTicketViews
|
||||
ret = ios_ioctlv(es_fd, 0x13, 2, 1, vec);
|
||||
if (ret < 0) {
|
||||
printf("ios_ioctlv failed on ES_GetTicketViews: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
write32(0x80003140, 0);
|
||||
|
||||
vec[0].data = titleID;
|
||||
vec[0].len = 8;
|
||||
vec[1].data = &views[0];
|
||||
vec[1].len = sizeof(tikview);
|
||||
|
||||
// Do ES_LaunchTitleBackground
|
||||
ret = __ios_ioctlv(es_fd, 0x8, 2, 0, vec, 0);
|
||||
if (ret < 0) {
|
||||
printf("ios_ioctlv failed on ES_LaunchTitleBackground: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
printf("Waiting for IPC startup... ");
|
||||
for (int counter = 0; !(read32(0x0d000004) & 2); counter++) {
|
||||
udelay(10000);
|
||||
|
||||
if (counter >= MAX_IPC_RETRIES) {
|
||||
printf("FAIL!\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
printf("OK.\n");
|
||||
|
||||
ipc_write(1,56);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Cleanup any old state.
|
||||
|
||||
static void ipc_cleanup_reply(void)
|
||||
{
|
||||
if ((ipc_read(1) & 0x14) != 0x14)
|
||||
return;
|
||||
|
||||
ipc_read(2);
|
||||
ipc_bell(4);
|
||||
|
||||
ipc_irq_ack();
|
||||
ipc_bell(8);
|
||||
}
|
||||
|
||||
static void ipc_cleanup_request(void)
|
||||
{
|
||||
if ((ipc_read(1) & 0x22) == 0x22)
|
||||
ipc_bell(2);
|
||||
}
|
||||
|
||||
static void release_old_stm_callback(void)
|
||||
{
|
||||
*((u32 *)0x80000018) = 0x00000014;
|
||||
sync_after_write((void*)0x80000014, 8);
|
||||
|
||||
int fd = ios_open("/dev/stm/immediate",0);
|
||||
if (fd < 0) {
|
||||
printf("STM Immediate open failed!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int err = ios_ioctl(fd, 0x3002, 0, 0, 0, 0);
|
||||
if (err < 0 && err != -6)
|
||||
printf("Eventhook release failed with code %d\n", err);
|
||||
|
||||
ios_close(fd);
|
||||
}
|
||||
|
||||
void reset_ios(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
//printf("Flushing IPC transactions");
|
||||
for (i = 0; i < 10; i++) {
|
||||
ipc_cleanup_request();
|
||||
ipc_cleanup_reply();
|
||||
ipc_irq_ack();
|
||||
udelay(1000);
|
||||
//printf(".");
|
||||
}
|
||||
//printf(" Done.\n");
|
||||
|
||||
//printf("Closing file descriptors...");
|
||||
for (i = 0; i < 32; i++)
|
||||
ios_close(i);
|
||||
//printf(" Done.\n");
|
||||
|
||||
release_old_stm_callback();
|
||||
}
|
179
loader.h
Normal file
179
loader.h
Normal file
@ -0,0 +1,179 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// 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 _LOADER_H
|
||||
#define _LOADER_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
// String functions.
|
||||
|
||||
size_t strlen(const char *);
|
||||
size_t strnlen(const char *, size_t);
|
||||
void *memset(void *, int, size_t);
|
||||
void *memcpy(void *, const void *, size_t);
|
||||
int memcmp(const void *, const void *, size_t);
|
||||
|
||||
|
||||
// Basic types.
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short int u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long int u64;
|
||||
|
||||
static inline u16 le16(const u8 *p)
|
||||
{
|
||||
return p[0] | (p[1] << 8);
|
||||
}
|
||||
|
||||
static inline u32 le32(const u8 *p)
|
||||
{
|
||||
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
||||
}
|
||||
|
||||
|
||||
// Helper macros
|
||||
|
||||
// courtesy of Marcan
|
||||
#define STACK_ALIGN(type, name, cnt, alignment) u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + (((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - ((sizeof(type)*(cnt))%(alignment))) : 0))]; \
|
||||
type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (((u32)(_al__##name))&((alignment)-1))))
|
||||
|
||||
|
||||
// Basic I/O.
|
||||
|
||||
static inline u32 read32(u32 addr)
|
||||
{
|
||||
u32 x;
|
||||
|
||||
asm volatile("lwz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr));
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline void write32(u32 addr, u32 x)
|
||||
{
|
||||
asm("stw %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr));
|
||||
}
|
||||
|
||||
static inline u16 read16(u32 addr)
|
||||
{
|
||||
u16 x;
|
||||
|
||||
asm volatile("lhz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr));
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline void write16(u32 addr, u16 x)
|
||||
{
|
||||
asm("sth %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr));
|
||||
}
|
||||
|
||||
|
||||
// Address mapping.
|
||||
|
||||
static inline u32 virt_to_phys(const void *p)
|
||||
{
|
||||
return (u32)p & 0x7fffffff;
|
||||
}
|
||||
|
||||
static inline void *phys_to_virt(u32 x)
|
||||
{
|
||||
return (void *)(x | 0x80000000);
|
||||
}
|
||||
|
||||
|
||||
// Cache synchronisation.
|
||||
|
||||
void sync_before_read(void *p, u32 len);
|
||||
void sync_after_write(const void *p, u32 len);
|
||||
void sync_before_exec(const void *p, u32 len);
|
||||
|
||||
|
||||
// Time.
|
||||
|
||||
void udelay(u32 us);
|
||||
|
||||
|
||||
// Special purpose registers.
|
||||
|
||||
#define mtspr(n, x) do { asm("mtspr %1,%0" : : "r"(x), "i"(n)); } while (0)
|
||||
#define mfspr(n) ({ \
|
||||
u32 x; asm volatile("mfspr %0,%1" : "=r"(x) : "i"(n)); x; \
|
||||
})
|
||||
|
||||
|
||||
// Exceptions.
|
||||
|
||||
void exception_init(void);
|
||||
|
||||
|
||||
// USB Gecko.
|
||||
|
||||
void usbgecko_init(void);
|
||||
int usbgecko_checkgecko(void);
|
||||
void usbgecko_console_putc(u8 c);
|
||||
|
||||
u8 usbgecko_flash_read8(u32 offset);
|
||||
u32 usbgecko_flash_read32(u32 offset);
|
||||
|
||||
|
||||
// Version string.
|
||||
|
||||
extern const char version[];
|
||||
|
||||
|
||||
// Video.
|
||||
|
||||
void video_init(void);
|
||||
void fb_putc(char);
|
||||
|
||||
|
||||
// Console.
|
||||
|
||||
void console_init(void);
|
||||
int printf(const char *fmt, ...);
|
||||
|
||||
|
||||
// SD card.
|
||||
|
||||
int sd_init(void);
|
||||
int sd_read_sector(u8 *data, u32 offset);
|
||||
int sd_close(void);
|
||||
|
||||
|
||||
// FAT.
|
||||
|
||||
int fat_init(void);
|
||||
int fat_open(const char *name);
|
||||
int fat_read(void *data, u32 len);
|
||||
|
||||
|
||||
// ELF.
|
||||
|
||||
int valid_elf_image(void *addr);
|
||||
void *load_elf_image(void *addr);
|
||||
|
||||
|
||||
// IOS.
|
||||
|
||||
struct ioctlv {
|
||||
void *data;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
int ios_open(const char *filename, u32 mode);
|
||||
int ios_close(int fd);
|
||||
int ios_read(int fd, void *data, u32 len);
|
||||
int ios_write(int fd, const void *data, u32 len);
|
||||
int ios_seek(int fd, int where, int whence);
|
||||
int ios_ioctl(int fd, u32 n, const void *in, u32 inlen, void *out, u32 outlen);
|
||||
int ios_ioctlv(int fd, u32 n, u32 in_count, u32 out_count, struct ioctlv *vec);
|
||||
int ios_reload(int version);
|
||||
|
||||
void reset_ios(void);
|
||||
|
||||
#endif
|
27
loader.lds
Normal file
27
loader.lds
Normal file
@ -0,0 +1,27 @@
|
||||
/* Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
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 */
|
||||
|
||||
OUTPUT_FORMAT("elf32-powerpc")
|
||||
OUTPUT_ARCH(powerpc:common)
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
. = 0x93000000;
|
||||
|
||||
.start : { crt0.o(*) }
|
||||
.text : { *(.text) }
|
||||
.rodata : { *(.rodata .rodata.*)}
|
||||
.data : { *(.data) }
|
||||
|
||||
__bss_start = .;
|
||||
.bss : { *(.bss) }
|
||||
__bss_end = .;
|
||||
|
||||
. = ALIGN(0x40);
|
||||
.stack : {
|
||||
. += 0x8000;
|
||||
_stack_top = .;
|
||||
}
|
||||
}
|
163
main.c
Normal file
163
main.c
Normal file
@ -0,0 +1,163 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "loader.h"
|
||||
#include "stub.h"
|
||||
|
||||
|
||||
static u8 *const code_buffer = (u8 *)0x80040000;
|
||||
static u8 *const trampoline_buffer = (u8 *)0x80001800;
|
||||
|
||||
static void dsp_reset(void)
|
||||
{
|
||||
write16(0x0c00500a, read16(0x0c00500a) & ~0x01f8);
|
||||
write16(0x0c00500a, read16(0x0c00500a) | 0x0010);
|
||||
write16(0x0c005036, 0);
|
||||
}
|
||||
|
||||
static u32 reboot_trampoline[] = {
|
||||
0x3c209300, // lis 1,0x9300
|
||||
0x60210000, // ori 1,1,0x0000
|
||||
0x7c2903a6, // mtctr 1
|
||||
0x4e800420 // bctr
|
||||
};
|
||||
|
||||
int try_sd_load(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sd_init();
|
||||
if (err) {
|
||||
printf("SD card not found (%d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = fat_init();
|
||||
if (err == 0)
|
||||
printf("SD card detected\n");
|
||||
else {
|
||||
printf("SD card not detected (%d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// if (usbgecko_checkgecko())
|
||||
// printf("USBGecko serial interface detected\n");
|
||||
// else
|
||||
// printf("USBGecko serial interface not detected\n");
|
||||
|
||||
printf("Opening bootmii/armboot.bin:\n");
|
||||
err = fat_open("bootmii/armboot.bin");
|
||||
|
||||
if (err) {
|
||||
printf("bootmii/armboot.bin not found (%d)\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
extern u32 fat_file_size;
|
||||
|
||||
printf("reading %d bytes...\n", fat_file_size);
|
||||
err = fat_read(code_buffer, fat_file_size);
|
||||
if (err) {
|
||||
printf("Error %d reading file\n", err);
|
||||
return err;
|
||||
}
|
||||
sync_after_write(code_buffer, fat_file_size);
|
||||
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __eabi(void){}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
dsp_reset();
|
||||
|
||||
exception_init();
|
||||
|
||||
// Install trampoline at 80001800; some payloads like to jump
|
||||
// there to restart. Sometimes this can even work.
|
||||
memcpy(trampoline_buffer, reboot_trampoline, sizeof(reboot_trampoline));
|
||||
|
||||
// Clear interrupt mask.
|
||||
write32(0x0c003004, 0);
|
||||
|
||||
// Unlock EXI.
|
||||
write32(0x0d00643c, 0);
|
||||
|
||||
video_init();
|
||||
usbgecko_init();
|
||||
|
||||
printf("savezelda %s\n", version);
|
||||
printf("\n");
|
||||
printf("Copyright 2018-2020 Dexter Gerig\n");
|
||||
printf("Copyright 2008,2009 Segher Boessenkool\n");
|
||||
printf("Copyright 2008 Haxx Enterprises\n");
|
||||
printf("Copyright 2008 Hector Martin (\"marcan\")\n");
|
||||
printf("Copyright 2003,2004 Felix Domke\n");
|
||||
printf("\n");
|
||||
printf("This code is licensed to you under the terms of the\n");
|
||||
printf("GNU GPL, version 2; see the file COPYING\n");
|
||||
printf("\n");
|
||||
printf("Font and graphics by Freddy Leitner\n");
|
||||
printf("\n");
|
||||
printf("\n");
|
||||
|
||||
printf("Cleaning up environment... ");
|
||||
|
||||
reset_ios();
|
||||
|
||||
printf("OK.\n");
|
||||
|
||||
printf("Reloading to IOS80\n");
|
||||
int err = ios_reload(80);
|
||||
if (err) {
|
||||
printf("Fail, attempting to reload to IOS58\n");
|
||||
err = ios_reload(58);
|
||||
if (err) {
|
||||
printf("Sorry, the ES exploit is only tested on IOS80 or 58\n");
|
||||
goto hang;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
printf("Vuln discovery: metaconstruct\n");
|
||||
printf("Implementation: Fullmetal5\n");
|
||||
printf("Doing hax\n");
|
||||
|
||||
err = try_sd_load();
|
||||
|
||||
if (err) {
|
||||
printf("Couldn't load bootmii/armboot.bin\n");
|
||||
goto hang;
|
||||
}
|
||||
|
||||
int es_hndl = ios_open("/dev/es", 0);
|
||||
printf("es_hndl: %d\n", es_hndl);
|
||||
|
||||
STACK_ALIGN(struct ioctlv, hax, 2, 32);
|
||||
STACK_ALIGN(u32, a, 1, 32);
|
||||
*a = 0x00000000;
|
||||
|
||||
printf("Preping call\n");
|
||||
hax[0].data = a;
|
||||
hax[0].len = 4;
|
||||
hax[1].data = phys_to_virt(0x201125b0);
|
||||
hax[1].len = 0;
|
||||
|
||||
printf("Copying ES payload: 0x80010000:%x\n", stub_bin_len);
|
||||
memcpy(0x80010000, stub_bin, stub_bin_len);
|
||||
sync_after_write(0x80010000, stub_bin_len);
|
||||
|
||||
printf("Launching armboot\n");
|
||||
int ret = ios_ioctlv(es_hndl, 0xf, 1, 1, hax);
|
||||
printf("Um, you shouldn't see this, something has gone wrong!\n");
|
||||
|
||||
printf("ret: %d\n", ret);
|
||||
hang:
|
||||
for (;;)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
283
sd.c
Normal file
283
sd.c
Normal file
@ -0,0 +1,283 @@
|
||||
// Copyright 2008 Haxx Enterprises <bushing@gmail.com>
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
|
||||
static int fd;
|
||||
static u32 rca; // 16 bottom bits are stuff bits
|
||||
|
||||
|
||||
static int sd_hc_write8(u8 reg, u8 data)
|
||||
{
|
||||
u32 param[6];
|
||||
int err;
|
||||
|
||||
memset(param, 0, sizeof param);
|
||||
param[0] = reg;
|
||||
param[3] = 1; // reg size
|
||||
param[4] = data;
|
||||
|
||||
err = ios_ioctl(fd, 1, param, sizeof param, 0, 0);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sd_hc_read8(u8 reg, u8 *x)
|
||||
{
|
||||
u32 param[6];
|
||||
u32 data;
|
||||
int err;
|
||||
|
||||
memset(param, 0, sizeof param);
|
||||
param[0] = reg;
|
||||
param[3] = 1; // reg size
|
||||
param[4] = 0;
|
||||
|
||||
err = ios_ioctl(fd, 2, param, sizeof param, &data, sizeof data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*x = data;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sd_reset_card(void)
|
||||
{
|
||||
u32 reply;
|
||||
int err;
|
||||
|
||||
err = ios_ioctl(fd, 4, 0, 0, &reply, sizeof reply);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
rca = reply & 0xffff0000;
|
||||
|
||||
// printf("sd_reset_card(): got reply = %08x\n", reply);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_set_clock(void)
|
||||
{
|
||||
u32 clock;
|
||||
int err;
|
||||
|
||||
clock = 1; // half of the sdclk divisor: a power of two or zero,
|
||||
// should look at capabilities reg to compute this
|
||||
|
||||
err = ios_ioctl(fd, 6, &clock, sizeof clock, 0, 0);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sd_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
|
||||
u32 block_count, u32 block_size, void *addr,
|
||||
u32 *outreply, u32 reply_size)
|
||||
{
|
||||
u32 param[9];
|
||||
u32 reply[4];
|
||||
int err;
|
||||
|
||||
param[0] = cmd;
|
||||
param[1] = cmd_type;
|
||||
param[2] = resp_type;
|
||||
param[3] = arg;
|
||||
param[4] = block_count;
|
||||
param[5] = block_size;
|
||||
param[6] = (u32)addr;
|
||||
param[7] = 0; // ???
|
||||
param[8] = 0; // ???
|
||||
|
||||
err = ios_ioctl(fd, 7, param, sizeof param, reply, sizeof reply);
|
||||
|
||||
if (reply_size) // ???
|
||||
memcpy(outreply, reply, reply_size);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
#define TYPE_BC 1
|
||||
#define TYPE_BCR 2
|
||||
#define TYPE_AC 3
|
||||
#define TYPE_ADTC 4
|
||||
|
||||
#define RESPONSE_NONE 0
|
||||
#define RESPONSE_R1 1
|
||||
#define RESPONSE_R1B 2
|
||||
#define RESPONSE_R2 3
|
||||
#define RESPONSE_R3 4
|
||||
#define RESPONSE_R4 5
|
||||
#define RESPONSE_R5 6
|
||||
#define RESPONSE_R6 7
|
||||
|
||||
|
||||
static int sd_app_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
|
||||
u32 block_count, u32 block_size, void *addr,
|
||||
u32 *outreply, u32 reply_size)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sd_command(55, TYPE_AC, RESPONSE_R1, rca, 0, 0, 0, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sd_command(cmd, cmd_type, resp_type, arg,
|
||||
block_count, block_size, addr,
|
||||
outreply, reply_size);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sd_data_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg,
|
||||
u32 block_count, u32 block_size, void *data,
|
||||
u32 unk1, u32 unk2, u32 *outreply, u32 reply_size)
|
||||
{
|
||||
u32 param[9];
|
||||
u32 reply[4];
|
||||
struct ioctlv vec[3];
|
||||
int err;
|
||||
|
||||
param[0] = cmd;
|
||||
param[1] = cmd_type;
|
||||
param[2] = resp_type;
|
||||
param[3] = arg;
|
||||
param[4] = block_count;
|
||||
param[5] = block_size;
|
||||
param[6] = (u32)data;
|
||||
param[7] = unk1; // ???
|
||||
param[8] = unk2; // ???
|
||||
|
||||
vec[0].data = param;
|
||||
vec[0].len = sizeof param;
|
||||
vec[1].data = data;
|
||||
vec[1].len = block_count * block_size;
|
||||
vec[2].data = reply;
|
||||
vec[2].len = sizeof reply;
|
||||
|
||||
err = ios_ioctlv(fd, 7, 2, 1, vec);
|
||||
|
||||
if (reply_size) // ???
|
||||
memcpy(outreply, reply, reply_size);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sd_select(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
//printf("Selecting card:\n");
|
||||
err = sd_command(7, TYPE_AC, RESPONSE_R1B, rca, 0, 0, 0, 0, 0);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sd_set_blocklength(u32 len)
|
||||
{
|
||||
int err;
|
||||
|
||||
//printf("sd_set_blocklength(%u)\n", len);
|
||||
err = sd_command(16, TYPE_AC, RESPONSE_R1, len, 0, 0, 0, 0, 0);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sd_set_bus_width(int width)
|
||||
{
|
||||
u32 arg;
|
||||
u8 reg;
|
||||
int err;
|
||||
|
||||
// First notify the card.
|
||||
arg = (width == 4) ? 2 : 0;
|
||||
//printf("sd_set_bus_width()\n");
|
||||
err = sd_app_command(6, TYPE_AC, RESPONSE_R1, arg, 0, 0, 0, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
// Now change the Host Control Register.
|
||||
err = sd_hc_read8(0x28, ®);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
reg = (reg & ~2) | arg;
|
||||
|
||||
err = sd_hc_write8(0x28, reg);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int sd_read_sector(u8 *data, u32 offset)
|
||||
{
|
||||
u32 reply[4];
|
||||
int err;
|
||||
|
||||
if (offset >= 0x800000)
|
||||
return -1;
|
||||
|
||||
err = sd_data_command(18, TYPE_AC, RESPONSE_R1, 0x200 * offset,
|
||||
1, 0x200, data, 1, 0, reply, sizeof reply);
|
||||
|
||||
sync_before_read(data, 0x200);
|
||||
|
||||
//printf("READ block %d\r",offset);
|
||||
if (err)
|
||||
printf("SD READ %d: err=%08x, reply=%08x %08x %08x %08x\n",
|
||||
offset, err, reply[0], reply[1], reply[2], reply[3]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int sd_close(void)
|
||||
{
|
||||
return ios_close(fd);
|
||||
}
|
||||
|
||||
int sd_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
fd = ios_open("/dev/sdio/slot0", 0);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
err = sd_reset_card();
|
||||
if (err) {
|
||||
printf("SD Card not present? (%d)\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
// now in standby state
|
||||
|
||||
err = sd_select();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
// now in transfer state
|
||||
|
||||
// Some broken cards require this:
|
||||
err = sd_set_blocklength(0x200);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = sd_set_bus_width(4); // XXX: Should check in SCR first.
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = sd_set_clock(); // XXX: Should check.
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
sd_close();
|
||||
|
||||
return err;
|
||||
}
|
58
string.c
Normal file
58
string.c
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
size_t strlen(const char *s)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
for (len = 0; s[len]; len++)
|
||||
;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t strnlen(const char *s, size_t count)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
for (len = 0; s[len] && len < count; len++)
|
||||
;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void *memset(void *b, int c, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
((unsigned char *)b)[i] = c;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int memcmp(const void *b1, const void *b2, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
int diff = ((unsigned char *)b1)[i] - ((unsigned char *)b2)[i];
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
44
sync.c
Normal file
44
sync.c
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
void sync_before_read(void *p, u32 len)
|
||||
{
|
||||
u32 a, b;
|
||||
|
||||
a = (u32)p & ~0x1f;
|
||||
b = ((u32)p + len + 0x1f) & ~0x1f;
|
||||
|
||||
for ( ; a < b; a += 32)
|
||||
asm("dcbi 0,%0" : : "b"(a) : "memory");
|
||||
|
||||
asm("sync ; isync");
|
||||
}
|
||||
|
||||
void sync_after_write(const void *p, u32 len)
|
||||
{
|
||||
u32 a, b;
|
||||
|
||||
a = (u32)p & ~0x1f;
|
||||
b = ((u32)p + len + 0x1f) & ~0x1f;
|
||||
|
||||
for ( ; a < b; a += 32)
|
||||
asm("dcbst 0,%0" : : "b"(a));
|
||||
|
||||
asm("sync ; isync");
|
||||
}
|
||||
|
||||
void sync_before_exec(const void *p, u32 len)
|
||||
{
|
||||
u32 a, b;
|
||||
|
||||
a = (u32)p & ~0x1f;
|
||||
b = ((u32)p + len + 0x1f) & ~0x1f;
|
||||
|
||||
for ( ; a < b; a += 32)
|
||||
asm("dcbst 0,%0 ; sync ; icbi 0,%0" : : "b"(a));
|
||||
|
||||
asm("sync ; isync");
|
||||
}
|
31
time.c
Normal file
31
time.c
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
// Timebase frequency is bus frequency / 4. Ignore roundoff, this
|
||||
// doesn't have to be very accurate.
|
||||
#define TICKS_PER_USEC (243/4)
|
||||
|
||||
static u32 mftb(void)
|
||||
{
|
||||
u32 x;
|
||||
|
||||
asm volatile("mftb %0" : "=r"(x));
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static void __delay(u32 ticks)
|
||||
{
|
||||
u32 start = mftb();
|
||||
|
||||
while (mftb() - start < ticks)
|
||||
;
|
||||
}
|
||||
|
||||
void udelay(u32 us)
|
||||
{
|
||||
__delay(TICKS_PER_USEC * us);
|
||||
}
|
131
usbgecko.c
Normal file
131
usbgecko.c
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// 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
|
||||
|
||||
// Based on code:
|
||||
// Copyright (c) 2008 - Nuke - <wiinuke@gmail.com>
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
|
||||
static void exi_write(u32 addr, u32 x)
|
||||
{
|
||||
write32(0x0d006800 + addr, x);
|
||||
}
|
||||
|
||||
static u32 exi_read(u32 addr)
|
||||
{
|
||||
return read32(0x0d006800 + addr);
|
||||
}
|
||||
|
||||
#define EXI_CH1_STATUS 0x14
|
||||
#define EXI_CH1_CONTROL 0x20
|
||||
#define EXI_CH1_DATA 0x24
|
||||
|
||||
|
||||
static void usbgecko_deselect_device(void)
|
||||
{
|
||||
exi_write(EXI_CH1_STATUS, 0);
|
||||
}
|
||||
|
||||
static void usbgecko_select_device(void)
|
||||
{
|
||||
// device 0, 16MHz
|
||||
exi_write(EXI_CH1_STATUS, 0xc0);
|
||||
}
|
||||
|
||||
static void usbgecko_wait_for_transfer_complete(void)
|
||||
{
|
||||
while (exi_read(EXI_CH1_CONTROL) & 1)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
u8 usbgecko_flash_read8(u32 offset)
|
||||
{
|
||||
u8 x;
|
||||
|
||||
usbgecko_deselect_device();
|
||||
|
||||
usbgecko_select_device();
|
||||
exi_write(EXI_CH1_DATA, 0xf0000000 | (offset << 9));
|
||||
exi_write(EXI_CH1_CONTROL, 0x35); // 4 bytes immediate write
|
||||
usbgecko_wait_for_transfer_complete();
|
||||
|
||||
usbgecko_select_device();
|
||||
exi_write(EXI_CH1_CONTROL, 0x39); // 4 bytes immediate read/write
|
||||
usbgecko_wait_for_transfer_complete();
|
||||
|
||||
x = exi_read(EXI_CH1_DATA) >> 23;
|
||||
|
||||
usbgecko_deselect_device();
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
u32 usbgecko_flash_read32(u32 offset)
|
||||
{
|
||||
u32 x, i;
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < 4; i++)
|
||||
x = (x << 8) | usbgecko_flash_read8(offset++);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int usbgecko_console_enabled = 0;
|
||||
|
||||
static u32 usbgecko_command(u32 command)
|
||||
{
|
||||
u32 x;
|
||||
|
||||
usbgecko_select_device();
|
||||
exi_write(EXI_CH1_DATA, command);
|
||||
exi_write(EXI_CH1_CONTROL, 0x19); // 2 bytes immediate read/write
|
||||
usbgecko_wait_for_transfer_complete();
|
||||
|
||||
x = exi_read(EXI_CH1_DATA);
|
||||
|
||||
usbgecko_deselect_device();
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
int usbgecko_checkgecko(void)
|
||||
{
|
||||
return usbgecko_command(0x90000000) == 0x04700000;
|
||||
}
|
||||
|
||||
void usbgecko_console_putc(u8 c)
|
||||
{
|
||||
u32 x;
|
||||
|
||||
if (!usbgecko_console_enabled)
|
||||
return;
|
||||
|
||||
if (c == '\n')
|
||||
usbgecko_console_putc('\r');
|
||||
|
||||
x = usbgecko_command(0xb0000000 | (c << 20));
|
||||
}
|
||||
|
||||
static void usbgecko_flush(void)
|
||||
{
|
||||
u32 x;
|
||||
|
||||
do {
|
||||
x = usbgecko_command(0xa0000000);
|
||||
} while (x & 0x08000000);
|
||||
}
|
||||
|
||||
void usbgecko_init(void)
|
||||
{
|
||||
if (!usbgecko_checkgecko())
|
||||
return;
|
||||
|
||||
usbgecko_console_enabled = 1;
|
||||
usbgecko_flush();
|
||||
}
|
168
video.c
Normal file
168
video.c
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
|
||||
// Copyright 2003-2004 Felix Domke <tmbinc@elitedvb.net>
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2;
|
||||
// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "loader.h"
|
||||
|
||||
extern u8 console_font_10x16x4[];
|
||||
|
||||
#define FONT_XSIZE 10
|
||||
#define FONT_YSIZE 16
|
||||
#define FONT_XGAP 0
|
||||
#define FONT_YGAP 2
|
||||
|
||||
static struct {
|
||||
u32 xres, yres, stride;
|
||||
|
||||
u32 cursor_x, cursor_y;
|
||||
|
||||
u32 border_left, border_right, border_top, border_bottom;
|
||||
} fb;
|
||||
|
||||
static void fb_write(u32 offset, u32 x)
|
||||
{
|
||||
// write32(0x00f00000 + offset, x);
|
||||
u32 *p = (u32 *)(0x80f00000 + offset);
|
||||
*p = x;
|
||||
sync_after_write(p, 4);
|
||||
}
|
||||
|
||||
static u32 fb_read(u32 offset)
|
||||
{
|
||||
// return read32(0x00f00000 + offset);
|
||||
u32 *p = (u32 *)(0x80f00000 + offset);
|
||||
|
||||
return *p;
|
||||
}
|
||||
|
||||
static void fb_clear_lines(u32 top, u32 lines)
|
||||
{
|
||||
u32 x, y;
|
||||
u32 offset;
|
||||
|
||||
offset = fb.stride * top;
|
||||
|
||||
for (y = 0; y < lines; y++) {
|
||||
for (x = 0; x < fb.xres/2; x++)
|
||||
fb_write(offset + 4*x, 0x00800080);
|
||||
|
||||
offset += fb.stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void fb_scroll_line(void)
|
||||
{
|
||||
u32 x, y;
|
||||
u32 offset, delta;
|
||||
u32 lines = FONT_YSIZE + FONT_YGAP;
|
||||
|
||||
offset = fb.stride * fb.border_top;
|
||||
delta = fb.stride * lines;
|
||||
|
||||
for (y = fb.border_top; y < fb.yres - lines; y++) {
|
||||
for (x = 0; x < fb.xres/2; x++)
|
||||
fb_write(offset + 4*x, fb_read(offset + 4*x + delta));
|
||||
|
||||
offset += fb.stride;
|
||||
}
|
||||
|
||||
fb_clear_lines(fb.yres - lines, lines);
|
||||
|
||||
fb.cursor_y -= lines;
|
||||
}
|
||||
|
||||
static void fb_drawc(u32 x, u32 y, u8 c)
|
||||
{
|
||||
if (c < 0x20 || c > 0x7f)
|
||||
c = 0x7f;
|
||||
c -= 0x20;
|
||||
|
||||
u32 offset = fb.stride*y + 2*x;
|
||||
u8 *font = &console_font_10x16x4[c * FONT_XSIZE * FONT_YSIZE / 2];
|
||||
|
||||
u32 ax, ay;
|
||||
for (ay = 0; ay < FONT_YSIZE; ay++) {
|
||||
for (ax = 0; ax < FONT_XSIZE / 2; ax++) {
|
||||
u8 bits = *font++;
|
||||
u32 nybh = bits & 0xf0;
|
||||
u32 nybl = bits & 0x0f;
|
||||
u32 q = 0x00800080;
|
||||
q |= (nybh << 24) | (nybh << 20);
|
||||
q |= (nybl << 12) | (nybl << 8);
|
||||
fb_write(offset + 4*ax, q);
|
||||
}
|
||||
offset += fb.stride;
|
||||
}
|
||||
}
|
||||
|
||||
void fb_putc(char c)
|
||||
{
|
||||
switch (c) {
|
||||
case '\n':
|
||||
fb.cursor_y += FONT_YSIZE + FONT_YGAP;
|
||||
|
||||
case '\r':
|
||||
fb.cursor_x = fb.border_left;
|
||||
break;
|
||||
|
||||
default:
|
||||
fb_drawc(fb.cursor_x, fb.cursor_y, c);
|
||||
fb.cursor_x += FONT_XSIZE + FONT_XGAP;
|
||||
if ((fb.cursor_x + FONT_XSIZE) > fb.border_right) {
|
||||
fb.cursor_y += FONT_YSIZE + FONT_YGAP;
|
||||
fb.cursor_x = fb.border_left;
|
||||
}
|
||||
}
|
||||
|
||||
if (fb.cursor_y + FONT_YSIZE >= fb.border_bottom)
|
||||
fb_scroll_line();
|
||||
}
|
||||
|
||||
|
||||
static void fb_init(u32 xres, u32 yres, u32 stride)
|
||||
{
|
||||
fb.xres = xres;
|
||||
fb.yres = yres;
|
||||
fb.stride = stride;
|
||||
|
||||
fb.border_left = 30;
|
||||
fb.border_top = 30;
|
||||
fb.border_right = fb.xres - 30;
|
||||
fb.border_bottom = fb.yres - 30;
|
||||
|
||||
fb.cursor_x = fb.border_left;
|
||||
fb.cursor_y = fb.border_top;
|
||||
|
||||
fb_clear_lines(0, fb.yres);
|
||||
}
|
||||
|
||||
void video_init(void)
|
||||
{
|
||||
// read VTR register to determine linecount and mode
|
||||
u32 vtr = read16(0x0c002000);
|
||||
u32 lines = vtr >> 4;
|
||||
|
||||
if ((vtr & 0x0f) > 10) { // progressive
|
||||
// set framebuffer position
|
||||
write32(0x0c00201c, 0x00f00000);
|
||||
write32(0x0c002024, 0x00f00000);
|
||||
} else { //interlaced
|
||||
lines *= 2;
|
||||
|
||||
u32 vto = read32(0x0c00200c);
|
||||
u32 vte = read32(0x0c002010);
|
||||
|
||||
// set framebuffer position
|
||||
// try to figure out the interlacing order
|
||||
if ((vto & 0x03ff) < (vte & 0x03ff)) {
|
||||
write32(0x0c00201c, 0x00f00000);
|
||||
write32(0x0c002024, 0x00f00000 + 2*640);
|
||||
} else {
|
||||
write32(0x0c00201c, 0x00f00000 + 2*640);
|
||||
write32(0x0c002024, 0x00f00000);
|
||||
}
|
||||
}
|
||||
|
||||
fb_init(640, lines, 2*640);
|
||||
}
|
Loading…
Reference in New Issue
Block a user