Initial commit
authorSegher Boessenkool <segher@kernel.crashing.org>
Tue, 26 May 2009 08:12:55 +0000 (10:12 +0200)
committerSegher Boessenkool <segher@kernel.crashing.org>
Tue, 26 May 2009 08:12:55 +0000 (10:12 +0200)
Enjoy!

51 files changed:
.gitattributes [new file with mode: 0644]
.gitignore [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
describe.sh [new file with mode: 0755]
generic-banner.png [new file with mode: 0644]
generic-banner.ppm [new file with mode: 0644]
head.b [new file with mode: 0644]
loader/.gitignore [new file with mode: 0644]
loader/Makefile [new file with mode: 0644]
loader/console.c [new file with mode: 0644]
loader/crt0.s [new file with mode: 0644]
loader/elf.c [new file with mode: 0644]
loader/exception.c [new file with mode: 0644]
loader/exception_2200.s [new file with mode: 0644]
loader/fat.c [new file with mode: 0644]
loader/font.png [new file with mode: 0644]
loader/font.ppm [new file with mode: 0644]
loader/font2c.pl [new file with mode: 0755]
loader/ios.c [new file with mode: 0644]
loader/loader.h [new file with mode: 0644]
loader/loader.lds [new file with mode: 0644]
loader/main.c [new file with mode: 0644]
loader/sd.c [new file with mode: 0644]
loader/string.c [new file with mode: 0644]
loader/sync.c [new file with mode: 0644]
loader/time.c [new file with mode: 0644]
loader/usbgecko.c [new file with mode: 0644]
loader/video.c [new file with mode: 0644]
make-title-bin.pl [new file with mode: 0755]
pack.sh [new file with mode: 0755]
rzde-icon.png [new file with mode: 0644]
rzde-icon.ppm [new file with mode: 0644]
rzdj-icon.png [new file with mode: 0644]
rzdj-icon.ppm [new file with mode: 0644]
rzdp-icon.png [new file with mode: 0644]
rzdp-icon.ppm [new file with mode: 0644]
start.S [new file with mode: 0644]
titleid.pl [new file with mode: 0755]
toc-rzde-3.2 [new file with mode: 0644]
toc-rzde-3.3 [new file with mode: 0644]
toc-rzde-3.4 [new file with mode: 0644]
toc-rzdj-3.2 [new file with mode: 0644]
toc-rzdj-3.3 [new file with mode: 0644]
toc-rzdj-3.4 [new file with mode: 0644]
toc-rzdp-3.2 [new file with mode: 0644]
toc-rzdp-3.3 [new file with mode: 0644]
toc-rzdp-3.4 [new file with mode: 0644]
twilight.c [new file with mode: 0644]
twilight.lds [new file with mode: 0644]

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..a705c04
--- /dev/null
@@ -0,0 +1,3 @@
+*.b    -crlf -diff -merge
+*.png  -crlf -diff -merge
+*.ppm  -crlf -diff -merge
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..f63dbb7
--- /dev/null
@@ -0,0 +1,14 @@
+*.o
+
+.version
+
+rzd?-?.?.bin
+rzd?.data
+
+rzd??.elf
+rzd??.slot
+
+title.bin
+
+zero16k
+FAILURE
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..623b625
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   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.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..278c630
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,151 @@
+# 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
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..7090af1
--- /dev/null
+++ b/README
@@ -0,0 +1,10 @@
+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].
diff --git a/describe.sh b/describe.sh
new file mode 100755 (executable)
index 0000000..5b7c068
--- /dev/null
@@ -0,0 +1,12 @@
+#!/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
diff --git a/generic-banner.png b/generic-banner.png
new file mode 100644 (file)
index 0000000..536d357
Binary files /dev/null and b/generic-banner.png differ
diff --git a/generic-banner.ppm b/generic-banner.ppm
new file mode 100644 (file)
index 0000000..7ed37aa
Binary files /dev/null and b/generic-banner.ppm differ
diff --git a/head.b b/head.b
new file mode 100644 (file)
index 0000000..7241dfb
Binary files /dev/null and b/head.b differ
diff --git a/loader/.gitignore b/loader/.gitignore
new file mode 100644 (file)
index 0000000..deffe43
--- /dev/null
@@ -0,0 +1,5 @@
+font.c
+version.c
+
+loader.elf
+loader.bin
diff --git a/loader/Makefile b/loader/Makefile
new file mode 100644 (file)
index 0000000..03944cd
--- /dev/null
@@ -0,0 +1,73 @@
+# 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
diff --git a/loader/console.c b/loader/console.c
new file mode 100644 (file)
index 0000000..09fe9af
--- /dev/null
@@ -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;
+}
diff --git a/loader/crt0.s b/loader/crt0.s
new file mode 100644 (file)
index 0000000..8c27e57
--- /dev/null
@@ -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 .
diff --git a/loader/elf.c b/loader/elf.c
new file mode 100644 (file)
index 0000000..437fd5c
--- /dev/null
@@ -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];
+}
diff --git a/loader/exception.c b/loader/exception.c
new file mode 100644 (file)
index 0000000..30330bd
--- /dev/null
@@ -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);
+}
diff --git a/loader/exception_2200.s b/loader/exception_2200.s
new file mode 100644 (file)
index 0000000..3d946db
--- /dev/null
@@ -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:
diff --git a/loader/fat.c b/loader/fat.c
new file mode 100644 (file)
index 0000000..8c13882
--- /dev/null
@@ -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;
+}
diff --git a/loader/font.png b/loader/font.png
new file mode 100644 (file)
index 0000000..a854991
Binary files /dev/null and b/loader/font.png differ
diff --git a/loader/font.ppm b/loader/font.ppm
new file mode 100644 (file)
index 0000000..2c35217
Binary files /dev/null and b/loader/font.ppm differ
diff --git a/loader/font2c.pl b/loader/font2c.pl
new file mode 100755 (executable)
index 0000000..8630744
--- /dev/null
@@ -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";
diff --git a/loader/ios.c b/loader/ios.c
new file mode 100644 (file)
index 0000000..33560aa
--- /dev/null
@@ -0,0 +1,271 @@
+// 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();
+}
diff --git a/loader/loader.h b/loader/loader.h
new file mode 100644 (file)
index 0000000..4c2a5c4
--- /dev/null
@@ -0,0 +1,171 @@
+// 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
diff --git a/loader/loader.lds b/loader/loader.lds
new file mode 100644 (file)
index 0000000..fdbde4a
--- /dev/null
@@ -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 {
+       . = 0x90000020;
+
+       .start : { crt0.o(*) }
+       .text : { *(.text) }
+       .rodata : { *(.rodata .rodata.*)}
+       .data : { *(.data) }
+
+       __bss_start = .;
+       .bss : { *(.bss) }
+       __bss_end = .;
+
+       . = ALIGN(0x40);
+       .stack : {
+               . += 0x8000;
+               _stack_top = .;
+       }
+}
diff --git a/loader/main.c b/loader/main.c
new file mode 100644 (file)
index 0000000..62f6aa6
--- /dev/null
@@ -0,0 +1,155 @@
+// 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;
+}
diff --git a/loader/sd.c b/loader/sd.c
new file mode 100644 (file)
index 0000000..8367791
--- /dev/null
@@ -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;
+}
diff --git a/loader/string.c b/loader/string.c
new file mode 100644 (file)
index 0000000..6553202
--- /dev/null
@@ -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;
+}
diff --git a/loader/sync.c b/loader/sync.c
new file mode 100644 (file)
index 0000000..b454a90
--- /dev/null
@@ -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");
+}
diff --git a/loader/time.c b/loader/time.c
new file mode 100644 (file)
index 0000000..8fac32d
--- /dev/null
@@ -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);
+}
diff --git a/loader/usbgecko.c b/loader/usbgecko.c
new file mode 100644 (file)
index 0000000..81c5128
--- /dev/null
@@ -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();
+}
diff --git a/loader/video.c b/loader/video.c
new file mode 100644 (file)
index 0000000..0358892
--- /dev/null
@@ -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);
+}
diff --git a/make-title-bin.pl b/make-title-bin.pl
new file mode 100755 (executable)
index 0000000..4b6ec87
--- /dev/null
@@ -0,0 +1,16 @@
+#!/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;
diff --git a/pack.sh b/pack.sh
new file mode 100755 (executable)
index 0000000..b1aa666
--- /dev/null
+++ b/pack.sh
@@ -0,0 +1,8 @@
+#!/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
diff --git a/rzde-icon.png b/rzde-icon.png
new file mode 100644 (file)
index 0000000..7e753c9
Binary files /dev/null and b/rzde-icon.png differ
diff --git a/rzde-icon.ppm b/rzde-icon.ppm
new file mode 100644 (file)
index 0000000..e695ee7
Binary files /dev/null and b/rzde-icon.ppm differ
diff --git a/rzdj-icon.png b/rzdj-icon.png
new file mode 100644 (file)
index 0000000..4f865f8
Binary files /dev/null and b/rzdj-icon.png differ
diff --git a/rzdj-icon.ppm b/rzdj-icon.ppm
new file mode 100644 (file)
index 0000000..94ce235
Binary files /dev/null and b/rzdj-icon.ppm differ
diff --git a/rzdp-icon.png b/rzdp-icon.png
new file mode 100644 (file)
index 0000000..d6f9263
Binary files /dev/null and b/rzdp-icon.png differ
diff --git a/rzdp-icon.ppm b/rzdp-icon.ppm
new file mode 100644 (file)
index 0000000..4088e74
Binary files /dev/null and b/rzdp-icon.ppm differ
diff --git a/start.S b/start.S
new file mode 100644 (file)
index 0000000..5d453aa
--- /dev/null
+++ b/start.S
@@ -0,0 +1,35 @@
+// 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
diff --git a/titleid.pl b/titleid.pl
new file mode 100755 (executable)
index 0000000..f735f1d
--- /dev/null
@@ -0,0 +1,2 @@
+#!/usr/bin/perl
+print "00010000", map { sprintf "%02x", ord uc } split //, $ARGV[0];
diff --git a/toc-rzde-3.2 b/toc-rzde-3.2
new file mode 100644 (file)
index 0000000..07bf1a4
--- /dev/null
@@ -0,0 +1,4 @@
+title.bin
+generic-banner.ppm
+rzde-icon.ppm
+rzde.data zeldaTp.dat
diff --git a/toc-rzde-3.3 b/toc-rzde-3.3
new file mode 100644 (file)
index 0000000..03d7fa5
--- /dev/null
@@ -0,0 +1,5 @@
+title.bin
+generic-banner.ppm
+rzde-icon.ppm
+zero16k zeldaTp.dat
+rzde.data zeldaTp.dat
diff --git a/toc-rzde-3.4 b/toc-rzde-3.4
new file mode 100644 (file)
index 0000000..30c4035
--- /dev/null
@@ -0,0 +1,5 @@
+title.bin
+generic-banner.ppm
+rzde-icon.ppm
+rzde.data zeldaTp.dat
+FAILURE FAILURE
diff --git a/toc-rzdj-3.2 b/toc-rzdj-3.2
new file mode 100644 (file)
index 0000000..fdab09e
--- /dev/null
@@ -0,0 +1,4 @@
+title.bin
+generic-banner.ppm
+rzdj-icon.ppm
+rzdj.data zeldaTp.dat
diff --git a/toc-rzdj-3.3 b/toc-rzdj-3.3
new file mode 100644 (file)
index 0000000..133d563
--- /dev/null
@@ -0,0 +1,5 @@
+title.bin
+generic-banner.ppm
+rzdj-icon.ppm
+zero16k zeldaTp.dat
+rzdj.data zeldaTp.dat
diff --git a/toc-rzdj-3.4 b/toc-rzdj-3.4
new file mode 100644 (file)
index 0000000..d32c4f8
--- /dev/null
@@ -0,0 +1,5 @@
+title.bin
+generic-banner.ppm
+rzdj-icon.ppm
+rzdj.data zeldaTp.dat
+FAILURE FAILURE
diff --git a/toc-rzdp-3.2 b/toc-rzdp-3.2
new file mode 100644 (file)
index 0000000..e58ce07
--- /dev/null
@@ -0,0 +1,4 @@
+title.bin
+generic-banner.ppm
+rzdp-icon.ppm
+rzdp.data zeldaTp.dat
diff --git a/toc-rzdp-3.3 b/toc-rzdp-3.3
new file mode 100644 (file)
index 0000000..bbdb572
--- /dev/null
@@ -0,0 +1,5 @@
+title.bin
+generic-banner.ppm
+rzdp-icon.ppm
+zero16k zeldaTp.dat
+rzdp.data zeldaTp.dat
diff --git a/toc-rzdp-3.4 b/toc-rzdp-3.4
new file mode 100644 (file)
index 0000000..e642bf4
--- /dev/null
@@ -0,0 +1,5 @@
+title.bin
+generic-banner.ppm
+rzdp-icon.ppm
+rzdp.data zeldaTp.dat
+FAILURE FAILURE
diff --git a/twilight.c b/twilight.c
new file mode 100644 (file)
index 0000000..9ce9a2b
--- /dev/null
@@ -0,0 +1,222 @@
+// 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 (;;)
+               ;
+}
diff --git a/twilight.lds b/twilight.lds
new file mode 100644 (file)
index 0000000..a871cdc
--- /dev/null
@@ -0,0 +1,40 @@
+/* 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;
+       }
+}