From 15edfaae0fe106910d059e91f8cb4f691cc50da4 Mon Sep 17 00:00:00 2001 From: Segher Boessenkool Date: Tue, 26 May 2009 10:12:55 +0200 Subject: [PATCH] Initial commit Enjoy! --- .gitattributes | 3 + .gitignore | 14 ++ COPYING | 340 ++++++++++++++++++++++++++++++++ Makefile | 151 ++++++++++++++ README | 10 + describe.sh | 12 ++ generic-banner.png | Bin 0 -> 11684 bytes generic-banner.ppm | Bin 0 -> 36878 bytes head.b | Bin 0 -> 444 bytes loader/.gitignore | 5 + loader/Makefile | 73 +++++++ loader/console.c | 153 +++++++++++++++ loader/crt0.s | 25 +++ loader/elf.c | 47 +++++ loader/exception.c | 54 +++++ loader/exception_2200.s | 24 +++ loader/fat.c | 425 ++++++++++++++++++++++++++++++++++++++++ loader/font.png | Bin 0 -> 5659 bytes loader/font.ppm | Bin 0 -> 69134 bytes loader/font2c.pl | 60 ++++++ loader/ios.c | 271 +++++++++++++++++++++++++ loader/loader.h | 171 ++++++++++++++++ loader/loader.lds | 27 +++ loader/main.c | 155 +++++++++++++++ loader/sd.c | 283 ++++++++++++++++++++++++++ loader/string.c | 58 ++++++ loader/sync.c | 44 +++++ loader/time.c | 31 +++ loader/usbgecko.c | 131 +++++++++++++ loader/video.c | 168 ++++++++++++++++ make-title-bin.pl | 16 ++ pack.sh | 8 + rzde-icon.png | Bin 0 -> 3760 bytes rzde-icon.ppm | Bin 0 -> 6925 bytes rzdj-icon.png | Bin 0 -> 3670 bytes rzdj-icon.ppm | Bin 0 -> 6925 bytes rzdp-icon.png | Bin 0 -> 3693 bytes rzdp-icon.ppm | Bin 0 -> 6925 bytes start.S | 35 ++++ titleid.pl | 2 + toc-rzde-3.2 | 4 + toc-rzde-3.3 | 5 + toc-rzde-3.4 | 5 + toc-rzdj-3.2 | 4 + toc-rzdj-3.3 | 5 + toc-rzdj-3.4 | 5 + toc-rzdp-3.2 | 4 + toc-rzdp-3.3 | 5 + toc-rzdp-3.4 | 5 + twilight.c | 222 +++++++++++++++++++++ twilight.lds | 40 ++++ 51 files changed, 3100 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README create mode 100755 describe.sh create mode 100644 generic-banner.png create mode 100644 generic-banner.ppm create mode 100644 head.b create mode 100644 loader/.gitignore create mode 100644 loader/Makefile create mode 100644 loader/console.c create mode 100644 loader/crt0.s create mode 100644 loader/elf.c create mode 100644 loader/exception.c create mode 100644 loader/exception_2200.s create mode 100644 loader/fat.c create mode 100644 loader/font.png create mode 100644 loader/font.ppm create mode 100755 loader/font2c.pl create mode 100644 loader/ios.c create mode 100644 loader/loader.h create mode 100644 loader/loader.lds create mode 100644 loader/main.c create mode 100644 loader/sd.c create mode 100644 loader/string.c create mode 100644 loader/sync.c create mode 100644 loader/time.c create mode 100644 loader/usbgecko.c create mode 100644 loader/video.c create mode 100755 make-title-bin.pl create mode 100755 pack.sh create mode 100644 rzde-icon.png create mode 100644 rzde-icon.ppm create mode 100644 rzdj-icon.png create mode 100644 rzdj-icon.ppm create mode 100644 rzdp-icon.png create mode 100644 rzdp-icon.ppm create mode 100644 start.S create mode 100755 titleid.pl create mode 100644 toc-rzde-3.2 create mode 100644 toc-rzde-3.3 create mode 100644 toc-rzde-3.4 create mode 100644 toc-rzdj-3.2 create mode 100644 toc-rzdj-3.3 create mode 100644 toc-rzdj-3.4 create mode 100644 toc-rzdp-3.2 create mode 100644 toc-rzdp-3.3 create mode 100644 toc-rzdp-3.4 create mode 100644 twilight.c create mode 100644 twilight.lds 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 0000000000000000000000000000000000000000..536d357057abd68202c50b39ec26545fb70c1332 GIT binary patch literal 11684 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 literal 0 HcmV?d00001 diff --git a/generic-banner.ppm b/generic-banner.ppm new file mode 100644 index 0000000000000000000000000000000000000000..7ed37aab0877ed63ca63ceb6cfb88db8b1a092bd GIT binary patch literal 36878 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 iG6pA8PL5wrPL7L#0f?Z0D19)+13 +# 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 0000000000000000000000000000000000000000..a85499122e413f29ccb8d4e1ed3dd1649b55c4ce GIT binary patch literal 5659 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 B1~&iz literal 0 HcmV?d00001 diff --git a/loader/font.ppm b/loader/font.ppm new file mode 100644 index 0000000000000000000000000000000000000000..2c3521722afce7d4c4f59dcf405bce451dd4dee7 GIT binary patch literal 69134 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 z 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7e753c95b1b985831df357cc45bada018b063b27 GIT binary patch literal 3760 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 ob%m0#Wy_YYe)9jpF07*qoM6N<$g5RbBh5!Hn literal 0 HcmV?d00001 diff --git a/rzdj-icon.ppm b/rzdj-icon.ppm new file mode 100644 index 0000000000000000000000000000000000000000..94ce235a9a7c3bac39594b4841a3afda466e0148 GIT binary patch literal 6925 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 P&>4?(VSI@9|JnBM!k7lc literal 0 HcmV?d00001 diff --git a/rzdp-icon.png b/rzdp-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d6f9263e076e187bd7786b8b24fd8c5b766edbb7 GIT binary patch literal 3693 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 LNkvXXu0mjf>F_Di literal 0 HcmV?d00001 diff --git a/rzdp-icon.ppm b/rzdp-icon.ppm new file mode 100644 index 0000000000000000000000000000000000000000..4088e7486d38d8b947005da826d28427ebd80a6c GIT binary patch literal 6925 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 VFE6k0pfNR?df<0}`STTc;NLo(zc2s* literal 0 HcmV?d00001 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; + } +}