commit 15edfaae0fe106910d059e91f8cb4f691cc50da4 Author: Segher Boessenkool Date: Tue May 26 10:12:55 2009 +0200 Initial commit Enjoy! diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a705c04 --- /dev/null +++ b/.gitattributes @@ -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 index 0000000..f63dbb7 --- /dev/null +++ b/.gitignore @@ -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 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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) + + 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. + + , 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 index 0000000..278c630 --- /dev/null +++ b/Makefile @@ -0,0 +1,151 @@ +# Copyright 2008-2009 Segher Boessenkool +# 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 index 0000000..7090af1 --- /dev/null +++ b/README @@ -0,0 +1,10 @@ +Copyright 2008-2009 Segher Boessenkool +Copyright 2008 Haxx Enterprises +Copyright 2008 Hector Martin "marcan" +Copyright 2003-2004 Felix Domke + +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 index 0000000..5b7c068 --- /dev/null +++ b/describe.sh @@ -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 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 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 index 0000000..7241dfb Binary files /dev/null and b/head.b differ diff --git a/loader/.gitignore b/loader/.gitignore new file mode 100644 index 0000000..deffe43 --- /dev/null +++ b/loader/.gitignore @@ -0,0 +1,5 @@ +font.c +version.c + +loader.elf +loader.bin diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 0000000..03944cd --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,73 @@ +# Copyright 2008-2009 Segher Boessenkool +# 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 index 0000000..09fe9af --- /dev/null +++ b/loader/console.c @@ -0,0 +1,153 @@ +// Copyright 2009 Segher Boessenkool +// 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 + +#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 index 0000000..8c27e57 --- /dev/null +++ b/loader/crt0.s @@ -0,0 +1,25 @@ +# Copyright 2008-2009 Segher Boessenkool +# 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 index 0000000..437fd5c --- /dev/null +++ b/loader/elf.c @@ -0,0 +1,47 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..30330bd --- /dev/null +++ b/loader/exception.c @@ -0,0 +1,54 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..3d946db --- /dev/null +++ b/loader/exception_2200.s @@ -0,0 +1,24 @@ +# Copyright 2008-2009 Segher Boessenkool +# 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 index 0000000..8c13882 --- /dev/null +++ b/loader/fat.c @@ -0,0 +1,425 @@ +// Copyright 2009 Segher Boessenkool +// 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 +#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 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 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 index 0000000..8630744 --- /dev/null +++ b/loader/font2c.pl @@ -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 index 0000000..33560aa --- /dev/null +++ b/loader/ios.c @@ -0,0 +1,271 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..4c2a5c4 --- /dev/null +++ b/loader/loader.h @@ -0,0 +1,171 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 + + +// 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 index 0000000..fdbde4a --- /dev/null +++ b/loader/loader.lds @@ -0,0 +1,27 @@ +/* Copyright 2008-2009 Segher Boessenkool + 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 index 0000000..62f6aa6 --- /dev/null +++ b/loader/main.c @@ -0,0 +1,155 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..8367791 --- /dev/null +++ b/loader/sd.c @@ -0,0 +1,283 @@ +// Copyright 2008 Haxx Enterprises +// Copyright 2008-2009 Segher Boessenkool +// This code is licensed to you under the terms of the GNU GPL, version 2; +// see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + + +#include "loader.h" + + +static int fd; +static u32 rca; // 16 bottom bits are stuff bits + + +static int sd_hc_write8(u8 reg, u8 data) +{ + u32 param[6]; + int err; + + memset(param, 0, sizeof param); + param[0] = reg; + param[3] = 1; // reg size + param[4] = data; + + err = ios_ioctl(fd, 1, param, sizeof param, 0, 0); + + return err; +} + +static int sd_hc_read8(u8 reg, u8 *x) +{ + u32 param[6]; + u32 data; + int err; + + memset(param, 0, sizeof param); + param[0] = reg; + param[3] = 1; // reg size + param[4] = 0; + + err = ios_ioctl(fd, 2, param, sizeof param, &data, sizeof data); + if (err) + return err; + + *x = data; + + return err; +} + +static int sd_reset_card(void) +{ + u32 reply; + int err; + + err = ios_ioctl(fd, 4, 0, 0, &reply, sizeof reply); + if (err) + return err; + + rca = reply & 0xffff0000; + +// printf("sd_reset_card(): got reply = %08x\n", reply); + + return 0; +} + +static int sd_set_clock(void) +{ + u32 clock; + int err; + + clock = 1; // half of the sdclk divisor: a power of two or zero, + // should look at capabilities reg to compute this + + err = ios_ioctl(fd, 6, &clock, sizeof clock, 0, 0); + + return err; +} + +static int sd_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg, + u32 block_count, u32 block_size, void *addr, + u32 *outreply, u32 reply_size) +{ + u32 param[9]; + u32 reply[4]; + int err; + + param[0] = cmd; + param[1] = cmd_type; + param[2] = resp_type; + param[3] = arg; + param[4] = block_count; + param[5] = block_size; + param[6] = (u32)addr; + param[7] = 0; // ??? + param[8] = 0; // ??? + + err = ios_ioctl(fd, 7, param, sizeof param, reply, sizeof reply); + + if (reply_size) // ??? + memcpy(outreply, reply, reply_size); + + return err; +} + + +#define TYPE_BC 1 +#define TYPE_BCR 2 +#define TYPE_AC 3 +#define TYPE_ADTC 4 + +#define RESPONSE_NONE 0 +#define RESPONSE_R1 1 +#define RESPONSE_R1B 2 +#define RESPONSE_R2 3 +#define RESPONSE_R3 4 +#define RESPONSE_R4 5 +#define RESPONSE_R5 6 +#define RESPONSE_R6 7 + + +static int sd_app_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg, + u32 block_count, u32 block_size, void *addr, + u32 *outreply, u32 reply_size) +{ + int err; + + err = sd_command(55, TYPE_AC, RESPONSE_R1, rca, 0, 0, 0, 0, 0); + if (err) + return err; + + err = sd_command(cmd, cmd_type, resp_type, arg, + block_count, block_size, addr, + outreply, reply_size); + + return err; +} + +static int sd_data_command(u32 cmd, u32 cmd_type, u32 resp_type, u32 arg, + u32 block_count, u32 block_size, void *data, + u32 unk1, u32 unk2, u32 *outreply, u32 reply_size) +{ + u32 param[9]; + u32 reply[4]; + struct ioctlv vec[3]; + int err; + + param[0] = cmd; + param[1] = cmd_type; + param[2] = resp_type; + param[3] = arg; + param[4] = block_count; + param[5] = block_size; + param[6] = (u32)data; + param[7] = unk1; // ??? + param[8] = unk2; // ??? + + vec[0].data = param; + vec[0].len = sizeof param; + vec[1].data = data; + vec[1].len = block_count * block_size; + vec[2].data = reply; + vec[2].len = sizeof reply; + + err = ios_ioctlv(fd, 7, 2, 1, vec); + + if (reply_size) // ??? + memcpy(outreply, reply, reply_size); + + return err; +} + +static int sd_select(void) +{ + int err; + + //printf("Selecting card:\n"); + err = sd_command(7, TYPE_AC, RESPONSE_R1B, rca, 0, 0, 0, 0, 0); + + return err; +} + +static int sd_set_blocklength(u32 len) +{ + int err; + + //printf("sd_set_blocklength(%u)\n", len); + err = sd_command(16, TYPE_AC, RESPONSE_R1, len, 0, 0, 0, 0, 0); + + return err; +} + +static int sd_set_bus_width(int width) +{ + u32 arg; + u8 reg; + int err; + + // First notify the card. + arg = (width == 4) ? 2 : 0; + //printf("sd_set_bus_width()\n"); + err = sd_app_command(6, TYPE_AC, RESPONSE_R1, arg, 0, 0, 0, 0, 0); + if (err) + return err; + + // Now change the Host Control Register. + err = sd_hc_read8(0x28, ®); + if (err) + return err; + + reg = (reg & ~2) | arg; + + err = sd_hc_write8(0x28, reg); + + return err; +} + +int sd_read_sector(u8 *data, u32 offset) +{ + u32 reply[4]; + int err; + + if (offset >= 0x800000) + return -1; + + err = sd_data_command(18, TYPE_AC, RESPONSE_R1, 0x200 * offset, + 1, 0x200, data, 1, 0, reply, sizeof reply); + + sync_before_read(data, 0x200); + + //printf("READ block %d\r",offset); + if (err) + printf("SD READ %d: err=%08x, reply=%08x %08x %08x %08x\n", + offset, err, reply[0], reply[1], reply[2], reply[3]); + + return err; +} + +int sd_close(void) +{ + return ios_close(fd); +} + +int sd_init(void) +{ + int err; + + fd = ios_open("/dev/sdio/slot0", 0); + if (fd < 0) + return fd; + + err = sd_reset_card(); + if (err) { + printf("SD Card not present? (%d)\n", err); + goto out; + } + + // now in standby state + + err = sd_select(); + if (err) + goto out; + + // now in transfer state + + // Some broken cards require this: + err = sd_set_blocklength(0x200); + if (err) + goto out; + + err = sd_set_bus_width(4); // XXX: Should check in SCR first. + if (err) + goto out; + + err = sd_set_clock(); // XXX: Should check. + if (err) + goto out; + + return 0; + + out: + sd_close(); + + return err; +} diff --git a/loader/string.c b/loader/string.c new file mode 100644 index 0000000..6553202 --- /dev/null +++ b/loader/string.c @@ -0,0 +1,58 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..b454a90 --- /dev/null +++ b/loader/sync.c @@ -0,0 +1,44 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..8fac32d --- /dev/null +++ b/loader/time.c @@ -0,0 +1,31 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..81c5128 --- /dev/null +++ b/loader/usbgecko.c @@ -0,0 +1,131 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 - + +#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 index 0000000..0358892 --- /dev/null +++ b/loader/video.c @@ -0,0 +1,168 @@ +// Copyright 2008-2009 Segher Boessenkool +// Copyright 2003-2004 Felix Domke +// 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 index 0000000..4b6ec87 --- /dev/null +++ b/make-title-bin.pl @@ -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 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 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 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 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 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 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 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 index 0000000..5d453aa --- /dev/null +++ b/start.S @@ -0,0 +1,35 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..f735f1d --- /dev/null +++ b/titleid.pl @@ -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 index 0000000..07bf1a4 --- /dev/null +++ b/toc-rzde-3.2 @@ -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 index 0000000..03d7fa5 --- /dev/null +++ b/toc-rzde-3.3 @@ -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 index 0000000..30c4035 --- /dev/null +++ b/toc-rzde-3.4 @@ -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 index 0000000..fdab09e --- /dev/null +++ b/toc-rzdj-3.2 @@ -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 index 0000000..133d563 --- /dev/null +++ b/toc-rzdj-3.3 @@ -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 index 0000000..d32c4f8 --- /dev/null +++ b/toc-rzdj-3.4 @@ -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 index 0000000..e58ce07 --- /dev/null +++ b/toc-rzdp-3.2 @@ -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 index 0000000..bbdb572 --- /dev/null +++ b/toc-rzdp-3.3 @@ -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 index 0000000..e642bf4 --- /dev/null +++ b/toc-rzdp-3.4 @@ -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 index 0000000..9ce9a2b --- /dev/null +++ b/twilight.c @@ -0,0 +1,222 @@ +// Copyright 2008-2009 Segher Boessenkool +// 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 index 0000000..a871cdc --- /dev/null +++ b/twilight.lds @@ -0,0 +1,40 @@ +/* Copyright 2008-2009 Segher Boessenkool + 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; + } +}