--- /dev/null
+*.b -crlf -diff -merge
+*.png -crlf -diff -merge
+*.ppm -crlf -diff -merge
--- /dev/null
+*.o
+
+.version
+
+rzd?-?.?.bin
+rzd?.data
+
+rzd??.elf
+rzd??.slot
+
+title.bin
+
+zero16k
+FAILURE
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+# 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
+
+
+# Configuration:
+
+# What toolchain prefix should we use
+CROSS ?= broadway-
+
+# Where are the tools (http://git.infradead.org/users/segher/wii.git)
+TOOLS ?= $(HOME)/wii/segher
+
+# End of configuration.
+
+
+
+# Set CC, LD, OBJCOPY based on CROSS, unless they are set already
+
+ifeq ($(origin CC), default)
+ CC := $(CROSS)gcc -m32
+endif
+ifeq ($(origin LD), default)
+ LD := $(CROSS)ld
+endif
+OBJCOPY ?= $(CROSS)objcopy
+
+
+# The compiler flags we need.
+
+CFLAGS := -Wall -W -Os -ffreestanding -mno-eabi -mno-sdata -mcpu=750
+
+
+# Build with "V=1" to see the commands executed; be quiet otherwise.
+
+ifeq ($(V),1)
+ Q :=
+else
+ Q := @
+ MAKEFLAGS += --no-print-directory
+endif
+
+
+targets := rzde-3.2.bin rzde-3.3.bin rzde-3.4.bin
+targets += rzdj-3.2.bin rzdj-3.3.bin rzdj-3.4.bin
+targets += rzdp-3.2.bin rzdp-3.3.bin rzdp-3.4.bin
+targets-short := rzde rzdj rzdp
+
+objs := twilight.o
+
+ppms := $(targets-short:%=%-icon.ppm) generic-banner.ppm
+assets := title.bin $(ppms)
+
+loader := loader/loader.bin
+
+
+titleid = $(shell perl titleid.pl $(1))
+
+
+# System menu 3.3 checks for the exploit, when a) you copy a save from SD,
+# and b) when the menu starts up; but for a) it only looks at the first
+# zeldaTp.dat file, and for b) it allows any file of non-aligned length.
+#
+# System menu 3.4 only looks at the last file in the wad when installing.
+#
+# System menu 4.0 finally avoids such silly bugs.
+
+define twintig
+ D=$(call titleid,$(1)); \
+ $(TOOLS)/twintig $$D $@ toc-$1
+endef
+
+
+all: $(targets)
+
+$(filter %-3.2.bin,$(targets)): %-3.2.bin: %.data
+$(filter %-3.3.bin,$(targets)): %-3.3.bin: %.data zero16k
+$(filter %-3.4.bin,$(targets)): %-3.4.bin: %.data FAILURE
+$(targets): %.bin: toc-% $(assets)
+ @echo " TWINTIG $@"
+ $(Q)$(call twintig,$*)
+
+saves := $(targets-short:%=%.data)
+
+rzde.data: rzde0.slot rzde2.slot
+rzdp.data: rzdp0.slot
+rzdj.data: rzdj0.slot
+$(saves): $(loader)
+ @echo " ZELDAPACK $@"
+ $(Q)./pack.sh $@ $(filter %.slot,$^)
+ $(Q)$(TOOLS)/zelda-cksum $@
+ $(Q)cat $(loader) >> $@
+ $(Q)printf '\0' >> $@
+
+slots := rzde0.slot rzde2.slot rzdj0.slot rzdp0.slot
+
+$(slots): %.slot: %.elf
+ @echo " OBJCOPY $@"
+ $(Q)$(OBJCOPY) -Obinary $< $@
+
+elfs := $(slots:.slot=.elf)
+
+rzde0.elf: baddr := 0x8046a3e0+0
+rzde2.elf: baddr := 0x804519e0+0x0a94
+rzdj0.elf: baddr := 0x8044f860+0
+rzdp0.elf: baddr := 0x804522e0+0
+$(elfs): %.elf: twilight.lds %.o $(objs)
+ @echo " LINK $@"
+ $(Q)$(LD) --defsym baddr=$(baddr) -T $^ -o $@
+
+exploit-objs := $(elfs:.elf=.o)
+
+$(exploit-objs): slot-name := Twilight Hack
+rzde0.o: slot-name := TwilightHack0
+rzde2.o: slot-name := TwilightHack2
+$(exploit-objs): %.o: start.S head.b
+ @echo " ASSEMBLE $@"
+ $(Q)$(CC) $(CFLAGS) -D NAME="$(slot-name)" -c $< -o $@
+
+%.o: %.c
+ @echo " COMPILE $@"
+ $(Q)$(CC) $(CFLAGS) -c $< -o $@
+
+title.bin: .version
+ @echo " TITLEBIN $@"
+ $(Q)perl make-title-bin.pl > $@
+
+.version: FORCE
+ $(Q)./describe.sh > .$@-tmp
+ $(Q)cmp -s $@ .$@-tmp || cp .$@-tmp $@
+ $(Q)rm .$@-tmp
+
+$(ppms): %.ppm: %.png
+ @echo " PPM $@"
+ $(Q)convert $< $@
+
+zero16k:
+ $(Q)dd if=/dev/zero bs=16384 count=1 2>/dev/null > $@
+
+FAILURE:
+ $(Q)echo FAILURE > $@
+
+$(loader): FORCE .version
+ $(Q)$(MAKE) -C loader
+
+FORCE:
+
+clean:
+ -rm -f $(targets) $(saves) $(elfs) $(exploit-objs) $(objs) $(slots)
+ -rm -f .version title.bin zero16k FAILURE
+ $(MAKE) -C loader clean
--- /dev/null
+Copyright 2008-2009 Segher Boessenkool <segher@kernel.crashing.org>
+Copyright 2008 Haxx Enterprises <bushing@gmail.com>
+Copyright 2008 Hector Martin "marcan" <marcan@marcansoft.com>
+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
+
+Console font created by Freddy Leitner [www.dreamer.de], based on
+the Droid Sans Mono font [www.droidfonts.com].
--- /dev/null
+#!/bin/bash
+
+# Check for git and a git repo.
+if head=`git rev-parse --verify HEAD 2>/dev/null`; then
+ echo -n `git describe`
+
+ # Are there uncommitted changes?
+ git update-index --refresh --unmerged > /dev/null
+ git diff-index --quiet HEAD || echo -n -dirty
+fi
+
+echo
--- /dev/null
+font.c
+version.c
+
+loader.elf
+loader.bin
--- /dev/null
+# 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 ?= broadway-
+
+
+ifeq ($(origin CC), default)
+ CC := $(CROSS)gcc -m32
+endif
+ifeq ($(origin LD), default)
+ LD := $(CROSS)ld
+endif
+OBJCOPY ?= $(CROSS)objcopy
+
+
+CFLAGS := -Wall -W -Os -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 $@
+
+version.c: ../.version
+ @echo " VERSION $@"
+ $(Q)echo "const char version[] = \"`cat $^` (`whoami`@`hostname -s`)\";" > $@
+
+../.version: FORCE
+ $(Q)$(MAKE) -C .. .version
+
+$(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 version.c
--- /dev/null
+// 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;
+}
--- /dev/null
+# 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 .
--- /dev/null
+// 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];
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+# 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:
--- /dev/null
+// 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;
+}
--- /dev/null
+#!/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";
--- /dev/null
+// 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)
+{
+ 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();
+ ipc_recv_reply();
+
+ 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;
+}
+
+
+// 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 releasse_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);
+ 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");
+
+ releasse_old_stm_callback();
+}
--- /dev/null
+// 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);
+}
+
+
+// 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);
+
+void reset_ios(void);
+
+#endif
--- /dev/null
+/* 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 {
+ . = 0x90000020;
+
+ .start : { crt0.o(*) }
+ .text : { *(.text) }
+ .rodata : { *(.rodata .rodata.*)}
+ .data : { *(.data) }
+
+ __bss_start = .;
+ .bss : { *(.bss) }
+ __bss_end = .;
+
+ . = ALIGN(0x40);
+ .stack : {
+ . += 0x8000;
+ _stack_top = .;
+ }
+}
--- /dev/null
+// 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"
+
+
+u8 *code_buffer = (u8 *)0x90100000;
+u8 *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[] = {
+ 0x3c209000, // lis 1,0x9000
+ 0x60210020, // ori 1,1,0x0020
+ 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 boot.elf:\n");
+ err = fat_open("boot.elf");
+
+ if (err) {
+ printf("boot.elf 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;
+ }
+
+ printf("Done.\n");
+ return 0;
+}
+
+int try_usbgecko_load(void)
+{
+ if (!usbgecko_checkgecko()) {
+ printf("USBGecko not found\n");
+ return -1;
+ }
+
+#define FLASH_OFFSET 0x30000
+ int i, size = usbgecko_flash_read32(FLASH_OFFSET);
+ if (size < 0) {
+ printf("Invalid code size in usbgecko flash (%d)\n", size);
+ return -1;
+ }
+ printf("Loading %d bytes from USBGecko flash (offset=%x)\n",
+ size, FLASH_OFFSET+4);
+
+ for (i=0; i < size; i++)
+ code_buffer[i] = usbgecko_flash_read8(FLASH_OFFSET + 4 + i);
+
+ return 0;
+}
+
+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("Twilight Hack %s\n", version);
+ printf("\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");
+
+
+ int err;
+
+ restart:
+ err = try_sd_load();
+
+ if (err) {
+ err = try_usbgecko_load();
+
+ if (err) {
+ printf("No code found to load, hanging.\n");
+ for (;;)
+ ;
+ }
+ }
+
+ if (valid_elf_image(code_buffer)) {
+ printf("Valid ELF image detected.\n");
+ void (*entry)() = load_elf_image(code_buffer);
+ entry();
+ printf("Program returned to loader, reloading.\n");
+ } else
+ printf("No valid ELF image detected, retrying.\n");
+
+ goto restart;
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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");
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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();
+}
--- /dev/null
+// 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);
+}
--- /dev/null
+#!/usr/bin/perl
+sub printline {
+ my $x = shift;
+ chomp $x;
+ $x .= "\0" x 32;
+ $x = substr $x, 0, 32;
+ $x =~ s/(.)/\0$1/g;
+
+ print $x;
+}
+
+$name = "Twilight Hack by Team Twiizers";
+$version = `cat .version`;
+
+printline $name;
+printline $version;
--- /dev/null
+#!/bin/bash
+out=$1; shift
+dd if=/dev/zero bs=1 count=$((0x4000)) of=$out 2>/dev/null
+start=0
+for save in $@; do
+ dd if=$save of=$out bs=1 seek=$start conv=notrunc 2>/dev/null
+ start=$((start+0xa94))
+done
--- /dev/null
+// 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
+
+#define XSTR(x) #x
+#define ISTR(x) XSTR(x)
+
+ .section .start,"ax"
+
+ // Uninteresting stuff.
+ .incbin "head.b"
+
+ // "Link". This is displayed on the load menu, so make it nice.
+0: .ascii ISTR(NAME)
+ .fill 17 - (. - 0b)
+
+ // "Epona". Hungry horse eats the stack.
+ .fill 0xe8,1,'3'
+
+ // The return address on the stack.
+ .long start
+
+ // Align things properly -- there's code after this.
+ .fill 7,1,'S'
+ .balign 4,0
+
+start:
+ // Set up a stack frame.
+ lis 1,0x8080 ; li 0,0 ; stwu 0,-64(1)
+
+ // Pass the address we are called from, to determine region.
+ mflr 3
+
+ // Go for it!
+ b main
--- /dev/null
+#!/usr/bin/perl
+print "00010000", map { sprintf "%02x", ord uc } split //, $ARGV[0];
--- /dev/null
+title.bin
+generic-banner.ppm
+rzde-icon.ppm
+rzde.data zeldaTp.dat
--- /dev/null
+title.bin
+generic-banner.ppm
+rzde-icon.ppm
+zero16k zeldaTp.dat
+rzde.data zeldaTp.dat
--- /dev/null
+title.bin
+generic-banner.ppm
+rzde-icon.ppm
+rzde.data zeldaTp.dat
+FAILURE FAILURE
--- /dev/null
+title.bin
+generic-banner.ppm
+rzdj-icon.ppm
+rzdj.data zeldaTp.dat
--- /dev/null
+title.bin
+generic-banner.ppm
+rzdj-icon.ppm
+zero16k zeldaTp.dat
+rzdj.data zeldaTp.dat
--- /dev/null
+title.bin
+generic-banner.ppm
+rzdj-icon.ppm
+rzdj.data zeldaTp.dat
+FAILURE FAILURE
--- /dev/null
+title.bin
+generic-banner.ppm
+rzdp-icon.ppm
+rzdp.data zeldaTp.dat
--- /dev/null
+title.bin
+generic-banner.ppm
+rzdp-icon.ppm
+zero16k zeldaTp.dat
+rzdp.data zeldaTp.dat
--- /dev/null
+title.bin
+generic-banner.ppm
+rzdp-icon.ppm
+rzdp.data zeldaTp.dat
+FAILURE FAILURE
--- /dev/null
+// 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
+
+#undef DEBUG_GECKO
+#undef DEBUG_BLINK
+
+typedef unsigned int u32;
+typedef unsigned char u8;
+
+int nand_open_E0(const char *path, void *buf, u32 mode);
+int nand_open_E2(const char *path, void *buf, u32 mode);
+int nand_open_J0(const char *path, void *buf, u32 mode);
+int nand_open_P0(const char *path, void *buf, u32 mode);
+
+int nand_read_E0(void *buf, void *dest, u32 len);
+int nand_read_E2(void *buf, void *dest, u32 len);
+int nand_read_J0(void *buf, void *dest, u32 len);
+int nand_read_P0(void *buf, void *dest, u32 len);
+
+void audio_stop_E0(void);
+void audio_stop_E2(void);
+void audio_stop_J0(void);
+void audio_stop_P0(void);
+
+void graphics_stop_E0(void);
+void graphics_stop_E2(void);
+void graphics_stop_J0(void);
+void graphics_stop_P0(void);
+
+static u8 nand_buf[0x100] __attribute__ ((aligned(0x40)));
+
+#ifdef DEBUG_GECKO
+void gecko_print(void *, const char *);
+
+#define PRINT(x) gecko_print(0, x)
+#define HEX(x) hex(x)
+
+static void hex(u32 x)
+{
+ u32 i;
+ u32 digit;
+ char s[10];
+
+ for (i = 0; i < 8; i++) {
+ digit = x >> 28;
+ x <<= 4;
+ s[i] = digit + '0' + (digit < 10 ? 0 : 'a' - 10 - '0');
+ }
+ s[8] = '\n';
+ s[9] = 0;
+ PRINT(s);
+}
+#else
+#define PRINT(x) do { } while (0)
+#define HEX(x) do { } while (0)
+#endif
+
+static void sync_cache(void *p, u32 n)
+{
+ u32 start, end;
+
+ start = (u32)p & ~31;
+ end = ((u32)p + n + 31) & ~31;
+ n = (end - start) >> 5;
+
+ while (n--) {
+ asm("dcbst 0,%0 ; icbi 0,%0" : : "b"(p));
+ p += 32;
+ }
+ asm("sync ; isync");
+}
+
+static void sync_before_read(void *p, u32 n)
+{
+ u32 start, end;
+
+ start = (u32)p & ~31;
+ end = ((u32)p + n + 31) & ~31;
+ n = (end - start) >> 5;
+
+ while (n--) {
+ asm("dcbf 0,%0" : : "b"(p));
+ p += 32;
+ }
+ asm("sync");
+}
+
+static void jump(void *p, u32 arg)
+{
+ PRINT("taking the plunge...\n");
+
+ asm("mr 3,%1 ; mtctr %0 ; bctrl" : : "r"(p), "r"(arg) : "r3");
+
+ PRINT("whoops, payload returned to us\n");
+}
+
+#ifdef DEBUG_BLINK
+static u32 read32(u32 addr)
+{
+ u32 x;
+
+ asm volatile("lwz %0,0(%1) ; sync" : "=r"(x) : "b"(0xc0000000 | addr));
+
+ return x;
+}
+
+static void write32(u32 addr, u32 x)
+{
+ asm("stw %0,0(%1) ; eieio" : : "r"(x), "b"(0xc0000000 | addr));
+}
+
+static void blink(u32 colour)
+{
+ u32 *fb = (u32 *)0xC0F00000;
+ u32 i;
+
+ // blink tray led
+ write32(0x0d8000c0, read32(0x0d8000c0) ^ 0x20);
+
+ for (i = 0; i < 640*576/2; i++)
+ fb[i] = colour;
+}
+#else
+#define blink(x) do { } while(0)
+#endif
+
+void __attribute__ ((noreturn)) main(u32 baddr)
+{
+ int ret, i, len;
+ char *area;
+ char *gameid = (char *)0x80000000;
+ int (*nand_open)(const char *path, void *buf, u32 mode);
+ int (*nand_read)(void *buf, void *dest, u32 len);
+ void (*audio_stop)(void);
+ void (*graphics_stop)(void);
+
+ PRINT("Hello, Brave New World!\n");
+
+ baddr -= 0x2c0;
+
+ switch (gameid[3]) {
+ case 'E':
+ if ((baddr>>16) == 0x8045) {
+ nand_open = nand_open_E2;
+ nand_read = nand_read_E2;
+ audio_stop = audio_stop_E2;
+ graphics_stop = graphics_stop_E2;
+ } else {
+ nand_open = nand_open_E0;
+ nand_read = nand_read_E0;
+ audio_stop = audio_stop_E0;
+ graphics_stop = graphics_stop_E0;
+ }
+ break;
+ case 'P':
+ nand_open = nand_open_P0;
+ nand_read = nand_read_P0;
+ audio_stop = audio_stop_P0;
+ graphics_stop = graphics_stop_P0;
+ break;
+ case 'J':
+ nand_open = nand_open_J0;
+ nand_read = nand_read_J0;
+ audio_stop = audio_stop_J0;
+ graphics_stop = graphics_stop_J0;
+ break;
+ default:
+ PRINT("unsupported game region\n");
+ for (;;)
+ ;
+ }
+
+ audio_stop();
+ graphics_stop();
+
+ blink(0x266a26c0); // maroon
+
+ ret = nand_open("zeldaTp.dat", nand_buf, 1);
+
+ blink(0x7140718a); // olive
+
+ PRINT("nand open --> ");
+ HEX(ret);
+
+ area = (void *)0x90000020;
+
+ // Skip past save game, to loader.bin
+ ret = nand_read(nand_buf, area, 0x4000);
+
+ len = 0;
+ for (i = 0; i < 0x40; i++) {
+ PRINT("reading bootloader page: ");
+ HEX(i);
+
+ blink(0x40804080 + i*0x02000200); // grey
+
+ sync_before_read(area + 0x1000*i, 0x1000);
+ ret = nand_read(nand_buf, area + 0x1000*i, 0x1000);
+ len += ret;
+
+ blink(0x552b5515 + i*0x02000200); // lime
+
+ PRINT("--> ");
+ HEX(ret);
+ PRINT("\n");
+ }
+
+ for (i = 0; i < 0x100; i++)
+ HEX(((u32 *)area)[i]);
+
+ blink(0xc399c36a); // sky blue
+
+ sync_cache(area, len);
+ jump(area, 0x123);
+
+ blink(0x4c544cff); // red
+
+ PRINT("(shouldn't happen)\n");
+ for (;;)
+ ;
+}
--- /dev/null
+/* 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)
+
+SECTIONS {
+ gecko_print = 0x802facf0;
+
+ nand_open_E0 = 0x80371f50;
+ nand_read_E0 = 0x80371710;
+ audio_stop_E0 = 0x8034607c;
+ graphics_stop_E0 = 0x8035c930;
+
+ nand_open_E2 = 0x8035c988;
+ nand_read_E2 = 0x8035c148;
+ audio_stop_E2 = 0x80330a4c;
+ graphics_stop_E2 = 0x80347368;
+
+ nand_open_P0 = 0x8035cdb8;
+ nand_read_P0 = 0x8035c578;
+ audio_stop_P0 = 0x80330e7c;
+ graphics_stop_P0 = 0x80347798;
+
+ nand_open_J0 = 0x8035e440;
+ nand_read_J0 = 0x8035dc00;
+ audio_stop_J0 = 0x8033256c;
+ graphics_stop_J0 = 0x80348e20;
+
+ .twilight baddr :
+ {
+ rzd*.o(.start)
+ *(.text)
+ *(.rodata .rodata.*)
+ *(.data)
+ *(.bss)
+ . = 0x0a94;
+ }
+}