pax_global_header00006660000000000000000000000064126234004650014514gustar00rootroot0000000000000052 comment=a9cb1c4c663fe36d64e78e9b19ced4c14d8435be sunxi-tools-1.3/000077500000000000000000000000001262340046500136635ustar00rootroot00000000000000sunxi-tools-1.3/.gitignore000066400000000000000000000001301262340046500156450ustar00rootroot00000000000000sunxi-fexc bin2fex fex2bin sunxi-bootinfo sunxi-fel sunxi-pio sunxi-nand-part *.o *.swp sunxi-tools-1.3/COPYING000066400000000000000000000432541262340046500147260ustar00rootroot00000000000000 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 Lesser 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 Lesser General Public License instead of this License. sunxi-tools-1.3/Makefile000066400000000000000000000137101262340046500153250ustar00rootroot00000000000000# Copyright (C) 2012 Alejandro Mery # Copyright (C) 2012,2013 Henrik Nordstrom # Copyright (C) 2013 Patrick Wood # Copyright (C) 2013 Pat Wood # # 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, see . CC = gcc CFLAGS = -g -O0 -Wall -Wextra CFLAGS += -std=c99 -D_POSIX_C_SOURCE=200112L CFLAGS += -Iinclude/ # Tools useful on host and target TOOLS = sunxi-fexc sunxi-bootinfo sunxi-fel sunxi-nand-part # Symlinks to sunxi-fexc FEXC_LINKS = bin2fex fex2bin # Tools which are only useful on the target TARGET_TOOLS = sunxi-pio MISC_TOOLS = phoenix_info CROSS_COMPILE ?= arm-none-eabi- DESTDIR ?= PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin .PHONY: all clean tools target-tools install install-tools install-target-tools all: tools target-tools tools: $(TOOLS) $(FEXC_LINKS) target-tools: $(TARGET_TOOLS) misc: $(MISC_TOOLS) install: install-tools install-target-tools install-tools: $(TOOLS) install -d $(DESTDIR)$(BINDIR) @set -ex ; for t in $^ ; do \ install -m0755 $$t $(DESTDIR)$(BINDIR)/$$t ; \ done @set -ex ; for l in $(FEXC_LINKS) ; do \ ln -nfs sunxi-fexc $(DESTDIR)$(BINDIR)/$$l ; \ done install-target-tools: $(TARGET_TOOLS) install -d $(DESTDIR)$(BINDIR) @set -ex ; for t in $^ ; do \ install -m0755 $$t $(DESTDIR)$(BINDIR)/$$t ; \ done clean: @rm -vf $(TOOLS) $(FEXC_LINKS) $(TARGET_TOOLS) $(MISC_TOOLS) @rm -vf *.o *.elf *.sunxi *.bin *.nm *.orig $(TOOLS) $(TARGET_TOOLS): Makefile common.h fex2bin bin2fex: sunxi-fexc ln -nsf $< $@ sunxi-fexc: fexc.h script.h script.c \ script_uboot.h script_uboot.c \ script_bin.h script_bin.c \ script_fex.h script_fex.c LIBUSB = libusb-1.0 LIBUSB_CFLAGS = `pkg-config --cflags $(LIBUSB)` LIBUSB_LIBS = `pkg-config --libs $(LIBUSB)` sunxi-fel: fel.c fel-to-spl-thunk.h $(CC) $(CFLAGS) $(LIBUSB_CFLAGS) $(LDFLAGS) -o $@ $(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) sunxi-nand-part: nand-part-main.c nand-part.c nand-part-a10.h nand-part-a20.h $(CC) $(CFLAGS) -c -o nand-part-main.o nand-part-main.c $(CC) $(CFLAGS) -c -o nand-part-a10.o nand-part.c -D A10 $(CC) $(CFLAGS) -c -o nand-part-a20.o nand-part.c -D A20 $(CC) $(LDFLAGS) -o $@ nand-part-main.o nand-part-a10.o nand-part-a20.o $(LIBS) sunxi-%: %.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(filter %.c,$^) $(LIBS) fel-pio.bin: fel-pio.elf fel-pio.nm $(CROSS_COMPILE)objcopy -O binary fel-pio.elf fel-pio.bin fel-pio.elf: fel-pio.c fel-pio.lds $(CROSS_COMPILE)gcc -g -Os -fpic -fno-common -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder fel-pio.c -nostdlib -o fel-pio.elf -T fel-pio.lds fel-pio.nm: fel-pio.elf $(CROSS_COMPILE)nm fel-pio.elf | grep -v " _" >fel-pio.nm jtag-loop.elf: jtag-loop.c jtag-loop.lds $(CROSS_COMPILE)gcc -g -Os -fpic -fno-common -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder jtag-loop.c -nostdlib -o jtag-loop.elf -T jtag-loop.lds -Wl,-N jtag-loop.bin: jtag-loop.elf $(CROSS_COMPILE)objcopy -O binary jtag-loop.elf jtag-loop.bin jtag-loop.sunxi: jtag-loop.bin mksunxiboot jtag-loop.bin jtag-loop.sunxi fel-sdboot.elf: fel-sdboot.c fel-sdboot.lds $(CROSS_COMPILE)gcc -g -Os -fpic -fno-common -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder fel-sdboot.c -nostdlib -o fel-sdboot.elf -T fel-sdboot.lds -Wl,-N fel-sdboot.bin: fel-sdboot.elf $(CROSS_COMPILE)objcopy -O binary fel-sdboot.elf fel-sdboot.bin fel-sdboot.sunxi: fel-sdboot.bin mksunxiboot fel-sdboot.bin fel-sdboot.sunxi boot_head_sun3i.elf: boot_head_sun3i.S boot_head_sun3i.lds $(CROSS_COMPILE)gcc -g -Os -fpic -fno-common -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder boot_head.S -nostdlib -o boot_head_sun3i.elf -T boot_head.lds -Wl,-N -DMACHID=0x1094 boot_head_sun3i.bin: boot_head_sun3i.elf $(CROSS_COMPILE)objcopy -O binary boot_head_sun3i.elf boot_head_sun3i.bin boot_head_sun4i.elf: boot_head.S boot_head.lds $(CROSS_COMPILE)gcc -g -Os -fpic -fno-common -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder boot_head.S -nostdlib -o boot_head_sun4i.elf -T boot_head.lds -Wl,-N -DMACHID=0x1008 boot_head_sun4i.bin: boot_head_sun4i.elf $(CROSS_COMPILE)objcopy -O binary boot_head_sun4i.elf boot_head_sun4i.bin boot_head_sun5i.elf: boot_head.S boot_head.lds $(CROSS_COMPILE)gcc -g -Os -fpic -fno-common -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder boot_head.S -nostdlib -o boot_head_sun5i.elf -T boot_head.lds -Wl,-N -DMACHID=0x102A boot_head_sun5i.bin: boot_head_sun5i.elf $(CROSS_COMPILE)objcopy -O binary boot_head_sun5i.elf boot_head_sun5i.bin sunxi-bootinfo: bootinfo.c sunxi-meminfo: meminfo.c $(CROSS_COMPILE)gcc -g -O0 -Wall -static -o $@ $^ .gitignore: Makefile @for x in $(TOOLS) $(FEXC_LINKS) $(TARGET_TOOLS) '*.o' '*.swp'; do \ echo "$$x"; \ done > $@ sunxi-tools-1.3/README000066400000000000000000000041731262340046500145500ustar00rootroot00000000000000sunxi-tools Copyright (C) 2012 Alejandro Mery Tools to help hacking Allwinner A10 (aka sun4i) based devices and possibly it's successors, that's why the 'x' in the package name. sunxi-fexc: `.fex` file (de)compiler Usage: ./sunxi-fexc [-vq] [-I ] [-O ] [ []] infmt: fex, bin (default:fex) outfmt: fex, bin (default:bin) bin2fex: compatibility shortcut to call `fexc` to decompile an script.bin blob back into `.fex` format used by allwinner's SDK to configure the boards. fex2bin: compatiblity shortcut to call `fexc` to compile a `.fex` file into the binary form used by the sun4i kernel. sunxi-fel: script interface for talking to the FEL USB handler built in to th CPU. You activate FEL mode by pushing the usboot/recovery button at poweron. See http://linux-sunxi.org/FEL/USBBoot for a detailed usage guide. fel-gpio: Simple wrapper around fel-pio and fel to allos GPIO manipulations via FEL fel-sdboot: ARM native sdcard bootloader forcing the device into FEL mode fel-pio: ARM native helper for fel-gpio sunxi-pio: Manipulate PIO register dumps sunxi-nand-part: Tool for manipulating Allwinner NAND partition tables jtag-loop.sunxi: ARM native boot helper to force the SD port into JTAG and then stop, to ease debugging of bootloaders. sunxi-bootinfo: Dump information from Allwinner boot files (boot0/boot1) --type=sd include SD boot info --type=nand include NAND boot info (not implemented) phoenix_info: gives information about a phoenix image created by the phoenixcard utility and optionally extracts the embedded boot code & firmware file from their hidden partitions. meminfo: Tool for reading DRAM settings from registers. Compiled as a static binary for use on android and other OSes. To build this, get a toolchain, and run: make CROSS_COMPILE=arm-linux-gnueabihf- meminfo script-extractor: A simple tool, which can be executed on a rooted Android device to dump the script.bin blob from RAM via reading /dev/mem. This software is licensed under the terms of GPLv2+ as defined by the Free Software Foundation, details can be read in the COPYING file. sunxi-tools-1.3/adb-devprobe.sh000077500000000000000000000033731262340046500165620ustar00rootroot00000000000000#!/bin/bash # Copyright (C) 2012 Henrik Nordstrom # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. adb shell insmod /vendor/modules/sunxi-dbgreg.ko >/dev/null dump_io() { module=$1 addr=$2 len=$3 for ((i = 0; i < len; i+=4)) { printf "%x %s " $((addr + i)) $module adb shell "echo `printf %x $((addr + i))` > /sys/devices/virtual/misc/sunxi-reg/rw/address; cat /sys/devices/virtual/misc/sunxi-reg/rw/value" echo } } dump_io SRAM 0xf1c00000 0x100 dump_io DRAM 0xf1c01000 0x400 dump_io CCM 0xf1c20000 0x400 dump_io PIO 0xf1c20800 0x400 dump_pmu() { for ((i = 0; i <0x100; i+=2)) { adb shell "echo `printf 0x%x $i` > /sys/bus/i2c/devices/0-0034/axp20_reg; cat /sys/bus/i2c/devices/0-0034/axp20_regs" } } dump_pmu sunxi-tools-1.3/bin/000077500000000000000000000000001262340046500144335ustar00rootroot00000000000000sunxi-tools-1.3/bin/fel-pio.bin000077500000000000000000000001341262340046500164610ustar00rootroot00000000000000ê ê ŸåŸå: ã²åƒäSáûÿÿÿ/áüÂ(2 Ÿå0ŸåŸå²åƒäSáûÿÿÿ/áü/Â( Âsunxi-tools-1.3/bin/fel-pio.nm000066400000000000000000000000561262340046500163230ustar00rootroot0000000000000000002000 T pio_to_sram 00002004 T sram_to_pio sunxi-tools-1.3/bin/fel-sdboot.sunxi000066400000000000000000000010001262340046500175500ustar00rootroot00000000000000êeGON.BT0,ÇB@-é0Ÿåà áÿ/ွè ÿÿsunxi-tools-1.3/bin/jtag-loop.sunxi000066400000000000000000000010001262340046500174060ustar00rootroot00000000000000 êeGON.BT0ÜdreŸåŸå€åþÿÿê´ÂDDDsunxi-tools-1.3/bin/ramboot.scr000066400000000000000000000003661262340046500166140ustar00rootroot00000000000000'Vp}”cQ›äe¶ø—Û®# U-boot RAM boot script ramdisk= if iminfo 0x4c000000; then ramdisk=0x4c000000 fi setenv bootargs console=ttyS0,115200 rdinit=/sbin/init panic=10 bootm 0x44000000 $ramdisk sunxi-tools-1.3/bin/ramboot.uboot-sh000066400000000000000000000002561262340046500175630ustar00rootroot00000000000000# U-boot RAM boot script ramdisk= if iminfo 0x4c000000; then ramdisk=0x4c000000 fi setenv bootargs console=ttyS0,115200 rdinit=/sbin/init panic=10 bootm 0x44000000 $ramdisk sunxi-tools-1.3/boot_head.S000066400000000000000000000026071262340046500157400ustar00rootroot00000000000000/* * Boot header to work around broken Allwinner A1x boot loaders * * Copyright (C) 2012 Henrik Nordstrom * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * * This file is a workaround to broken bootloaders on Allwinner A1x * platform who do not provide correct machid or atags address * * Usage: * load the header at 0x40007000 and change the entry point of your * boot process to 0x40007000 * * Detailed memory map: * 0x40000100 atags * 0x40007000 boot_head (entry point) * 0x40008000 kernel * 0x43000000 script.bin * If you have a ramdisk then load it at some higher address */ _start: ldr r0, =0 ldr r1, =MACHID ldr r2, =0x40000100 ldr lr, =0x40008000 bx lr sunxi-tools-1.3/boot_head.lds000066400000000000000000000014461262340046500163200ustar00rootroot00000000000000/* * Copyright (C) 2012 Henrik Nordstrom * * 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, see . */ SECTIONS { . = 0x40007000; .text : { *(.text) } } sunxi-tools-1.3/bootinfo.c000066400000000000000000000302071262340046500156500ustar00rootroot00000000000000/* * (C) Copyright 2012 Henrik Nordstrom * * display information about sunxi boot headers * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include #include #include "types.h" /* boot_file_head copied from mksunxiboot */ /* boot head definition from sun4i boot code */ typedef struct boot_file_head { u32 jump_instruction; // one intruction jumping to real code u8 magic[8]; // ="eGON.BT0" or "eGON.BT1", not C-style string. u32 check_sum; // generated by PC u32 length; // generated by PC u32 pub_head_size; // the size of boot_file_head_t u8 pub_head_vsn[4]; // the version of boot_file_head_t u8 file_head_vsn[4]; // the version of boot0_file_head_t or boot1_file_head_t u8 Boot_vsn[4]; // Boot version u8 eGON_vsn[4]; // eGON version u8 platform[8]; // platform information } boot_file_head_t; typedef struct brom_file_head { u32 jump_instruction; // one intruction jumping to real code u8 magic[8]; // ="eGON.BRM", not C-style string. u32 length; // generated by PC u8 Boot_vsn[4]; // Boot version u8 eGON_vsn[4]; // eGON version u8 platform[8]; // platform information } brom_file_head_t; typedef struct _boot_dram_para_t { __u32 dram_baseaddr; __u32 dram_clk; __u32 dram_type; __u32 dram_rank_num; __u32 dram_chip_density; __u32 dram_io_width; __u32 dram_bus_width; __u32 dram_cas; __u32 dram_zq; __u32 dram_odt_en; __u32 dram_size; __u32 dram_tpr0; __u32 dram_tpr1; __u32 dram_tpr2; __u32 dram_tpr3; __u32 dram_tpr4; __u32 dram_tpr5; __u32 dram_emr1; __u32 dram_emr2; __u32 dram_emr3; } boot_dram_para_t; typedef struct _normal_gpio_cfg { __u8 port; __u8 port_num; __u8 mul_sel; __u8 pull; __u8 drv_level; __u8 data; __u8 reserved[2]; } normal_gpio_cfg; typedef struct _boot0_private_head_t { __u32 prvt_head_size; char prvt_head_vsn[4]; boot_dram_para_t dram_para; __s32 uart_port; normal_gpio_cfg uart_ctrl[2]; __s32 enable_jtag; normal_gpio_cfg jtag_gpio[5]; normal_gpio_cfg storage_gpio[32]; __u8 storage_data[256]; } boot0_private_head_t; typedef struct _boot0_file_head_t { boot_file_head_t boot_head; boot0_private_head_t prvt_head; } boot0_file_head_t; typedef struct _boot_core_para_t { __u32 user_set_clock; __u32 user_set_core_vol; __u32 vol_threshold; } boot_core_para_t; typedef struct _boot1_private_head_t { __u32 prvt_head_size; __u8 prvt_head_vsn[4]; __s32 uart_port; normal_gpio_cfg uart_ctrl[2]; boot_dram_para_t dram_para; char script_buf[32768]; boot_core_para_t core_para; __s32 twi_port; normal_gpio_cfg twi_ctrl[2]; __s32 debug_enable; __s32 hold_key_min; __s32 hold_key_max; __u32 work_mode; __u32 storage_type; normal_gpio_cfg storage_gpio[32]; __u8 storage_data[256]; } boot1_private_head_t; typedef struct _boot1_file_head_t { boot_file_head_t boot_head; boot1_private_head_t prvt_head; } boot1_file_head_t; /* STORAGE DATA on SD loaders */ typedef struct _boot_sdcard_info_t { __s32 card_ctrl_num; __s32 boot_offset; __s32 card_no[4]; __s32 speed_mode[4]; __s32 line_sel[4]; __s32 line_count[4]; } boot_sdcard_info_t; #define BROM_MAGIC "eGON.BRM" #define BOOT0_MAGIC "eGON.BT0" #define BOOT1_MAGIC "eGON.BT1" union { boot_file_head_t boot; boot0_file_head_t boot0; boot1_file_head_t boot1; brom_file_head_t brom; } boot_hdr; typedef enum { ALLWINNER_UNKNOWN_LOADER=0, ALLWINNER_SD_LOADER, ALLWINNER_NAND_LOADER } loader_type; void fail(char *msg) { perror(msg); exit(1); } void pprintf(void *addr, const char *fmt, ...) { va_list ap; va_start(ap, fmt); printf("%8x:\t", (unsigned)((char *)addr - (char *)&boot_hdr)); vprintf(fmt, ap); va_end(ap); } void print_brom_file_head(brom_file_head_t *hdr) { pprintf(&hdr->magic, "Magic : %.8s\n", hdr->magic); pprintf(&hdr->length, "Length : %u\n", hdr->length); pprintf(&hdr->Boot_vsn, "BOOT ver : %.4s\n", hdr->Boot_vsn); pprintf(&hdr->eGON_vsn, "eGON ver : %.4s\n", hdr->eGON_vsn); pprintf(&hdr->platform, "Chip? : %.8s\n", hdr->platform); } void print_boot_file_head(boot_file_head_t *hdr) { pprintf(&hdr->magic, "Magic : %.8s\n", hdr->magic); pprintf(&hdr->length, "Length : %u\n", hdr->length); pprintf(&hdr->pub_head_size, "HSize : %u\n", hdr->pub_head_size); pprintf(&hdr->pub_head_vsn, "HEAD ver : %.4s\n", hdr->pub_head_vsn); pprintf(&hdr->file_head_vsn, "FILE ver : %.4s\n", hdr->file_head_vsn); pprintf(&hdr->Boot_vsn, "BOOT ver : %.4s\n", hdr->Boot_vsn); pprintf(&hdr->eGON_vsn, "eGON ver : %.4s\n", hdr->eGON_vsn); pprintf(&hdr->platform, "platform : %c%c%c%c%c%c%c%c\n", hdr->platform[0], hdr->platform[1], hdr->platform[2], hdr->platform[3], hdr->platform[4], hdr->platform[5], hdr->platform[6], hdr->platform[7]); } void print_boot_dram_para(boot_dram_para_t *dram) { pprintf(&dram->dram_baseaddr, "DRAM base : %p\n", (void *)(long)dram->dram_baseaddr); pprintf(&dram->dram_clk, "DRAM clk : %d\n", dram->dram_clk); pprintf(&dram->dram_type, "DRAM type : %d\n", dram->dram_type); pprintf(&dram->dram_rank_num, "DRAM rank : %d\n", dram->dram_rank_num); pprintf(&dram->dram_chip_density,"DRAM den : %d\n", dram->dram_chip_density); pprintf(&dram->dram_io_width, "DRAM iow : %d\n", dram->dram_io_width); pprintf(&dram->dram_bus_width, "DRAM busw : %d\n", dram->dram_bus_width); pprintf(&dram->dram_cas, "DRAM cas : %d\n", dram->dram_cas); pprintf(&dram->dram_zq, "DRAM zq : %d\n", dram->dram_zq); pprintf(&dram->dram_odt_en, "DRAM odt : 0x%x\n", dram->dram_odt_en); pprintf(&dram->dram_size, "DRAM size : %d\n", dram->dram_size); pprintf(&dram->dram_tpr0, "DRAM tpr0 : 0x%x\n", dram->dram_tpr0); pprintf(&dram->dram_tpr1, "DRAM tpr1 : 0x%x\n", dram->dram_tpr1); pprintf(&dram->dram_tpr2, "DRAM tpr2 : 0x%x\n", dram->dram_tpr2); pprintf(&dram->dram_tpr3, "DRAM tpr3 : 0x%x\n", dram->dram_tpr3); pprintf(&dram->dram_tpr4, "DRAM tpr4 : 0x%x\n", dram->dram_tpr4); pprintf(&dram->dram_tpr5, "DRAM tpr5 : 0x%x\n", dram->dram_tpr5); pprintf(&dram->dram_emr1, "DRAM emr1 : 0x%x\n", dram->dram_emr1); pprintf(&dram->dram_emr2, "DRAM emr2 : 0x%x\n", dram->dram_emr2); pprintf(&dram->dram_emr3, "DRAM emr3 : 0x%x\n", dram->dram_emr3); } void print_normal_gpio_cfg(normal_gpio_cfg *gpio, int count) { int i; for (i = 0; i < count; i++) { if (gpio[i].port) pprintf(&gpio[i], " GPIO %d : port=%c%d, sel=%d, pull=%d, drv=%d, data=%d, reserved=%02x,%02x\n", i, 'A'+gpio[i].port-1, gpio[i].port_num, gpio[i].mul_sel, gpio[i].pull, gpio[i].drv_level, gpio[i].data, gpio[i].reserved[0], gpio[i].reserved[1]); } } void print_boot_sdcard_info(boot_sdcard_info_t *info) { pprintf(&info->card_ctrl_num, " CARD Ctrl Num: %d\n", info->card_ctrl_num); pprintf(&info->boot_offset, " BOOT Offset: %08x\n", info->boot_offset); for (int i = 0; i < 4; i++) { if (info->card_no[i] == -1) continue; pprintf(&info->card_no[i], " CARD No : %d (%d)\n", info->card_no[i], i); pprintf(&info->speed_mode[i], " Speed : %d\n", info->speed_mode[i]); pprintf(&info->line_sel[i], " Line sel: %d\n", info->line_sel[i]); pprintf(&info->line_count[i], " Line cnt: %d\n", info->line_count[i]); } } void print_boot0_private_head(boot0_private_head_t *hdr, loader_type type) { pprintf(&hdr->prvt_head_size, "FHSize : %u\n", hdr->prvt_head_size); pprintf(&hdr->prvt_head_vsn, "FILE ver : %.4s\n", hdr->prvt_head_vsn); print_boot_dram_para(&hdr->dram_para); pprintf(&hdr->uart_port, "UART port : %d\n", hdr->uart_port); print_normal_gpio_cfg(hdr->uart_ctrl, 2); pprintf(&hdr->enable_jtag, "JTAG en : %d\n", hdr->enable_jtag); print_normal_gpio_cfg(hdr->jtag_gpio, 5); pprintf(&hdr->storage_gpio, "STORAGE :\n"); print_normal_gpio_cfg(hdr->storage_gpio, 32); int i = 0; if (type == ALLWINNER_SD_LOADER) { print_boot_sdcard_info((boot_sdcard_info_t *)hdr->storage_data); i = sizeof(boot_sdcard_info_t); } for (int n = 0; i < 256; i++, n++) { if (n % 16 == 0) { if (n) { printf("\n"); } pprintf(&hdr->storage_data[i], " DATA %02x :", i); } printf(" %02x", hdr->storage_data[i]); } printf("\n"); } void print_script(void *script) { } void print_core_para(boot_core_para_t *core) { pprintf(&core->user_set_clock, "Set Clock : %d\n", core->user_set_clock); pprintf(&core->user_set_core_vol, "Set Core Vol: %d\n", core->user_set_core_vol); pprintf(&core->vol_threshold, "Vol Threshold: %d\n", core->vol_threshold); } void print_boot1_private_head(boot1_private_head_t *hdr, loader_type type) { pprintf(&hdr->prvt_head_size, "FHSize : %u\n", hdr->prvt_head_size); pprintf(&hdr->prvt_head_vsn, "FILE ver : %.4s\n", hdr->prvt_head_vsn); pprintf(&hdr->uart_port, "UART port : %d\n", hdr->uart_port); print_normal_gpio_cfg(hdr->uart_ctrl, 2); print_boot_dram_para(&hdr->dram_para); print_script(&hdr->script_buf); print_core_para(&hdr->core_para); pprintf(&hdr->twi_port, "TWI port : %d\n", hdr->twi_port); print_normal_gpio_cfg(hdr->twi_ctrl, 2); pprintf(&hdr->debug_enable, "Debug : %d\n", hdr->debug_enable); pprintf(&hdr->hold_key_min, "Hold key min : %d\n", hdr->hold_key_min); pprintf(&hdr->hold_key_max, "Hold key max : %d\n", hdr->hold_key_max); pprintf(&hdr->work_mode, "Work mode : %d\n", hdr->work_mode); pprintf(&hdr->storage_type, "STORAGE :\n"); pprintf(&hdr->storage_type, " type : %d\n", hdr->storage_type); print_normal_gpio_cfg(hdr->storage_gpio, 32); int i = 0; if (type == ALLWINNER_SD_LOADER) { print_boot_sdcard_info((boot_sdcard_info_t *)hdr->storage_data); i = sizeof(boot_sdcard_info_t); } for (int n = 0; i < 256; i++, n++) { if (n % 16 == 0) { if (n) { printf("\n"); } pprintf(&hdr->storage_data[i], " DATA %02x :", i); } printf(" %02x", hdr->storage_data[i]); } printf("\n"); } void print_boot0_file_head(boot0_file_head_t *hdr, loader_type type) { print_boot_file_head(&hdr->boot_head); if (strncmp((char *)hdr->boot_head.file_head_vsn, "1230", 4) == 0) print_boot0_private_head(&hdr->prvt_head, type); else printf("Unknown boot0 header version\n"); } void print_boot1_file_head(boot1_file_head_t *hdr, loader_type type) { print_boot_file_head(&hdr->boot_head); if (strncmp((char *)hdr->boot_head.file_head_vsn, "1230", 4) == 0) print_boot1_private_head(&hdr->prvt_head, type); else printf("Unknown boot0 header version\n"); } int main(int argc, char * argv[]) { FILE *in = stdin; loader_type type = ALLWINNER_UNKNOWN_LOADER; if (argc > 1 && strcmp(argv[1], "--type=sd") == 0) { type = ALLWINNER_SD_LOADER; argc--; argv++; } if (argc > 1 && strcmp(argv[1], "--type=nand") == 0) { type = ALLWINNER_NAND_LOADER; argc--; argv++; } if (argc > 1) { in = fopen(argv[1], "rb"); if (!in) fail("open input: "); } int len; len = fread(&boot_hdr, 1, sizeof(boot_hdr), in); if (len < (int)sizeof(boot_file_head_t)) fail("Failed to read header:"); if (strncmp((char *)boot_hdr.boot.magic, BOOT0_MAGIC, strlen(BOOT0_MAGIC)) == 0) { print_boot0_file_head(&boot_hdr.boot0, type); } else if (strncmp((char *)boot_hdr.boot.magic, BOOT1_MAGIC, strlen(BOOT1_MAGIC)) == 0) { print_boot1_file_head(&boot_hdr.boot1, type); } else if (strncmp((char *)boot_hdr.boot.magic, BROM_MAGIC, strlen(BROM_MAGIC)) == 0) { print_brom_file_head(&boot_hdr.brom); } else { fail("Invalid magic\n"); } return 0; } sunxi-tools-1.3/common.h000066400000000000000000000052601262340046500153270ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #ifndef _SUNXI_TOOLS_COMMON_H #define _SUNXI_TOOLS_COMMON_H #include /* offsetof */ /** flat function argument as unused */ #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #else # define UNUSED(x) UNUSED_ ## x #endif /** finds the parent of an struct member */ #ifndef container_of #define container_of(P,T,M) (T *)((char *)(P) - offsetof(T, M)) #endif /** calculate number of elements of an array */ #ifndef ARRAY_SIZE #define ARRAY_SIZE(A) (sizeof(A)/sizeof((A)[0])) #endif /** shortcut to printf to stderr */ #define errf(...) fprintf(stderr, __VA_ARGS__) /* * list */ /** a list hook */ struct list_entry { struct list_entry *prev; struct list_entry *next; }; /** initialize an empty list hook */ static inline void list_init(struct list_entry *self) { self->prev = self->next = self; } /** puts an entry between two other on a list */ static inline void list_inject(struct list_entry *l, struct list_entry *prev, struct list_entry *next) { l->prev = prev; l->next = next; next->prev = l; prev->next = l; } #define list_insert(H, E) list_inject((E), (H), (H)->next) #define list_append(H, E) list_inject((E), (H)->prev, (H)) /** removes an entry for the list where it's contained */ static inline void list_remove(struct list_entry *l) { struct list_entry *prev = l->prev, *next = l->next; next->prev = prev; prev->next = next; } /** returns first element of a list */ static inline struct list_entry *list_first(struct list_entry *l) { return (l->next == l) ? NULL : l->next; } /** returns last element of a list */ static inline struct list_entry *list_last(struct list_entry *l) { return (l->prev == l) ? NULL : l->prev; } /** returns next element on a list */ static inline struct list_entry *list_next(struct list_entry *l, struct list_entry *e) { return (e->next == l) ? NULL : e->next; } /** is list empty? */ static inline int list_empty(struct list_entry *l) { return (l->prev == l); } #endif sunxi-tools-1.3/fel-copy.c000066400000000000000000000027601262340046500155520ustar00rootroot00000000000000/* * (C) Copyright 2011 Henrik Nordstrom * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ /* Build instructions: arm-none-linux-gnueabi-gcc -g -Os -fno-common -ffixed-r8 -msoft-float -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder fel-copy.c -c arm-none-linux-gnueabi-objcopy -O binary fel-copy.o fel-copy.bin Parameters: 0x2100 Destination address 0x2104 Source address 0x2108 Length Source address is updated, allowing repeated copy to same destination */ #define CONFIG_BASE 0x2100 void copy(void) { unsigned long *b = (void *)CONFIG_BASE; unsigned long **ptr = (void *)b++; unsigned long *a = *ptr; unsigned long i = *b++; while (i--) { *b++ = *a++; } *ptr = a; } sunxi-tools-1.3/fel-gpio000077500000000000000000000033011262340046500153100ustar00rootroot00000000000000#!/bin/sh -e # Copyright (C) 2012,2013 Henrik Nordstrom # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. pio_to_sram=0x2000 sram_to_pio=0x2004 if [ -f fel-pio.bin ]; then ./sunxi-fel write 0x2000 fel-pio.bin else ./sunxi-fel write 0x2000 bin/fel-pio.bin fi ./sunxi-fel exec $pio_to_sram ./sunxi-fel read 0x3000 0x228 pio.reg ./sunxi-pio -i pio.reg print > pio.old cat pio.old | fgrep -v '<0><0><0><0>' while read cmd; do ./sunxi-pio -i pio.reg -o pio.reg $cmd ./sunxi-fel write 0x3000 pio.reg ./sunxi-fel exe 0x2004 ./sunxi-fel exe 0x2000 ./sunxi-fel read 0x3000 0x228 pio.reg ./sunxi-pio -i pio.reg print > pio.new diff -U0 pio.old pio.new || true mv -f pio.new pio.old done sunxi-tools-1.3/fel-pio.c000066400000000000000000000031151262340046500153620ustar00rootroot00000000000000/* * (C) Copyright 2011 Henrik Nordstrom * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ /* Build instructions: arm-none-eabi-gcc -g -Os -fno-common -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder fel-pio.c -nostdlib -o fel-pio.elf arm-none-eabi-objcopy -O binary fel-pio.elf fel-pio.bin arm-none-eabi-nm fel-pio.o */ void _pio_to_sram(void); void _sram_to_pio(void); void pio_to_sram(void) { _pio_to_sram(); } void sram_to_pio(void) { _sram_to_pio(); } void _pio_to_sram(void) { unsigned long *a = (void *)0x1c20800; unsigned long *b = (void *)0x3000; int i = 0x228 >> 2; while (i--) { *b++ = *a++; } } void _sram_to_pio(void) { unsigned long *a = (void *)0x1c20800; unsigned long *b = (void *)0x3000; int i = 0x228 >> 2; while (i--) { *a++ = *b++; } } sunxi-tools-1.3/fel-pio.lds000066400000000000000000000014421262340046500157230ustar00rootroot00000000000000/* * Copyright (C) 2012 Henrik Nordstrom * * 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, see . */ SECTIONS { . = 0x2000; .text : { *(.text) } } sunxi-tools-1.3/fel-sdboot.c000066400000000000000000000024471262340046500160740ustar00rootroot00000000000000/* * (C) Copyright 2011 Henrik Nordstrom * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ /* Build instructions: arm-none-linux-gnueabi-gcc -g -Os -fno-common -ffixed-r8 -msoft-float -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder fel-boot.c -c arm-none-linux-gnueabi-objcopy -O binary fel-boot.o fel-boot.bin mksunxiboot fel-boot.bin fel-boot.sunxi Install instructions: dd if=fel-boot.sunxi of=/dev/sdX bs=1024 seek=8 */ void _start(void) { ((void (*)(void))0xffff0020)(); } sunxi-tools-1.3/fel-sdboot.lds000066400000000000000000000014421262340046500164260ustar00rootroot00000000000000/* * Copyright (C) 2012 Henrik Nordstrom * * 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, see . */ SECTIONS { . = 0x0030; .text : { *(.text) } } sunxi-tools-1.3/fel-to-spl-thunk.S000066400000000000000000000116231262340046500171230ustar00rootroot00000000000000/* * Copyright © 2015 Siarhei Siamashka * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ /* Usage instructions: "ruby -x fel-to-spl-thunk.S > fel-to-spl-thunk.h" */ /*************************************************************************/ /* Open a comment for gas. Do not close the comment until after the Ruby code terminator (__END__). Write the '*' '/' sequence of characters as "\x2a/" in string literals to avoid doing so. #!/usr/bin/env ruby def tool_exists(tool_name) `which #{tool_name} > /dev/null 2>&1` return $?.to_i == 0 end toolchains = [ "arm-none-eabi-", "arm-linux-gnueabihf-", "arm-none-linux-gnueabi-", "armv7a-hardfloat-linux-gnueabi-", ] toolchain = toolchains.find { |toolchain| tool_exists("#{toolchain}as") } abort "Can't find any ARM crosscompiler\n" unless toolchain system("#{toolchain}as -o #{$PROGRAM_NAME}.o #{$PROGRAM_NAME}") exit($?.to_i) if $?.to_i != 0 `#{toolchain}objdump -d #{$PROGRAM_NAME}.o`.each_line {|l| next unless l =~ /(\h+)\:\s+(\h+)\s+(\S+)\s+([^;]*)/ printf("\t0x%s, /* %8s: %-10s %-28s \x2a/\n", $2, $1, $3, $4.strip) } __END__ */ /*************************************************************************/ BUF1 .req r0 BUF2 .req r1 TMP1 .req r2 TMP2 .req r3 SWAPTBL .req r4 FULLSIZE .req r5 BUFSIZE .req r6 CHECKSUM .req r7 SPL_ADDR .req r8 entry_point: b setup_stack stack_begin: nop nop nop nop nop nop nop nop stack_end: nop /* A function, which walks the table and swaps all buffers */ swap_all_buffers: adr SWAPTBL, appended_data + 4 swap_next_buffer: ldr BUF1, [SWAPTBL], #4 ldr BUF2, [SWAPTBL], #4 ldr BUFSIZE, [SWAPTBL], #4 cmp BUFSIZE, #0 bxeq lr swap_next_word: ldr TMP1, [BUF1] ldr TMP2, [BUF2] subs BUFSIZE, BUFSIZE, #4 str TMP1, [BUF2], #4 str TMP2, [BUF1], #4 bne swap_next_word b swap_next_buffer setup_stack: /* Save the original SP, LR and CPSR to stack */ ldr SPL_ADDR, appended_data adr BUF1, stack_end str sp, [BUF1, #-4]! mov sp, BUF1 mrs TMP1, cpsr push {TMP1, lr} /* Disable IRQ and FIQ */ orr TMP1, #0xc0 msr cpsr_c, TMP1 /* Check if the instructions or data cache is enabled */ mrc p15, 0, TMP1, c1, c0, 0 movw TMP2, #((1 << 12) | (1 << 2)) tst TMP1, TMP2 bne cache_is_unsupported bl swap_all_buffers verify_checksum: movw CHECKSUM, #0x6c39 movt CHECKSUM, #0x5f0a mov BUF1, SPL_ADDR ldr FULLSIZE, [BUF1, #16] check_next_word: ldr TMP1, [BUF1], #4 subs FULLSIZE, FULLSIZE, #4 add CHECKSUM, CHECKSUM, TMP1 bne check_next_word ldr TMP1, [SPL_ADDR, #12] subs CHECKSUM, CHECKSUM, TMP1, lsl #1 bne checksum_is_bad /* Change 'eGON.BT0' -> 'eGON.FEL' */ movw TMP1, (('F' << 8) + '.') movt TMP1, (('L' << 8) + 'E') str TMP1, [SPL_ADDR, #8] /* Call the SPL code */ dsb isb blx SPL_ADDR /* Return back to FEL */ b return_to_fel cache_is_unsupported: /* Bail out if cache is enabled and change 'eGON.BT0' -> 'eGON.???' */ movw TMP1, (('?' << 8) + '.') movt TMP1, (('?' << 8) + '?') str TMP1, [SPL_ADDR, #8] b return_to_fel_noswap checksum_is_bad: /* The checksum test failed, so change 'eGON.BT0' -> 'eGON.BAD' */ movw TMP1, (('B' << 8) + '.') movt TMP1, (('D' << 8) + 'A') str TMP1, [SPL_ADDR, #8] return_to_fel: bl swap_all_buffers return_to_fel_noswap: pop {TMP1, lr} msr cpsr_c, TMP1 /* Restore the original CPSR */ ldr sp, [sp] bx lr appended_data: /* * The appended data uses the following format: * * struct { * uint32_t spl_addr; * sram_swap_buffers swaptbl[]; * }; * * More details about the 'spl_addr' variable and the 'sram_swap_buffers' * struct can be found in the 'fel.c' source file. */ sunxi-tools-1.3/fel-to-spl-thunk.h000066400000000000000000000112201262340046500171410ustar00rootroot00000000000000 0xea000015, /* 0: b 5c */ 0xe1a00000, /* 4: nop */ 0xe1a00000, /* 8: nop */ 0xe1a00000, /* c: nop */ 0xe1a00000, /* 10: nop */ 0xe1a00000, /* 14: nop */ 0xe1a00000, /* 18: nop */ 0xe1a00000, /* 1c: nop */ 0xe1a00000, /* 20: nop */ 0xe1a00000, /* 24: nop */ 0xe28f40dc, /* 28: add r4, pc, #220 */ 0xe4940004, /* 2c: ldr r0, [r4], #4 */ 0xe4941004, /* 30: ldr r1, [r4], #4 */ 0xe4946004, /* 34: ldr r6, [r4], #4 */ 0xe3560000, /* 38: cmp r6, #0 */ 0x012fff1e, /* 3c: bxeq lr */ 0xe5902000, /* 40: ldr r2, [r0] */ 0xe5913000, /* 44: ldr r3, [r1] */ 0xe2566004, /* 48: subs r6, r6, #4 */ 0xe4812004, /* 4c: str r2, [r1], #4 */ 0xe4803004, /* 50: str r3, [r0], #4 */ 0x1afffff9, /* 54: bne 40 */ 0xeafffff3, /* 58: b 2c */ 0xe59f80a4, /* 5c: ldr r8, [pc, #164] */ 0xe24f0044, /* 60: sub r0, pc, #68 */ 0xe520d004, /* 64: str sp, [r0, #-4]! */ 0xe1a0d000, /* 68: mov sp, r0 */ 0xe10f2000, /* 6c: mrs r2, CPSR */ 0xe92d4004, /* 70: push {r2, lr} */ 0xe38220c0, /* 74: orr r2, r2, #192 */ 0xe121f002, /* 78: msr CPSR_c, r2 */ 0xee112f10, /* 7c: mrc 15, 0, r2, cr1, cr0, {0} */ 0xe3013004, /* 80: movw r3, #4100 */ 0xe1120003, /* 84: tst r2, r3 */ 0x1a000012, /* 88: bne d8 */ 0xebffffe5, /* 8c: bl 28 */ 0xe3067c39, /* 90: movw r7, #27705 */ 0xe3457f0a, /* 94: movt r7, #24330 */ 0xe1a00008, /* 98: mov r0, r8 */ 0xe5905010, /* 9c: ldr r5, [r0, #16] */ 0xe4902004, /* a0: ldr r2, [r0], #4 */ 0xe2555004, /* a4: subs r5, r5, #4 */ 0xe0877002, /* a8: add r7, r7, r2 */ 0x1afffffb, /* ac: bne a0 */ 0xe598200c, /* b0: ldr r2, [r8, #12] */ 0xe0577082, /* b4: subs r7, r7, r2, lsl #1 */ 0x1a00000a, /* b8: bne e8 */ 0xe304262e, /* bc: movw r2, #17966 */ 0xe3442c45, /* c0: movt r2, #19525 */ 0xe5882008, /* c4: str r2, [r8, #8] */ 0xf57ff04f, /* c8: dsb sy */ 0xf57ff06f, /* cc: isb sy */ 0xe12fff38, /* d0: blx r8 */ 0xea000006, /* d4: b f4 */ 0xe3032f2e, /* d8: movw r2, #16174 */ 0xe3432f3f, /* dc: movt r2, #16191 */ 0xe5882008, /* e0: str r2, [r8, #8] */ 0xea000003, /* e4: b f8 */ 0xe304222e, /* e8: movw r2, #16942 */ 0xe3442441, /* ec: movt r2, #17473 */ 0xe5882008, /* f0: str r2, [r8, #8] */ 0xebffffcb, /* f4: bl 28 */ 0xe8bd4004, /* f8: pop {r2, lr} */ 0xe121f002, /* fc: msr CPSR_c, r2 */ 0xe59dd000, /* 100: ldr sp, [sp] */ 0xe12fff1e, /* 104: bx lr */ sunxi-tools-1.3/fel.c000066400000000000000000001110671262340046500146030ustar00rootroot00000000000000/* * Copyright (C) 2012 Henrik Nordstrom * * 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, see . */ /* Needs _BSD_SOURCE for htole and letoh */ /* glibc 2.20+ also requires _DEFAULT_SOURCE */ #define _DEFAULT_SOURCE #define _BSD_SOURCE #define _NETBSD_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "endian_compat.h" struct aw_usb_request { char signature[8]; uint32_t length; uint32_t unknown1; /* 0x0c000000 */ uint16_t request; uint32_t length2; /* Same as length */ char pad[10]; } __attribute__((packed)); struct aw_fel_version { char signature[8]; uint32_t soc_id; /* 0x00162300 */ uint32_t unknown_0a; /* 1 */ uint16_t protocol; /* 1 */ uint8_t unknown_12; /* 0x44 */ uint8_t unknown_13; /* 0x08 */ uint32_t scratchpad; /* 0x7e00 */ uint32_t pad[2]; /* unused */ } __attribute__((packed)); static const int AW_USB_READ = 0x11; static const int AW_USB_WRITE = 0x12; static int AW_USB_FEL_BULK_EP_OUT; static int AW_USB_FEL_BULK_EP_IN; static int timeout = 60000; static bool verbose = false; /* If set, makes the 'fel' tool more talkative */ static uint32_t uboot_entry = 0; /* entry point (address) of U-Boot */ static uint32_t uboot_size = 0; /* size of U-Boot binary */ static void pr_info(const char *fmt, ...) { va_list arglist; if (verbose) { va_start(arglist, fmt); vprintf(fmt, arglist); va_end(arglist); } } static const int AW_USB_MAX_BULK_SEND = 4 * 1024 * 1024; // 4 MiB per bulk request void usb_bulk_send(libusb_device_handle *usb, int ep, const void *data, int length) { int rc, sent; while (length > 0) { int len = length < AW_USB_MAX_BULK_SEND ? length : AW_USB_MAX_BULK_SEND; rc = libusb_bulk_transfer(usb, ep, (void *)data, len, &sent, timeout); if (rc != 0) { fprintf(stderr, "libusb usb_bulk_send error %d\n", rc); exit(2); } length -= sent; data += sent; } } void usb_bulk_recv(libusb_device_handle *usb, int ep, void *data, int length) { int rc, recv; while (length > 0) { rc = libusb_bulk_transfer(usb, ep, data, length, &recv, timeout); if (rc != 0) { fprintf(stderr, "usb_bulk_recv error %d\n", rc); exit(2); } length -= recv; data += recv; } } /* Constants taken from ${U-BOOT}/include/image.h */ #define IH_MAGIC 0x27051956 /* Image Magic Number */ #define IH_ARCH_ARM 2 /* ARM */ #define IH_TYPE_INVALID 0 /* Invalid Image */ #define IH_TYPE_FIRMWARE 5 /* Firmware Image */ #define IH_TYPE_SCRIPT 6 /* Script file */ #define IH_NMLEN 32 /* Image Name Length */ /* Additional error codes, newly introduced for get_image_type() */ #define IH_TYPE_ARCH_MISMATCH -1 #define HEADER_NAME_OFFSET 32 /* offset of name field */ #define HEADER_SIZE (HEADER_NAME_OFFSET + IH_NMLEN) /* * Utility function to determine the image type from a mkimage-compatible * header at given buffer (address). * * For invalid headers (insufficient size or 'magic' mismatch) the function * will return IH_TYPE_INVALID. Negative return values might indicate * special error conditions, e.g. IH_TYPE_ARCH_MISMATCH signals that the * image doesn't match the expected (ARM) architecture. * Otherwise the function will return the "ih_type" field for valid headers. */ int get_image_type(const uint8_t *buf, size_t len) { uint32_t *buf32 = (uint32_t *)buf; if (len <= HEADER_SIZE) /* insufficient length/size */ return IH_TYPE_INVALID; if (be32toh(buf32[0]) != IH_MAGIC) /* signature mismatch */ return IH_TYPE_INVALID; /* For sunxi, we always expect ARM architecture here */ if (buf[29] != IH_ARCH_ARM) return IH_TYPE_ARCH_MISMATCH; /* assume a valid header, and return ih_type */ return buf[30]; } void aw_send_usb_request(libusb_device_handle *usb, int type, int length) { struct aw_usb_request req; memset(&req, 0, sizeof(req)); strcpy(req.signature, "AWUC"); req.length = req.length2 = htole32(length); req.request = htole16(type); req.unknown1 = htole32(0x0c000000); usb_bulk_send(usb, AW_USB_FEL_BULK_EP_OUT, &req, sizeof(req)); } void aw_read_usb_response(libusb_device_handle *usb) { char buf[13]; usb_bulk_recv(usb, AW_USB_FEL_BULK_EP_IN, &buf, sizeof(buf)); assert(strcmp(buf, "AWUS") == 0); } void aw_usb_write(libusb_device_handle *usb, const void *data, size_t len) { aw_send_usb_request(usb, AW_USB_WRITE, len); usb_bulk_send(usb, AW_USB_FEL_BULK_EP_OUT, data, len); aw_read_usb_response(usb); } void aw_usb_read(libusb_device_handle *usb, const void *data, size_t len) { aw_send_usb_request(usb, AW_USB_READ, len); usb_bulk_send(usb, AW_USB_FEL_BULK_EP_IN, data, len); aw_read_usb_response(usb); } struct aw_fel_request { uint32_t request; uint32_t address; uint32_t length; uint32_t pad; }; static const int AW_FEL_VERSION = 0x001; static const int AW_FEL_1_WRITE = 0x101; static const int AW_FEL_1_EXEC = 0x102; static const int AW_FEL_1_READ = 0x103; void aw_send_fel_request(libusb_device_handle *usb, int type, uint32_t addr, uint32_t length) { struct aw_fel_request req; memset(&req, 0, sizeof(req)); req.request = htole32(type); req.address = htole32(addr); req.length = htole32(length); aw_usb_write(usb, &req, sizeof(req)); } void aw_read_fel_status(libusb_device_handle *usb) { char buf[8]; aw_usb_read(usb, &buf, sizeof(buf)); } void aw_fel_get_version(libusb_device_handle *usb, struct aw_fel_version *buf) { aw_send_fel_request(usb, AW_FEL_VERSION, 0, 0); aw_usb_read(usb, buf, sizeof(*buf)); aw_read_fel_status(usb); buf->soc_id = (le32toh(buf->soc_id) >> 8) & 0xFFFF; buf->unknown_0a = le32toh(buf->unknown_0a); buf->protocol = le32toh(buf->protocol); buf->scratchpad = le16toh(buf->scratchpad); buf->pad[0] = le32toh(buf->pad[0]); buf->pad[1] = le32toh(buf->pad[1]); } void aw_fel_print_version(libusb_device_handle *usb) { struct aw_fel_version buf; aw_fel_get_version(usb, &buf); const char *soc_name="unknown"; switch (buf.soc_id) { case 0x1623: soc_name="A10";break; case 0x1625: soc_name="A13";break; case 0x1633: soc_name="A31";break; case 0x1651: soc_name="A20";break; case 0x1650: soc_name="A23";break; case 0x1639: soc_name="A80";break; case 0x1667: soc_name="A33";break; case 0x1673: soc_name="A83T";break; case 0x1680: soc_name="H3";break; } printf("%.8s soc=%08x(%s) %08x ver=%04x %02x %02x scratchpad=%08x %08x %08x\n", buf.signature, buf.soc_id, soc_name, buf.unknown_0a, buf.protocol, buf.unknown_12, buf.unknown_13, buf.scratchpad, buf.pad[0], buf.pad[1]); } void aw_fel_read(libusb_device_handle *usb, uint32_t offset, void *buf, size_t len) { aw_send_fel_request(usb, AW_FEL_1_READ, offset, len); aw_usb_read(usb, buf, len); aw_read_fel_status(usb); } void aw_fel_write(libusb_device_handle *usb, void *buf, uint32_t offset, size_t len) { /* safeguard against overwriting an already loaded U-Boot binary */ if (uboot_size > 0 && offset <= uboot_entry + uboot_size && offset + len >= uboot_entry) { fprintf(stderr, "ERROR: Attempt to overwrite U-Boot! " "Request 0x%08X-0x%08X overlaps 0x%08X-0x%08X.\n", offset, offset + (int)len, uboot_entry, uboot_entry + uboot_size); exit(1); } aw_send_fel_request(usb, AW_FEL_1_WRITE, offset, len); aw_usb_write(usb, buf, len); aw_read_fel_status(usb); } void aw_fel_execute(libusb_device_handle *usb, uint32_t offset) { aw_send_fel_request(usb, AW_FEL_1_EXEC, offset, 0); aw_read_fel_status(usb); } void hexdump(void *data, uint32_t offset, size_t size) { size_t j; unsigned char *buf = data; for (j = 0; j < size; j+=16) { size_t i; printf("%08lx: ",(long int)offset + j); for (i = 0; i < 16; i++) { if ((j+i) < size) { printf("%02x ", buf[j+i]); } else { printf("__ "); } } printf(" "); for (i = 0; i < 16; i++) { if (j+i >= size) { printf("."); } else if (isprint(buf[j+i])) { printf("%c", buf[j+i]); } else { printf("."); } } printf("\n"); } } int save_file(const char *name, void *data, size_t size) { FILE *out = fopen(name, "wb"); int rc; if (!out) { perror("Failed to open output file: "); exit(1); } rc = fwrite(data, size, 1, out); fclose(out); return rc; } void *load_file(const char *name, size_t *size) { size_t bufsize = 8192; size_t offset = 0; char *buf = malloc(bufsize); FILE *in; if (strcmp(name, "-") == 0) in = stdin; else in = fopen(name, "rb"); if (!in) { perror("Failed to open input file: "); exit(1); } while(1) { ssize_t len = bufsize - offset; ssize_t n = fread(buf+offset, 1, len, in); offset += n; if (n < len) break; bufsize <<= 1; buf = realloc(buf, bufsize); } if (size) *size = offset; if (in != stdin) fclose(in); return buf; } void aw_fel_hexdump(libusb_device_handle *usb, uint32_t offset, size_t size) { unsigned char buf[size]; aw_fel_read(usb, offset, buf, size); hexdump(buf, offset, size); } void aw_fel_dump(libusb_device_handle *usb, uint32_t offset, size_t size) { unsigned char buf[size]; aw_fel_read(usb, offset, buf, size); fwrite(buf, size, 1, stdout); } void aw_fel_fill(libusb_device_handle *usb, uint32_t offset, size_t size, unsigned char value) { unsigned char buf[size]; memset(buf, value, size); aw_fel_write(usb, buf, offset, size); } /* * The 'sram_swap_buffers' structure is used to describe information about * two buffers in SRAM, the content of which needs to be exchanged before * calling the U-Boot SPL code and then exchanged again before returning * control back to the FEL code from the BROM. */ typedef struct { uint32_t buf1; /* BROM buffer */ uint32_t buf2; /* backup storage location */ uint32_t size; /* buffer size */ } sram_swap_buffers; /* * Each SoC variant may have its own list of memory buffers to be exchanged * and the information about the placement of the thunk code, which handles * the transition of execution from the BROM FEL code to the U-Boot SPL and * back. * * Note: the entries in the 'swap_buffers' tables need to be sorted by 'buf1' * addresses. And the 'buf1' addresses are the BROM data buffers, while 'buf2' * addresses are the intended backup locations. */ typedef struct { uint32_t soc_id; /* ID of the SoC */ uint32_t spl_addr; /* SPL load address */ uint32_t scratch_addr; /* A safe place to upload & run code */ uint32_t thunk_addr; /* Address of the thunk code */ uint32_t thunk_size; /* Maximal size of the thunk code */ uint32_t needs_l2en; /* Set the L2EN bit */ sram_swap_buffers *swap_buffers; } soc_sram_info; /* * The FEL code from BROM in A10/A13/A20 sets up two stacks for itself. One * at 0x2000 (and growing down) for the IRQ handler. And another one at 0x7000 * (and also growing down) for the regular code. In order to use the whole * 32 KiB in the A1/A2 sections of SRAM, we need to temporarily move these * stacks elsewhere. And the addresses above 0x7000 are also a bit suspicious, * so it might be safer to backup the 0x7000-0x8000 area too. On A10/A13/A20 * we can use the SRAM section A3 (0x8000) for this purpose. */ sram_swap_buffers a10_a13_a20_sram_swap_buffers[] = { { .buf1 = 0x01800, .buf2 = 0x8000, .size = 0x800 }, { .buf1 = 0x05C00, .buf2 = 0x8800, .size = 0x8000 - 0x5C00 }, { 0 } /* End of the table */ }; /* * A31 is very similar to A10/A13/A20, except that it has no SRAM at 0x8000. * So we use the SRAM section at 0x44000 instead. This is the memory, which * is normally shared with the OpenRISC core (should we do an extra check to * ensure that this core is powered off and can't interfere?). */ sram_swap_buffers a31_sram_swap_buffers[] = { { .buf1 = 0x01800, .buf2 = 0x44000, .size = 0x800 }, { .buf1 = 0x05C00, .buf2 = 0x44800, .size = 0x8000 - 0x5C00 }, { 0 } /* End of the table */ }; soc_sram_info soc_sram_info_table[] = { { .soc_id = 0x1623, /* Allwinner A10 */ .scratch_addr = 0x2000, .thunk_addr = 0xAE00, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .needs_l2en = 1, }, { .soc_id = 0x1625, /* Allwinner A13 */ .scratch_addr = 0x2000, .thunk_addr = 0xAE00, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .needs_l2en = 1, }, { .soc_id = 0x1651, /* Allwinner A20 */ .scratch_addr = 0x2000, .thunk_addr = 0xAE00, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, }, { .soc_id = 0x1650, /* Allwinner A23 */ .scratch_addr = 0x2000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = a31_sram_swap_buffers, }, { .soc_id = 0x1633, /* Allwinner A31 */ .scratch_addr = 0x2000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = a31_sram_swap_buffers, }, { .soc_id = 0x1667, /* Allwinner A33 */ .scratch_addr = 0x2000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = a31_sram_swap_buffers, }, { .soc_id = 0x1673, /* Allwinner A83T */ .scratch_addr = 0x2000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = a31_sram_swap_buffers, }, { .soc_id = 0x1680, /* Allwinner H3 */ .scratch_addr = 0x2000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = a31_sram_swap_buffers, }, { 0 } /* End of the table */ }; /* * This generic record assumes BROM with similar properties to A10/A13/A20/A31, * but no extra SRAM sections beyond 0x8000. It also assumes that the IRQ * handler stack usage never exceeds 0x400 bytes. * * The users may or may not hope that the 0x7000-0x8000 area is also unused * by the BROM and re-purpose it for the SPL stack. * * The size limit for the ".text + .data" sections is ~21 KiB. */ sram_swap_buffers generic_sram_swap_buffers[] = { { .buf1 = 0x01C00, .buf2 = 0x5800, .size = 0x400 }, { 0 } /* End of the table */ }; soc_sram_info generic_sram_info = { .scratch_addr = 0x2000, .thunk_addr = 0x5680, .thunk_size = 0x180, .swap_buffers = generic_sram_swap_buffers, }; soc_sram_info *aw_fel_get_sram_info(libusb_device_handle *usb) { /* persistent sram_info, retrieves result pointer once and caches it */ static soc_sram_info *result = NULL; if (result == NULL) { int i; struct aw_fel_version buf; aw_fel_get_version(usb, &buf); for (i = 0; soc_sram_info_table[i].swap_buffers; i++) if (soc_sram_info_table[i].soc_id == buf.soc_id) { result = &soc_sram_info_table[i]; break; } if (!result) { printf("Warning: no 'soc_sram_info' data for your SoC (id=%04X)\n", buf.soc_id); result = &generic_sram_info; } } return result; } static uint32_t fel_to_spl_thunk[] = { #include "fel-to-spl-thunk.h" }; #define DRAM_BASE 0x40000000 #define DRAM_SIZE 0x80000000 void aw_enable_l2_cache(libusb_device_handle *usb, soc_sram_info *sram_info) { uint32_t arm_code[] = { htole32(0xee112f30), /* mrc 15, 0, r2, cr1, cr0, {1} */ htole32(0xe3822002), /* orr r2, r2, #2 */ htole32(0xee012f30), /* mcr 15, 0, r2, cr1, cr0, {1} */ htole32(0xe12fff1e), /* bx lr */ }; aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); } void aw_get_stackinfo(libusb_device_handle *usb, soc_sram_info *sram_info, uint32_t *sp_irq, uint32_t *sp) { uint32_t results[2] = { 0 }; #if 0 /* Does not work on Cortex-A8 (needs Virtualization Extensions) */ uint32_t arm_code[] = { htole32(0xe1010300), /* mrs r0, SP_irq */ htole32(0xe58f0004), /* str r0, [pc, #4] */ htole32(0xe58fd004), /* str sp, [pc, #4] */ htole32(0xe12fff1e), /* bx lr */ }; aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); aw_fel_read(usb, sram_info->scratch_addr + 0x10, results, 8); #else /* Works everywhere */ uint32_t arm_code[] = { htole32(0xe10f0000), /* mrs r0, CPSR */ htole32(0xe3c0101f), /* bic r1, r0, #31 */ htole32(0xe3811012), /* orr r1, r1, #18 */ htole32(0xe121f001), /* msr CPSR_c, r1 */ htole32(0xe1a0100d), /* mov r1, sp */ htole32(0xe121f000), /* msr CPSR_c, r0 */ htole32(0xe58f1004), /* str r1, [pc, #4] */ htole32(0xe58fd004), /* str sp, [pc, #4] */ htole32(0xe12fff1e), /* bx lr */ }; aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); aw_fel_read(usb, sram_info->scratch_addr + 0x24, results, 8); #endif *sp_irq = le32toh(results[0]); *sp = le32toh(results[1]); } uint32_t aw_get_ttbr0(libusb_device_handle *usb, soc_sram_info *sram_info) { uint32_t ttbr0 = 0; uint32_t arm_code[] = { htole32(0xee122f10), /* mrc 15, 0, r2, cr2, cr0, {0} */ htole32(0xe58f2008), /* str r2, [pc, #8] */ htole32(0xe12fff1e), /* bx lr */ }; aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); aw_fel_read(usb, sram_info->scratch_addr + 0x14, &ttbr0, sizeof(ttbr0)); ttbr0 = le32toh(ttbr0); return ttbr0; } uint32_t aw_get_sctlr(libusb_device_handle *usb, soc_sram_info *sram_info) { uint32_t sctlr = 0; uint32_t arm_code[] = { htole32(0xee112f10), /* mrc 15, 0, r2, cr1, cr0, {0} */ htole32(0xe58f2008), /* str r2, [pc, #8] */ htole32(0xe12fff1e), /* bx lr */ }; aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); aw_fel_read(usb, sram_info->scratch_addr + 0x14, &sctlr, sizeof(sctlr)); sctlr = le32toh(sctlr); return sctlr; } uint32_t *aw_backup_and_disable_mmu(libusb_device_handle *usb, soc_sram_info *sram_info) { uint32_t *tt = NULL; uint32_t ttbr0 = aw_get_ttbr0(usb, sram_info); uint32_t sctlr = aw_get_sctlr(usb, sram_info); uint32_t i; uint32_t arm_code[] = { /* Disable I-cache, MMU and branch prediction */ htole32(0xee110f10), /* mrc 15, 0, r0, cr1, cr0, {0} */ htole32(0xe3c00001), /* bic r0, r0, #1 */ htole32(0xe3c00a01), /* bic r0, r0, #4096 */ htole32(0xe3c00b02), /* bic r0, r0, #2048 */ htole32(0xee010f10), /* mcr 15, 0, r0, cr1, cr0, {0} */ /* Return back to FEL */ htole32(0xe12fff1e), /* bx lr */ }; if (!(sctlr & 1)) { pr_info("MMU is not enabled by BROM\n"); return NULL; } if ((sctlr >> 28) & 1) { fprintf(stderr, "TEX remap is enabled!\n"); exit(1); } if (ttbr0 & 0x3FFF) { fprintf(stderr, "Unexpected TTBR0 (%08X)\n", ttbr0); exit(1); } tt = malloc(16 * 1024); pr_info("Reading the MMU translation table from 0x%08X\n", ttbr0); aw_fel_read(usb, ttbr0, tt, 16 * 1024); for (i = 0; i < 4096; i++) tt[i] = le32toh(tt[i]); /* Basic sanity checks to be sure that this is a valid table */ for (i = 0; i < 4096; i++) { if (((tt[i] >> 1) & 1) != 1 || ((tt[i] >> 18) & 1) != 0) { fprintf(stderr, "MMU: not a section descriptor\n"); exit(1); } if ((tt[i] >> 20) != i) { fprintf(stderr, "MMU: not a direct mapping\n"); exit(1); } } pr_info("Disabling I-cache, MMU and branch prediction..."); aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); pr_info(" done.\n"); return tt; } void aw_restore_and_enable_mmu(libusb_device_handle *usb, soc_sram_info *sram_info, uint32_t *tt) { uint32_t i; uint32_t ttbr0 = aw_get_ttbr0(usb, sram_info); uint32_t arm_code[] = { /* Invalidate I-cache, TLB and BTB */ htole32(0xe3a00000), /* mov r0, #0 */ htole32(0xee080f17), /* mcr 15, 0, r0, cr8, cr7, {0} */ htole32(0xee070f15), /* mcr 15, 0, r0, cr7, cr5, {0} */ htole32(0xee070fd5), /* mcr 15, 0, r0, cr7, cr5, {6} */ htole32(0xf57ff04f), /* dsb sy */ htole32(0xf57ff06f), /* isb sy */ /* Enable I-cache, MMU and branch prediction */ htole32(0xee110f10), /* mrc 15, 0, r0, cr1, cr0, {0} */ htole32(0xe3800001), /* orr r0, r0, #1 */ htole32(0xe3800a01), /* orr r0, r0, #4096 */ htole32(0xe3800b02), /* orr r0, r0, #2048 */ htole32(0xee010f10), /* mcr 15, 0, r0, cr1, cr0, {0} */ /* Return back to FEL */ htole32(0xe12fff1e), /* bx lr */ }; pr_info("Setting write-combine mapping for DRAM.\n"); for (i = (DRAM_BASE >> 20); i < ((DRAM_BASE + DRAM_SIZE) >> 20); i++) { /* Clear TEXCB bits */ tt[i] &= ~((7 << 12) | (1 << 3) | (1 << 2)); /* Set TEXCB to 00100 (Normal uncached mapping) */ tt[i] |= (1 << 12); } pr_info("Setting cached mapping for BROM.\n"); /* Clear TEXCB bits first */ tt[0xFFF] &= ~((7 << 12) | (1 << 3) | (1 << 2)); /* Set TEXCB to 00111 (Normal write-back cached mapping) */ tt[0xFFF] |= (1 << 12) | /* TEX */ (1 << 3) | /* C */ (1 << 2); /* B */ pr_info("Writing back the MMU translation table.\n"); for (i = 0; i < 4096; i++) tt[i] = htole32(tt[i]); aw_fel_write(usb, tt, ttbr0, 16 * 1024); pr_info("Enabling I-cache, MMU and branch prediction..."); aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); pr_info(" done.\n"); free(tt); } /* * Maximum size of SPL, at the same time this is the start offset * of the main U-Boot image within u-boot-sunxi-with-spl.bin */ #define SPL_LEN_LIMIT 0x8000 void aw_fel_write_and_execute_spl(libusb_device_handle *usb, uint8_t *buf, size_t len) { soc_sram_info *sram_info = aw_fel_get_sram_info(usb); sram_swap_buffers *swap_buffers; char header_signature[9] = { 0 }; size_t i, thunk_size; uint32_t *thunk_buf; uint32_t sp, sp_irq; uint32_t spl_checksum, spl_len, spl_len_limit = SPL_LEN_LIMIT; uint32_t *buf32 = (uint32_t *)buf; uint32_t cur_addr = sram_info->spl_addr; uint32_t *tt = NULL; if (!sram_info || !sram_info->swap_buffers) { fprintf(stderr, "SPL: Unsupported SoC type\n"); exit(1); } if (len < 32 || memcmp(buf + 4, "eGON.BT0", 8) != 0) { fprintf(stderr, "SPL: eGON header is not found\n"); exit(1); } spl_checksum = 2 * le32toh(buf32[3]) - 0x5F0A6C39; spl_len = le32toh(buf32[4]); if (spl_len > len || (spl_len % 4) != 0) { fprintf(stderr, "SPL: bad length in the eGON header\n"); exit(1); } len = spl_len; for (i = 0; i < len / 4; i++) spl_checksum -= le32toh(buf32[i]); if (spl_checksum != 0) { fprintf(stderr, "SPL: checksum check failed\n"); exit(1); } if (sram_info->needs_l2en) { pr_info("Enabling the L2 cache\n"); aw_enable_l2_cache(usb, sram_info); } aw_get_stackinfo(usb, sram_info, &sp_irq, &sp); pr_info("Stack pointers: sp_irq=0x%08X, sp=0x%08X\n", sp_irq, sp); tt = aw_backup_and_disable_mmu(usb, sram_info); swap_buffers = sram_info->swap_buffers; for (i = 0; swap_buffers[i].size; i++) { if ((swap_buffers[i].buf2 >= sram_info->spl_addr) && (swap_buffers[i].buf2 < sram_info->spl_addr + spl_len_limit)) spl_len_limit = swap_buffers[i].buf2 - sram_info->spl_addr; if (len > 0 && cur_addr < swap_buffers[i].buf1) { uint32_t tmp = swap_buffers[i].buf1 - cur_addr; if (tmp > len) tmp = len; aw_fel_write(usb, buf, cur_addr, tmp); cur_addr += tmp; buf += tmp; len -= tmp; } if (len > 0 && cur_addr == swap_buffers[i].buf1) { uint32_t tmp = swap_buffers[i].size; if (tmp > len) tmp = len; aw_fel_write(usb, buf, swap_buffers[i].buf2, tmp); cur_addr += tmp; buf += tmp; len -= tmp; } } /* Clarify the SPL size limitations, and bail out if they are not met */ if (sram_info->thunk_addr < spl_len_limit) spl_len_limit = sram_info->thunk_addr; if (spl_len > spl_len_limit) { fprintf(stderr, "SPL: too large (need %d, have %d)\n", (int)spl_len, (int)spl_len_limit); exit(1); } /* Write the remaining part of the SPL */ if (len > 0) aw_fel_write(usb, buf, cur_addr, len); thunk_size = sizeof(fel_to_spl_thunk) + sizeof(sram_info->spl_addr) + (i + 1) * sizeof(*swap_buffers); if (thunk_size > sram_info->thunk_size) { fprintf(stderr, "SPL: bad thunk size (need %d, have %d)\n", (int)sizeof(fel_to_spl_thunk), sram_info->thunk_size); exit(1); } thunk_buf = malloc(thunk_size); memcpy(thunk_buf, fel_to_spl_thunk, sizeof(fel_to_spl_thunk)); memcpy(thunk_buf + sizeof(fel_to_spl_thunk) / sizeof(uint32_t), &sram_info->spl_addr, sizeof(sram_info->spl_addr)); memcpy(thunk_buf + sizeof(fel_to_spl_thunk) / sizeof(uint32_t) + 1, swap_buffers, (i + 1) * sizeof(*swap_buffers)); for (i = 0; i < thunk_size / sizeof(uint32_t); i++) thunk_buf[i] = htole32(thunk_buf[i]); pr_info("=> Executing the SPL..."); aw_fel_write(usb, thunk_buf, sram_info->thunk_addr, thunk_size); aw_fel_execute(usb, sram_info->thunk_addr); pr_info(" done.\n"); free(thunk_buf); /* TODO: Try to find and fix the bug, which needs this workaround */ usleep(250000); /* Read back the result and check if everything was fine */ aw_fel_read(usb, sram_info->spl_addr + 4, header_signature, 8); if (strcmp(header_signature, "eGON.FEL") != 0) { fprintf(stderr, "SPL: failure code '%s'\n", header_signature); exit(1); } /* re-enable the MMU if it was enabled by BROM */ if(tt != NULL) aw_restore_and_enable_mmu(usb, sram_info, tt); } /* * This function tests a given buffer address and length for a valid U-Boot * image. Upon success, the image data gets transferred to the default memory * address stored within the image header; and the function preserves the * U-Boot entry point (offset) and size values. */ void aw_fel_write_uboot_image(libusb_device_handle *usb, uint8_t *buf, size_t len) { if (len <= HEADER_SIZE) return; /* Insufficient size (no actual data), just bail out */ uint32_t *buf32 = (uint32_t *)buf; /* Check for a valid mkimage header */ int image_type = get_image_type(buf, len); if (image_type <= IH_TYPE_INVALID) { switch (image_type) { case IH_TYPE_INVALID: fprintf(stderr, "Invalid U-Boot image: bad size or signature\n"); break; case IH_TYPE_ARCH_MISMATCH: fprintf(stderr, "Invalid U-Boot image: wrong architecture\n"); break; default: fprintf(stderr, "Invalid U-Boot image: error code %d\n", image_type); } exit(1); } if (image_type != IH_TYPE_FIRMWARE) { fprintf(stderr, "U-Boot image type mismatch: " "expected IH_TYPE_FIRMWARE, got %02X\n", image_type); exit(1); } uint32_t data_size = be32toh(buf32[3]); /* Image Data Size */ uint32_t load_addr = be32toh(buf32[4]); /* Data Load Address */ if (data_size != len - HEADER_SIZE) { fprintf(stderr, "U-Boot image data size mismatch: " "expected %zu, got %u\n", len - HEADER_SIZE, data_size); exit(1); } /* TODO: Verify image data integrity using the checksum field ih_dcrc, * available from be32toh(buf32[6]) * * However, this requires CRC routines that mimic their U-Boot * counterparts, namely image_check_dcrc() in ${U-BOOT}/common/image.c * and crc_wd() in ${U-BOOT}/lib/crc32.c * * It should be investigated if existing CRC routines in sunxi-tools * could be factored out and reused for this purpose - e.g. calc_crc32() * from nand-part-main.c */ /* If we get here, we're "good to go" (i.e. actually write the data) */ pr_info("Writing image \"%.*s\", %u bytes @ 0x%08X.\n", IH_NMLEN, buf + HEADER_NAME_OFFSET, data_size, load_addr); aw_fel_write(usb, buf + HEADER_SIZE, load_addr, data_size); /* keep track of U-Boot memory region in global vars */ uboot_entry = load_addr; uboot_size = data_size; } /* * This function handles the common part of both "spl" and "uboot" commands. */ void aw_fel_process_spl_and_uboot(libusb_device_handle *usb, const char *filename) { /* load file into memory buffer */ size_t size; uint8_t *buf = load_file(filename, &size); /* write and execute the SPL from the buffer */ aw_fel_write_and_execute_spl(usb, buf, size); /* check for optional main U-Boot binary (and transfer it, if applicable) */ if (size > SPL_LEN_LIMIT) aw_fel_write_uboot_image(usb, buf + SPL_LEN_LIMIT, size - SPL_LEN_LIMIT); } /* * Test the SPL header for our "sunxi" variant. We want to make sure that * we can safely use specific header fields to pass information to U-Boot. * In case of a missing signature (e.g. Allwinner boot0) or header version * mismatch, this function will return "false". If all seems fine, * the result is "true". */ #define SPL_SIGNATURE "SPL" /* marks "sunxi" header */ #define SPL_MIN_VERSION 1 /* minimum required version */ #define SPL_MAX_VERSION 1 /* maximum supported version */ bool have_sunxi_spl(libusb_device_handle *usb, uint32_t spl_addr) { uint8_t spl_signature[4]; aw_fel_read(usb, spl_addr + 0x14, &spl_signature, sizeof(spl_signature)); if (memcmp(spl_signature, SPL_SIGNATURE, 3) != 0) return false; /* signature mismatch, no "sunxi" SPL */ if (spl_signature[3] < SPL_MIN_VERSION) { fprintf(stderr, "sunxi SPL version mismatch: " "found 0x%02X < required minimum 0x%02X\n", spl_signature[3], SPL_MIN_VERSION); fprintf(stderr, "You need to update your U-Boot (mksunxiboot) to a more recent version.\n"); return false; } if (spl_signature[3] > SPL_MAX_VERSION) { fprintf(stderr, "sunxi SPL version mismatch: " "found 0x%02X > maximum supported 0x%02X\n", spl_signature[3], SPL_MAX_VERSION); fprintf(stderr, "You need a more recent version of this (sunxi-tools) fel utility.\n"); return false; } return true; /* sunxi SPL and suitable version */ } /* * Pass information to U-Boot via specialized fields in the SPL header * (see "boot_file_head" in ${U-BOOT}/arch/arm/include/asm/arch-sunxi/spl.h), * providing the boot script address (DRAM location of boot.scr). */ void pass_fel_information(libusb_device_handle *usb, uint32_t script_address) { soc_sram_info *sram_info = aw_fel_get_sram_info(usb); /* write something _only_ if we have a suitable SPL header */ if (have_sunxi_spl(usb, sram_info->spl_addr)) { pr_info("Passing boot info via sunxi SPL: script address = 0x%08X\n", script_address); aw_fel_write(usb, &script_address, sram_info->spl_addr + 0x18, sizeof(script_address)); } } static int aw_fel_get_endpoint(libusb_device_handle *usb) { struct libusb_device *dev = libusb_get_device(usb); struct libusb_config_descriptor *config; int if_idx, set_idx, ep_idx, ret; ret = libusb_get_active_config_descriptor(dev, &config); if (ret) return ret; for (if_idx = 0; if_idx < config->bNumInterfaces; if_idx++) { const struct libusb_interface *iface = config->interface + if_idx; for (set_idx = 0; set_idx < iface->num_altsetting; set_idx++) { const struct libusb_interface_descriptor *setting = iface->altsetting + set_idx; for (ep_idx = 0; ep_idx < setting->bNumEndpoints; ep_idx++) { const struct libusb_endpoint_descriptor *ep = setting->endpoint + ep_idx; // Test for bulk transfer endpoint if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) != LIBUSB_TRANSFER_TYPE_BULK) continue; if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) AW_USB_FEL_BULK_EP_IN = ep->bEndpointAddress; else AW_USB_FEL_BULK_EP_OUT = ep->bEndpointAddress; } } } libusb_free_config_descriptor(config); return 0; } /* Less reliable than clock_gettime, but does not require linking with -lrt */ static double gettime(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + (double)tv.tv_usec / 1000000.; } int main(int argc, char **argv) { int rc; libusb_device_handle *handle = NULL; int iface_detached = -1; rc = libusb_init(NULL); assert(rc == 0); if (argc <= 1) { printf("Usage: %s [options] command arguments... [command...]\n" " -v, --verbose Verbose logging\n" "\n" " spl file Load and execute U-Boot SPL\n" " If file additionally contains a main U-Boot binary\n" " (u-boot-sunxi-with-spl.bin), this command also transfers that\n" " to memory (default address from image), but won't execute it.\n" "\n" " uboot file-with-spl like \"spl\", but actually starts U-Boot\n" " U-Boot execution will take place when the fel utility exits.\n" " This allows combining \"uboot\" with further \"write\" commands\n" " (to transfer other files needed for the boot).\n" "\n" " hex[dump] address length Dumps memory region in hex\n" " dump address length Binary memory dump\n" " exe[cute] address Call function address\n" " read address length file Write memory contents into file\n" " write address file Store file contents into memory\n" " ver[sion] Show BROM version\n" " clear address length Clear memory\n" " fill address length value Fill memory\n" , argv[0] ); } handle = libusb_open_device_with_vid_pid(NULL, 0x1f3a, 0xefe8); if (!handle) { switch (errno) { case EACCES: fprintf(stderr, "ERROR: You don't have permission to access Allwinner USB FEL device\n"); break; default: fprintf(stderr, "ERROR: Allwinner USB FEL device not found!\n"); break; } exit(1); } rc = libusb_claim_interface(handle, 0); #if defined(__linux__) if (rc != LIBUSB_SUCCESS) { libusb_detach_kernel_driver(handle, 0); iface_detached = 0; rc = libusb_claim_interface(handle, 0); } #endif assert(rc == 0); if (aw_fel_get_endpoint(handle)) { fprintf(stderr, "ERROR: Failed to get FEL mode endpoint addresses!\n"); exit(1); } if (argc > 1 && (strcmp(argv[1], "--verbose") == 0 || strcmp(argv[1], "-v") == 0)) { verbose = true; argc -= 1; argv += 1; } while (argc > 1 ) { int skip = 1; if (strncmp(argv[1], "hex", 3) == 0 && argc > 3) { aw_fel_hexdump(handle, strtoul(argv[2], NULL, 0), strtoul(argv[3], NULL, 0)); skip = 3; } else if (strncmp(argv[1], "dump", 4) == 0 && argc > 3) { aw_fel_dump(handle, strtoul(argv[2], NULL, 0), strtoul(argv[3], NULL, 0)); skip = 3; } else if ((strncmp(argv[1], "exe", 3) == 0 && argc > 2) ) { aw_fel_execute(handle, strtoul(argv[2], NULL, 0)); skip=3; } else if (strncmp(argv[1], "ver", 3) == 0 && argc > 1) { aw_fel_print_version(handle); skip=1; } else if (strcmp(argv[1], "write") == 0 && argc > 3) { double t1, t2; size_t size; void *buf = load_file(argv[3], &size); uint32_t offset = strtoul(argv[2], NULL, 0); t1 = gettime(); aw_fel_write(handle, buf, offset, size); t2 = gettime(); if (t2 > t1) pr_info("Written %.1f KB in %.1f sec (speed: %.1f KB/s)\n", (double)size / 1000., t2 - t1, (double)size / (t2 - t1) / 1000.); /* * If we have transferred a script, try to inform U-Boot * about its address. */ if (get_image_type(buf, size) == IH_TYPE_SCRIPT) pass_fel_information(handle, offset); free(buf); skip=3; } else if (strcmp(argv[1], "read") == 0 && argc > 4) { size_t size = strtoul(argv[3], NULL, 0); void *buf = malloc(size); aw_fel_read(handle, strtoul(argv[2], NULL, 0), buf, size); save_file(argv[4], buf, size); free(buf); skip=4; } else if (strcmp(argv[1], "clear") == 0 && argc > 2) { aw_fel_fill(handle, strtoul(argv[2], NULL, 0), strtoul(argv[3], NULL, 0), 0); skip=3; } else if (strcmp(argv[1], "fill") == 0 && argc > 3) { aw_fel_fill(handle, strtoul(argv[2], NULL, 0), strtoul(argv[3], NULL, 0), (unsigned char)strtoul(argv[4], NULL, 0)); skip=4; } else if (strcmp(argv[1], "spl") == 0 && argc > 2) { aw_fel_process_spl_and_uboot(handle, argv[2]); skip=2; } else if (strcmp(argv[1], "uboot") == 0 && argc > 2) { aw_fel_process_spl_and_uboot(handle, argv[2]); if (!uboot_entry) printf("Warning: \"uboot\" command failed to detect image! Can't execute U-Boot.\n"); skip=2; } else { fprintf(stderr,"Invalid command %s\n", argv[1]); exit(1); } argc-=skip; argv+=skip; } // auto-start U-Boot if requested (by the "uboot" command) if (uboot_entry > 0 && uboot_size > 0) { pr_info("Starting U-Boot (0x%08X).\n", uboot_entry); aw_fel_execute(handle, uboot_entry); } #if defined(__linux__) if (iface_detached >= 0) libusb_attach_kernel_driver(handle, iface_detached); #endif return 0; } sunxi-tools-1.3/fexc.c000066400000000000000000000165161262340046500147650ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #include "fexc.h" #include #include #include #include #include #include #include #include #define pr_info(...) errf("fexc: " __VA_ARGS__) #define pr_err(...) errf("E: fexc: " __VA_ARGS__) enum script_format { FEX_SCRIPT_FORMAT, BIN_SCRIPT_FORMAT, UBOOT_HEADER_FORMAT, }; /* */ static inline char *read_all(int fd, const char *filename, size_t *size) { size_t buf_size = 4096, count = 0; char *p, *buf = malloc(buf_size); if (!buf) { pr_err("%s: %s\n", "malloc", strerror(errno)); return NULL; } p = buf; while (1) { ssize_t rc = read(fd, p, buf_size-count); if (rc == 0) break; else if (rc > 0) { count += rc; p += rc; if (count == buf_size) { char *new; buf_size *= 2; new = realloc(buf, buf_size); if (!new) { pr_err("%s: %s\n", "realloc", strerror(errno)); free(buf); return NULL; } else if (new != buf) { buf = new; p = buf + count; } } } else if (errno != EAGAIN && errno != EINTR) { pr_err("%s: %s: %s\n", filename, "read", strerror(errno)); free(buf); return NULL; } } *size = count; return buf; } /* */ static inline int script_parse(enum script_format format, const char *filename, struct script *script) { int ret = 0; switch (format) { case FEX_SCRIPT_FORMAT: { FILE *in = stdin; if (!filename) filename = ""; else if ((in = fopen(filename, "r")) == NULL) { pr_err("%s: %s\n", filename, strerror(errno)); break; } ret = script_parse_fex(in, filename, script); fclose(in); }; break; case BIN_SCRIPT_FORMAT: { int in = 0; /* stdin */ struct stat sb; void *bin = NULL; size_t bin_size; int allocated = 1; if (!filename) filename = ""; else if ((in = open(filename, O_RDONLY)) < 0) { pr_err("%s: %s\n", filename, strerror(errno)); break; } if (fstat(in, &sb) == -1) { pr_err("%s: %s: %s\n", filename, "fstat", strerror(errno)); goto bin_close; } else if (S_ISREG(sb.st_mode)) { /* regular file, mmap it */ bin = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, in, 0); if (bin == MAP_FAILED) { pr_err("%s: %s: %s\n", filename, "mmap", strerror(errno)); goto bin_close; } bin_size = sb.st_size; allocated = 0; } else { /* something else... just read it all! */ bin = read_all(in, filename, &bin_size); if (bin == NULL) goto bin_close; allocated = 1; } ret = script_decompile_bin(bin, bin_size, filename, script); if (allocated) free(bin); else if (munmap(bin, bin_size) == -1) { pr_err("%s: %s: %s\n", filename, "munmap", strerror(errno)); } bin_close: close(in); }; break; case UBOOT_HEADER_FORMAT: /* not valid input */ ; } return ret; } static inline int script_generate(enum script_format format, const char *filename, struct script *script) { int ret = 0; static int (*text_gen[3]) (FILE *, const char *, struct script *) = { [FEX_SCRIPT_FORMAT] = script_generate_fex, [UBOOT_HEADER_FORMAT] = script_generate_uboot, }; if (text_gen[format]) { FILE *out = stdout; if (!filename) filename = ""; else if ((out = fopen(filename, "w")) == NULL) { pr_err("%s: %s\n", filename, strerror(errno)); goto done; } ret = text_gen[format](out, filename, script); fclose(out); } else { int out = 1; /* stdout */ size_t sections, entries, bin_size; void *bin; if (!filename) filename = ""; else if ((out = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) { pr_err("%s: %s\n", filename, strerror(errno)); goto done; } bin_size = script_bin_size(script, §ions, &entries); bin = calloc(1, bin_size); if (!bin) pr_err("%s: %s\n", "malloc", strerror(errno)); else if (script_generate_bin(bin, bin_size, script, sections, entries)) { char *p = bin; while(bin_size) { ssize_t wc = write(out, p, bin_size); if (wc>0) { p += wc; bin_size -= wc; } else if (wc < 0 && errno != EINTR) { pr_err("%s: %s: %s\n", filename, "write", strerror(errno)); break; } } ret = (bin_size == 0); } free(bin); close(out); } done: return ret; } /* */ static inline void app_usage(const char *arg0, int mode) { errf("Usage: %s [-vq]%s[ []]\n", arg0, mode ? " " : " [-I ] [-O ] "); if (mode == 0) fputs("\ninfmt: fex, bin (default:fex)" "\noutfmt: fex, bin, uboot (default:bin)\n", stderr); } static inline int app_choose_mode(char *arg0) { const char *name = basename(arg0); if (strcmp(name, "fex2bin") == 0) return 1; else if (strcmp(name, "bin2fex") == 0) return 2; else return 0; } /* */ int main(int argc, char *argv[]) { static const char *formats[] = { "fex", "bin", "uboot", NULL }; enum script_format infmt=FEX_SCRIPT_FORMAT; enum script_format outfmt=BIN_SCRIPT_FORMAT; const char *filename[] = { NULL /*stdin*/, NULL /*stdout*/}; struct script *script; int app_mode = app_choose_mode(argv[0]); const char *opt_string = "I:O:vq?"+ ((app_mode == 0)? 0: 4); int opt, ret = 1; int verbose = 0; if (app_mode == 2) { /* bin2fex */ infmt = BIN_SCRIPT_FORMAT; outfmt = FEX_SCRIPT_FORMAT; } while ((opt = getopt(argc, argv, opt_string)) != -1) { switch (opt) { case 'I': infmt=0; for (const char **f = formats; *f; f++, infmt++) { if (strcmp(*f, optarg) == 0) break; } switch (infmt) { case FEX_SCRIPT_FORMAT: case BIN_SCRIPT_FORMAT: break; default: errf("%s: invalid format -- \"%s\"\n", argv[0], optarg); goto show_usage; } break; case 'O': outfmt=0; for (const char **f = formats; *f; f++, outfmt++) { if (strcmp(*f, optarg) == 0) break; } if (!formats[outfmt]) { errf("%s: invalid format -- \"%s\"\n", argv[0], optarg); goto show_usage; } break; case 'v': verbose++; break; case 'q': verbose--; break; default: show_usage: app_usage(argv[0], app_mode); goto done; } } switch (argc - optind) { case 2: filename[1] = argv[optind+1]; /* out */ case 1: if (strcmp(argv[optind], "-") != 0) filename[0] = argv[optind]; /* in */ case 0: break; default: goto show_usage; } if (verbose>0) errf("%s: from %s:%s to %s:%s\n", argv[0], formats[infmt], filename[0]?filename[0]:"", formats[outfmt], filename[1]?filename[1]:""); if ((script = script_new()) == NULL) { perror("malloc"); goto done; } else if (script_parse(infmt, filename[0], script) && script_generate(outfmt, filename[1], script)) { ret = 0; } script_delete(script); done: return ret; } sunxi-tools-1.3/fexc.h000066400000000000000000000016431262340046500147650ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #ifndef _SUNXI_TOOLS_FEXC_H #define _SUNXI_TOOLS_FEXC_H #include "common.h" #include #include #include "script.h" #include "script_bin.h" #include "script_fex.h" #include "script_uboot.h" #endif sunxi-tools-1.3/include/000077500000000000000000000000001262340046500153065ustar00rootroot00000000000000sunxi-tools-1.3/include/endian_compat.h000066400000000000000000000027161262340046500202660ustar00rootroot00000000000000/* * Copyright (C) 2012 Eric Molitor * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef SUNXI_ENDIAN_COMPAT_H_ #define SUNXI_ENDIAN_COMPAT_H_ #ifdef __APPLE__ #include #define htole32(x) CFSwapInt32HostToLittle(x) #define le32toh(x) CFSwapInt32LittleToHost(x) #define htole16(x) CFSwapInt16HostToLittle(x) #define le16toh(x) CFSwapInt16LittleToHost(x) #else #include #endif #endif sunxi-tools-1.3/include/types.h000066400000000000000000000023541262340046500166270ustar00rootroot00000000000000/* * (C) Copyright 2012 Henrik Nordstrom * (C) Copyright 2012 Alejandro Mery * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #ifndef SUNXI_TYPES_H #define SUNXI_TYPES_H #include #define __s8 int8_t #define __s16 int16_t #define __s32 int32_t #define __s64 int64_t #define s8 int8_t #define s16 int16_t #define s32 int32_t #define s64 int64_t #define __u8 uint8_t #define __u16 uint16_t #define __u32 uint32_t #define __u64 uint64_t #define u8 uint8_t #define u16 uint16_t #define u32 uint32_t #define u64 uint64_t #endif sunxi-tools-1.3/jtag-loop.S000066400000000000000000000024601262340046500157050ustar00rootroot00000000000000/* * (C) Copyright 2012 Jens Andersen * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ /* Build instructions: arm-none-linux-gnueabi-gcc -g -fno-common -ffixed-r8 -msoft-float -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder jtag-loop.S -c arm-none-linux-gnueabi-objcopy -O binary jtag-loop.o jtag-loop.bin mksunxiboot jtag-loop.bin jtag-loop.sunxi */ .file "fel-loop.S" .global entry .text .code 32 .section ".start", "ax" entry: ldr r0,=0x01c208b4 ldr r1,=0x00444444 str r1, [r0] b . sunxi-tools-1.3/jtag-loop.c000066400000000000000000000024731262340046500157310ustar00rootroot00000000000000/* * (C) Copyright 2012 Jens Andersen * (C) Copyright 2012 Henrik Nordstrom * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ /* Build instructions: arm-none-linux-gnueabi-gcc -g -fno-common -ffixed-r8 -msoft-float -fno-builtin -ffreestanding -nostdinc -mno-thumb-interwork -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fno-toplevel-reorder -Os jtag-loop.c -c arm-none-linux-gnueabi-objcopy -O binary jtag-loop.o jtag-loop.bin mksunxiboot jtag-loop.bin jtag-loop.sunxi */ void _start(void) { *(volatile unsigned long *)0x01c208b4 = 0x00444444; while(1); } sunxi-tools-1.3/jtag-loop.lds000066400000000000000000000014421262340046500162640ustar00rootroot00000000000000/* * Copyright (C) 2012 Henrik Nordstrom * * 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, see . */ SECTIONS { . = 0x0030; .text : { *(.text) } } sunxi-tools-1.3/meminfo.c000066400000000000000000000460641262340046500154730ustar00rootroot00000000000000/* * Copyright (C) 2012 Floris Bos * Copyright (c) 2014 Luc Verhaegen * * 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, see . */ #include #include #include #include #include #include #include #include typedef uint32_t u32; /* from u-boot code: */ struct sun4i_dram_para { u32 baseaddr; u32 clock; u32 type; u32 rank_num; u32 density; u32 io_width; u32 bus_width; u32 cas; u32 zq; u32 odt_en; u32 size; u32 tpr0; u32 tpr1; u32 tpr2; u32 tpr3; u32 tpr4; u32 tpr5; u32 emr1; u32 emr2; u32 emr3; }; #define DEVMEM_FILE "/dev/mem" static int devmem_fd; enum sunxi_soc_version { SUNXI_SOC_SUN4I = 0x1623, /* A10 */ SUNXI_SOC_SUN5I = 0x1625, /* A13, A10s */ SUNXI_SOC_SUN6I = 0x1633, /* A31 */ SUNXI_SOC_SUN7I = 0x1651, /* A20 */ SUNXI_SOC_SUN8I = 0x1650, /* A23 */ SUNXI_SOC_SUN9I = 0x1667, /* A33 */ SUNXI_SOC_SUN10I = 0x1635, /* A80 */ }; static enum sunxi_soc_version soc_version; /* * Libv's favourite register handling calls. */ unsigned int sunxi_io_read(void *base, int offset) { return inl((unsigned long) (base + offset)); } void sunxi_io_write(void *base, int offset, unsigned int value) { outl(value, (unsigned long) (base + offset)); } void sunxi_io_mask(void *base, int offset, unsigned int value, unsigned int mask) { unsigned int tmp = inl((unsigned long) (base + offset)); tmp &= ~mask; tmp |= value & mask; outl(tmp, (unsigned long) (base + offset)); } /* * Find out exactly which SoC we are dealing with. */ #define SUNXI_IO_SRAM_BASE 0x01C00000 #define SUNXI_IO_SRAM_SIZE 0x00001000 #define SUNXI_IO_SRAM_VERSION 0x24 static int soc_version_read(void) { void *base; unsigned int restore; base = mmap(NULL, SUNXI_IO_SRAM_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, devmem_fd, SUNXI_IO_SRAM_BASE); if (base == MAP_FAILED) { fprintf(stderr, "Failed to map sram registers: %s\n", strerror(errno)); return errno; } restore = sunxi_io_read(base, SUNXI_IO_SRAM_VERSION); sunxi_io_mask(base, SUNXI_IO_SRAM_VERSION, 0x8000, 0x8000); soc_version = sunxi_io_read(base, SUNXI_IO_SRAM_VERSION) >> 16; sunxi_io_mask(base, SUNXI_IO_SRAM_VERSION, restore, 0x8000); munmap(base, SUNXI_IO_SRAM_SIZE); return 0; } /* * Read DRAM clock. */ #define SUNXI_IO_CCM_BASE 0x01C20000 #define SUNXI_IO_CCM_SIZE 0x00001000 #define SUNXI_IO_CCM_PLL5_CFG 0x20 static int sunxi_dram_clock_read(unsigned int *clock) { void *base; unsigned int tmp; int n, k, m; base = mmap(NULL, SUNXI_IO_CCM_SIZE, PROT_READ, MAP_SHARED, devmem_fd, SUNXI_IO_CCM_BASE); if (base == MAP_FAILED) { fprintf(stderr, "Failed to map ccm registers: %s\n", strerror(errno)); return errno; } tmp = sunxi_io_read(base, SUNXI_IO_CCM_PLL5_CFG); munmap(base, SUNXI_IO_CCM_SIZE); n = (tmp >> 8) & 0x1F; k = ((tmp >> 4) & 0x03) + 1; m = (tmp & 0x03) + 1; switch (soc_version) { case SUNXI_SOC_SUN6I: case SUNXI_SOC_SUN8I: n++; break; default: break; } *clock = (24 * n * k) / m; return 0; } struct regs { int offset; char *name; }; static int dram_registers_print(unsigned int address, int size, const struct regs *regs, const char *description, const char *prefix) { void *base; int i, j; base = mmap(NULL, size, PROT_READ, MAP_SHARED, devmem_fd, address); if (base == MAP_FAILED) { fprintf(stderr, "Failed to map %s registers: %s\n", description, strerror(errno)); return errno; } printf("/*\n"); printf(" * %s Registers\n", description); printf(" */\n"); for (i = 0; i < size; i += 4) { unsigned int reg = sunxi_io_read(base, i); for (j = 0; regs[j].name; j++) if (i == regs[j].offset) { printf("%s = 0x%08x;\n", regs[j].name, reg); } if (reg && !regs[j].name) printf("%s_%03X = 0x%08x;\n", prefix, i, reg); } printf("\n"); munmap(base, size); return 0; } static int dram_register_range_print(unsigned int address, int size, const char *description, const char *prefix) { void *base; int i; base = mmap(NULL, size, PROT_READ, MAP_SHARED, devmem_fd, address); if (base == MAP_FAILED) { fprintf(stderr, "Failed to map %s registers: %s\n", description, strerror(errno)); return errno; } printf("/*\n"); printf(" * %s Registers\n", description); printf(" */\n"); for (i = 0; i < size; i += 4) { unsigned int reg = sunxi_io_read(base, i); if (reg) printf("%s_%03X = 0x%08x;\n", prefix, i, reg); } printf("\n"); munmap(base, size); return 0; } /* * Read DRAM parameters. */ #define SUN4I_IO_DRAM_BASE 0x01C01000 #define SUN4I_IO_DRAM_SIZE 0x00001000 #define SUN4I_IO_DRAM_CCR 0x000 /* controller configuration register */ #define SUN4I_IO_DRAM_DCR 0x004 /* dram configuration */ #define SUN4I_IO_DRAM_IOCR 0x008 /* i/o configuration */ #define SUN4I_IO_DRAM_TPR0 0x014 /* dram timing parameters register 0 */ #define SUN4I_IO_DRAM_TPR1 0x018 /* dram timing parameters register 1 */ #define SUN4I_IO_DRAM_TPR2 0x01C /* dram timing parameters register 2 */ #define SUN4I_IO_DRAM_ZQCR0 0x0A8 /* zq control register 0 */ #define SUN4I_IO_DRAM_ZQCR1 0x0AC /* zq control register 1 */ #define SUN4I_IO_DRAM_MR 0x1F0 /* mode register */ #define SUN4I_IO_DRAM_EMR 0x1F4 /* extended mode register */ #define SUN4I_IO_DRAM_EMR2 0x1F8 /* extended mode register */ #define SUN4I_IO_DRAM_EMR3 0x1FC /* extended mode register */ #define SUN4I_IO_DRAM_DLLCR0 0x204 /* dll control register 0(byte 0) */ #define SUN4I_IO_DRAM_DLLCR1 0x208 /* dll control register 1(byte 1) */ #define SUN4I_IO_DRAM_DLLCR2 0x20C /* dll control register 2(byte 2) */ #define SUN4I_IO_DRAM_DLLCR3 0x210 /* dll control register 3(byte 3) */ #define SUN4I_IO_DRAM_DLLCR4 0x214 /* dll control register 4(byte 4) */ static int sun4i_dram_parameters_read(struct sun4i_dram_para *dram_para) { void *base; unsigned int zqcr0, dcr; unsigned int dllcr0, dllcr1, dllcr2, dllcr3, dllcr4; base = mmap(NULL, SUN4I_IO_DRAM_SIZE, PROT_READ, MAP_SHARED, devmem_fd, SUN4I_IO_DRAM_BASE); if (base == MAP_FAILED) { fprintf(stderr, "Failed to map dram registers: %s\n", strerror(errno)); return errno; } dram_para->tpr0 = sunxi_io_read(base, SUN4I_IO_DRAM_TPR0); dram_para->tpr1 = sunxi_io_read(base, SUN4I_IO_DRAM_TPR1); dram_para->tpr2 = sunxi_io_read(base, SUN4I_IO_DRAM_TPR2); dllcr0 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR0) >> 6) & 0x3F; dllcr1 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR1) >> 14) & 0x0F; dllcr2 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR2) >> 14) & 0x0F; dllcr3 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR3) >> 14) & 0x0F; dllcr4 = (sunxi_io_read(base, SUN4I_IO_DRAM_DLLCR4) >> 14) & 0x0F; dram_para->tpr3 = (dllcr0 << 16) | (dllcr4 << 12) | (dllcr3 << 8) | (dllcr2 << 4) | dllcr1; if (soc_version == SUNXI_SOC_SUN7I) { if (sunxi_io_read(base, SUN4I_IO_DRAM_CCR) & 0x20) dram_para->tpr4 |= 0x01; if (!(sunxi_io_read(base, SUN4I_IO_DRAM_ZQCR1) & 0x01000000)) dram_para->tpr4 |= 0x02; } dram_para->cas = (sunxi_io_read(base, SUN4I_IO_DRAM_MR) >> 4) & 0x0F; dram_para->emr1 = sunxi_io_read(base, SUN4I_IO_DRAM_EMR); dram_para->emr2 = sunxi_io_read(base, SUN4I_IO_DRAM_EMR2); dram_para->emr3 = sunxi_io_read(base, SUN4I_IO_DRAM_EMR3); dram_para->odt_en = sunxi_io_read(base, SUN4I_IO_DRAM_IOCR) & 0x03; zqcr0 = sunxi_io_read(base, SUN4I_IO_DRAM_ZQCR0); dram_para->zq = (zqcr0 & 0xf0000000) | ((zqcr0 >> 20) & 0xff) | ((zqcr0 & 0xfffff) << 8); dcr = sunxi_io_read(base, SUN4I_IO_DRAM_DCR); if (dcr & 0x01) { dram_para->cas += 4; dram_para->type = 3; } else dram_para->type = 2; dram_para->density = (1 << ((dcr >> 3) & 0x07)) * 256; dram_para->rank_num = ((dcr >> 10) & 0x03) + 1; dram_para->io_width = ((dcr >> 1) & 0x03) * 8; dram_para->bus_width = (((dcr >> 6) & 3) + 1) * 8; munmap(base, SUN4I_IO_DRAM_SIZE); return 0; } /* * Print a dram.c that can be stuck immediately into u-boot. */ void sun4i_dram_para_print_uboot(struct sun4i_dram_para *dram_para) { printf("// place this file in board/sunxi/ in u-boot\n"); printf("/* this file is generated, don't edit it yourself */\n"); printf("\n"); printf("#include \"common.h\"\n"); printf("#include \n"); printf("\n"); printf("static struct dram_para dram_para = {\n"); printf("\t.clock = %d,\n", dram_para->clock); printf("\t.type = %d,\n", dram_para->type); printf("\t.rank_num = %d,\n", dram_para->rank_num); printf("\t.density = %d,\n", dram_para->density); printf("\t.io_width = %d,\n", dram_para->io_width); printf("\t.bus_width = %d,\n", dram_para->bus_width); printf("\t.cas = %d,\n", dram_para->cas); printf("\t.zq = 0x%02x,\n", dram_para->zq); printf("\t.odt_en = %d,\n", dram_para->odt_en); printf("\t.size = !!! FIXME !!!, /* in MiB */\n"); printf("\t.tpr0 = 0x%08x,\n", dram_para->tpr0); printf("\t.tpr1 = 0x%04x,\n", dram_para->tpr1); printf("\t.tpr2 = 0x%05x,\n", dram_para->tpr2); printf("\t.tpr3 = 0x%02x,\n", dram_para->tpr3); printf("\t.tpr4 = 0x%02x,\n", dram_para->tpr4); printf("\t.tpr5 = 0x%02x,\n", dram_para->tpr5); printf("\t.emr1 = 0x%02x,\n", dram_para->emr1); printf("\t.emr2 = 0x%02x,\n", dram_para->emr2); printf("\t.emr3 = 0x%02x,\n", dram_para->emr3); printf("};\n"); printf("\n"); printf("unsigned long sunxi_dram_init(void)\n"); printf("{\n"); printf("\treturn dramc_init(&dram_para);\n"); printf("}\n"); } /* * Print output matching the .fex output, so it can be stuck in a * fex file directly. */ void sun4i_dram_para_print_fex(struct sun4i_dram_para *dram_para) { printf("; Insert this section into your .fex file\n"); printf("[dram_para]\n"); printf("dram_baseaddr = 0x40000000\n"); printf("dram_clk = %d\n", dram_para->clock); printf("dram_type = %d\n", dram_para->type); printf("dram_rank_num = %d\n", dram_para->rank_num); printf("dram_chip_density = %d\n", dram_para->density); printf("dram_io_width = %d\n", dram_para->io_width); printf("dram_bus_width = %d\n", dram_para->bus_width); printf("dram_cas = %d\n", dram_para->cas); printf("dram_zq = 0x%02x\n", dram_para->zq); printf("dram_odt_en = %d\n", dram_para->odt_en); printf("dram_size = !!! FIXME !!!\n"); printf("dram_tpr0 = 0x%08x\n", dram_para->tpr0); printf("dram_tpr1 = 0x%04x\n", dram_para->tpr1); printf("dram_tpr2 = 0x%05x\n", dram_para->tpr2); printf("dram_tpr3 = 0x%02x\n", dram_para->tpr3); printf("dram_tpr4 = 0x%02x\n", dram_para->tpr4); printf("dram_tpr5 = 0x%02x\n", dram_para->tpr5); printf("dram_emr1 = 0x%02x\n", dram_para->emr1); printf("dram_emr2 = 0x%02x\n", dram_para->emr2); printf("dram_emr3 = 0x%02x\n", dram_para->emr3); } static int sun4i_dram_para_print(bool uboot) { struct sun4i_dram_para dram_para = {0}; int ret; ret = sunxi_dram_clock_read(&dram_para.clock); if (ret) return ret; ret = sun4i_dram_parameters_read(&dram_para); if (ret) return ret; if (uboot) sun4i_dram_para_print_uboot(&dram_para); else sun4i_dram_para_print_fex(&dram_para); return 0; } /* * */ #define SUN6I_IO_DRAMCOM_BASE 0x01C62000 #define SUN6I_IO_DRAMCOM_SIZE 0x0300 #define SUN6I_IO_DRAMCTL_BASE 0x01C63000 #define SUN6I_IO_DRAMCTL_SIZE 0x0400 #define SUN6I_IO_DRAMPHY_BASE 0x01C65000 #define SUN6I_IO_DRAMPHY_SIZE 0x0400 static struct regs sun6i_dramcom_regs[] = { {0x00, "SDR_COM_CR"}, {0x04, "SDR_COM_CCR"}, {0x10, "SDR_COM_MFACR"}, {0x30, "SDR_COM_MSACR"}, {0x50, "SDR_COM_MBACR"}, {0, NULL} }; static struct regs sun6i_dramctl_regs[] = { {0x004, "SDR_SCTL"}, {0x008, "SDR_SSTAT"}, {0x040, "SDR_MCMD"}, {0x04c, "SDR_CMDSTAT"}, {0x050, "SDR_CMDSTATEN"}, {0x060, "SDR_MRRCFG0"}, {0x064, "SDR_MRRSTAT0"}, {0x068, "SDR_MRRSTAT1"}, {0x07c, "SDR_MCFG1"}, {0x080, "SDR_MCFG"}, {0x084, "SDR_PPCFG"}, {0x088, "SDR_MSTAT"}, {0x08c, "SDR_LP2ZQCFG"}, {0x094, "SDR_DTUSTAT"}, {0x098, "SDR_DTUNA"}, {0x09c, "SDR_DTUNE"}, {0x0a0, "SDR_DTUPRD0"}, {0x0a4, "SDR_DTUPRD1"}, {0x0a8, "SDR_DTUPRD2"}, {0x0ac, "SDR_DTUPRD3"}, {0x0b0, "SDR_DTUAWDT"}, {0x0c0, "SDR_TOGCNT1U"}, {0x0cc, "SDR_TOGCNT100N"}, {0x0d0, "SDR_TREFI"}, {0x0d4, "SDR_TMRD"}, {0x0d8, "SDR_TRFC"}, {0x0dc, "SDR_TRP"}, {0x0e0, "SDR_TRTW"}, {0x0e4, "SDR_TAL"}, {0x0e8, "SDR_TCL"}, {0x0ec, "SDR_TCWL"}, {0x0f0, "SDR_TRAS"}, {0x0f4, "SDR_TRC"}, {0x0f8, "SDR_TRCD"}, {0x0fc, "SDR_TRRD"}, {0x100, "SDR_TRTP"}, {0x104, "SDR_TWR"}, {0x108, "SDR_TWTR"}, {0x10c, "SDR_TEXSR"}, {0x110, "SDR_TXP"}, {0x114, "SDR_TXPDLL"}, {0x118, "SDR_TZQCS"}, {0x11c, "SDR_TZQCSI"}, {0x120, "SDR_TDQS"}, {0x124, "SDR_TCKSRE"}, {0x128, "SDR_TCKSRX"}, {0x12c, "SDR_TCKE"}, {0x130, "SDR_TMOD"}, {0x134, "SDR_TRSTL"}, {0x138, "SDR_TZQCL"}, {0x13c, "SDR_TMRR"}, {0x140, "SDR_TCKESR"}, {0x144, "SDR_TDPD"}, {0x200, "SDR_DTUWACTL"}, {0x204, "SDR_DTURACTL"}, {0x208, "SDR_DTUCFG"}, {0x20c, "SDR_DTUECTL"}, {0x210, "SDR_DTUWD0"}, {0x214, "SDR_DTUWD1"}, {0x218, "SDR_DTUWD2"}, {0x21c, "SDR_DTUWD3"}, {0x220, "SDR_DTUWDM"}, {0x224, "SDR_DTURD0"}, {0x224, "SDR_DTURD1"}, {0x22c, "SDR_DTURD2"}, {0x230, "SDR_DTURD3"}, {0x234, "SDR_DTULFSRWD"}, {0x238, "SDR_DTULFSRRD"}, {0x23c, "SDR_DTUEAF"}, {0x240, "SDR_DFITCTLDLY"}, {0x244, "SDR_DFIODTCFG"}, {0x248, "SDR_DFIODTCFG1"}, {0x24c, "SDR_DFIODTRMAP"}, {0x250, "SDR_DFITPHYWRD"}, {0x254, "SDR_DFITPHYWRL"}, {0x260, "SDR_DFITRDDEN"}, {0x264, "SDR_DFITPHYRDL"}, {0x270, "SDR_DFITPHYUPDTYPE0"}, {0x274, "SDR_DFITPHYUPDTYPE1"}, {0x278, "SDR_DFITPHYUPDTYPE2"}, {0x27c, "SDR_DFITPHYUPDTYPE3"}, {0x280, "SDR_DFITCTRLUPDMIN"}, {0x284, "SDR_DFITCTRLUPDMAX"}, {0x288, "SDR_DFITCTRLUPDDLY"}, {0x290, "SDR_DFIUPDCFG"}, {0x294, "SDR_DFITREFMSKI"}, {0x298, "SDR_DFITCRLUPDI"}, {0x2ac, "SDR_DFITRCFG0"}, {0x2b0, "SDR_DFITRSTAT0"}, {0x2b4, "SDR_DFITRWRLVLEN"}, {0x2b8, "SDR_DFITRRDLVLEN"}, {0x2bc, "SDR_DFITRRDLVLGATEEN"}, {0x2c4, "SDR_DFISTCFG0"}, {0x2c8, "SDR_DFISTCFG1"}, {0x2d0, "SDR_DFITDRAMCLKEN"}, {0x2d4, "SDR_DFITDRAMCLKDIS"}, {0x2f0, "SDR_DFILPCFG0"}, {0, NULL} }; static struct regs sun6i_dramphy_regs[] = { {0x004, "SDR_PIR"}, {0x008, "SDR_PGCR"}, {0x00c, "SDR_PGSR"}, {0x010, "SDR_DLLGCR"}, {0x014, "SDR_ACDLLCR"}, {0x018, "SDR_PTR0"}, {0x01c, "SDR_PTR1"}, {0x020, "SDR_PTR2"}, {0x024, "SDR_ACIOCR"}, {0x028, "SDR_DXCCR"}, {0x02c, "SDR_DSGCR"}, {0x030, "SDR_DCR"}, {0x034, "SDR_DTPR0"}, {0x038, "SDR_DTPR1"}, {0x03c, "SDR_DTPR2"}, {0x040, "SDR_MR0"}, {0x044, "SDR_MR1"}, {0x048, "SDR_MR2"}, {0x04c, "SDR_MR3"}, {0x050, "SDR_ODTCR"}, {0x054, "SDR_DTAR"}, {0x058, "SDR_DTDT0"}, {0x05c, "SDR_DTDT1"}, {0x0c0, "SDR_DCUAR"}, {0x0c4, "SDR_DCUDR"}, {0x0c8, "SDR_DCURR"}, {0x0cc, "SDR_DCULR"}, {0x0d0, "SDR_DCUGCR"}, {0x0d4, "SDR_DCUTPR"}, {0x0d8, "SDR_DCUSR0"}, {0x0dc, "SDR_DCUSR1"}, {0x100, "SDR_BISTRR"}, {0x104, "SDR_BISTMSKR0"}, {0x108, "SDR_BISTMSKR1"}, {0x10c, "SDR_BISTWCR"}, {0x110, "SDR_BISTLSR"}, {0x114, "SDR_BISTAR0"}, {0x118, "SDR_BISTAR1"}, {0x11c, "SDR_BISTAR2"}, {0x120, "SDR_BISTUDPR"}, {0x124, "SDR_BISTGSR"}, {0x128, "SDR_BISTWER"}, {0x12c, "SDR_BISTBER0"}, {0x130, "SDR_BISTBER1"}, {0x134, "SDR_BISTBER2"}, {0x138, "SDR_BISTWCSR"}, {0x13c, "SDR_BISTFWR0"}, {0x140, "SDR_BISTFWR1"}, {0x180, "SDR_ZQ0CR0"}, {0x184, "SDR_ZQ0CR1"}, {0x188, "SDR_ZQ0SR0"}, {0x18c, "SDR_ZQ0SR1"}, {0x1c0, "SDR_DX0GCR"}, {0x1c4, "SDR_DX0GSR0"}, {0x1c8, "SDR_DX0GSR1"}, {0x1cc, "SDR_DX0DLLCR"}, {0x1d0, "SDR_DX0DQTR"}, {0x1d4, "SDR_DX0DQSTR"}, {0x200, "SDR_DX1GCR"}, {0x204, "SDR_DX1GSR0"}, {0x208, "SDR_DX1GSR1"}, {0x20c, "SDR_DX1DLLCR"}, {0x210, "SDR_DX1DQTR"}, {0x214, "SDR_DX1DQSTR"}, {0x240, "SDR_DX2GCR"}, {0x244, "SDR_DX2GSR0"}, {0x248, "SDR_DX2GSR1"}, {0x24c, "SDR_DX2DLLCR"}, {0x250, "SDR_DX2DQTR"}, {0x254, "SDR_DX2DQSTR"}, {0x280, "SDR_DX3GCR"}, {0x284, "SDR_DX3GSR0"}, {0x288, "SDR_DX3GSR1"}, {0x28c, "SDR_DX3DLLCR"}, {0x290, "SDR_DX3DQTR"}, {0x294, "SDR_DX3DQSTR"}, {0, NULL} }; static int sun6i_dram_regs_print(void) { unsigned int clock; int ret; ret = sunxi_dram_clock_read(&clock); if (ret) return ret; printf("DRAM Clock: %dMHz\n", clock); ret = dram_registers_print(SUN6I_IO_DRAMCOM_BASE, SUN6I_IO_DRAMCOM_SIZE, &sun6i_dramcom_regs[0], "DRAM COM", "SDR_COM"); if (ret) return ret; ret = dram_registers_print(SUN6I_IO_DRAMCTL_BASE, SUN6I_IO_DRAMCTL_SIZE, &sun6i_dramctl_regs[0], "DRAM CTL", "SDR_CTL"); if (ret) return ret; ret = dram_registers_print(SUN6I_IO_DRAMPHY_BASE, SUN6I_IO_DRAMPHY_SIZE, &sun6i_dramphy_regs[0], "DRAM PHY", "SDR_PHY"); if (ret) return ret; return 0; } /* * */ static int sun8i_dram_regs_print(void) { unsigned int clock; int ret; ret = sunxi_dram_clock_read(&clock); if (ret) return ret; printf("DRAM Clock: %dMHz\n", clock); ret = dram_register_range_print(SUN6I_IO_DRAMCOM_BASE, SUN6I_IO_DRAMCOM_SIZE, "DRAM COM", "SDR_COM"); if (ret) return ret; ret = dram_register_range_print(SUN6I_IO_DRAMCTL_BASE, SUN6I_IO_DRAMCTL_SIZE, "DRAM CTL", "SDR_CTL"); if (ret) return ret; ret = dram_register_range_print(SUN6I_IO_DRAMPHY_BASE, SUN6I_IO_DRAMPHY_SIZE, "DRAM PHY", "SDR_PHY"); if (ret) return ret; return 0; } static void print_usage(const char *name) { printf("Utility to retrieve DRAM information from registers on " "Allwinner SoCs.\n"); printf("\n"); printf("This is part of the sunxi-tools package from the sunxi " "project. "); printf("For more \ninformation visit " "http://linux-sunxi.org/Sunxi-tools.\n"); printf("\n"); printf("Usage: %s [OPTION]\n", name); printf("\n"); printf("Options:\n"); printf(" -f: print in FEX format (default).\n"); printf(" -u: print in sunxi U-Boot dram.c file format.\n"); printf(" -h: print this usage information.\n"); } int main(int argc, char *argv[]) { bool uboot; int ret; if (argc == 2) { if (argv[1][0] == '-') { if (argv[1][1] == 'f') uboot = false; else if (argv[1][1] == 'u') uboot = true; else if (argv[1][1] == 'h') goto help; else if ((argv[1][1] == '-') && (argv[1][2] == 'h')) goto help; else goto usage; if (argv[1][2] != 0) goto usage; } else goto usage; } else if (argc == 1) uboot = false; else goto usage; devmem_fd = open(DEVMEM_FILE, O_RDWR); if (devmem_fd == -1) { fprintf(stderr, "Error: failed to open %s: %s\n", DEVMEM_FILE, strerror(errno)); return errno; } ret = soc_version_read(); if (ret) return ret; switch (soc_version) { case SUNXI_SOC_SUN4I: case SUNXI_SOC_SUN5I: case SUNXI_SOC_SUN7I: return sun4i_dram_para_print(uboot); case SUNXI_SOC_SUN6I: return sun6i_dram_regs_print(); case SUNXI_SOC_SUN8I: return sun8i_dram_regs_print(); default: fprintf(stderr, "Error: unknown or unhandled Soc: 0x%04X\n", soc_version); return -1; } usage: fprintf(stderr, "Error: wrong argument(s).\n"); print_usage(argv[0]); return EINVAL; help: print_usage(argv[0]); return 0; } sunxi-tools-1.3/nand-common.h000066400000000000000000000021671262340046500162500ustar00rootroot00000000000000/* * (C) Copyright 2013 * Patrick H Wood, All rights reserved. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * */ #include "types.h" extern int nand_part_a10 (int argc, char **argv, const char *cmd, int fd, int force); extern int nand_part_a20 (int argc, char **argv, const char *cmd, int fd, int force); extern int checkmbrs_a10 (int fd); extern int checkmbrs_a20 (int fd); extern void usage (const char *cmd); extern __u32 calc_crc32(void * buffer, __u32 length); sunxi-tools-1.3/nand-part-a10.h000066400000000000000000000051341262340046500163020ustar00rootroot00000000000000/* * drivers/block/sun4i_nand/nfd/mbr.h * * (C) Copyright 2007-2012 * Allwinner Technology Co., Ltd. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #ifndef __MBR_H__ #define __MBR_H__ #include "types.h" #define MBR_MAGIC "softw311" #define MBR_VERSION 0x100 #define nand_part nand_part_a10 #define checkmbrs checkmbrs_a10 #define MAX_PART_COUNT 15 //max part count #define MBR_COPY_NUM 4 //mbr backup count #define MBR_START_ADDRESS 0x0 //mbr start address #define MBR_SIZE 1024 //mbr size #define MBR_RESERVED (MBR_SIZE - 20 - (MAX_PART_COUNT * 64)) //mbr reserved space // extern struct __NandDriverGlobal_t NandDriverInfo; // extern struct __NandStorageInfo_t NandStorageInfo; #define DiskSize (SECTOR_CNT_OF_SINGLE_PAGE * PAGE_CNT_OF_PHY_BLK * BLOCK_CNT_OF_DIE * \ DIE_CNT_OF_CHIP * NandStorageInfo.ChipCnt / 1024 * DATA_BLK_CNT_OF_ZONE) struct nand_disk{ unsigned long size; unsigned long offset; unsigned char type; }; /* part info */ typedef struct tag_PARTITION{ __u32 addrhi; //start address high 32 bit __u32 addrlo; //start address low 32 bit __u32 lenhi; //size high 32 bit __u32 lenlo; //size low 32 bit __u8 classname[12]; //major device name __u8 name[12]; //minor device name unsigned int user_type; //标志当å‰ç›˜ç¬¦æ‰€å±žäºŽçš„用户 unsigned int ro; //标志当å‰ç›˜ç¬¦çš„读写属性 __u8 res[16]; //reserved }PARTITION; /* mbr info */ typedef struct tag_MBR{ __u32 crc32; // crc, from byte 4 to mbr tail __u32 version; // version __u8 magic[8]; // magic number __u8 copy; // mbr backup count __u8 index; // current part no __u16 PartCount; // part counter PARTITION array[MAX_PART_COUNT];// part info __u8 res[MBR_RESERVED]; // reserved space }MBR; int mbr2disks(struct nand_disk* disk_array); #endif //__MBR_H__ sunxi-tools-1.3/nand-part-a20.h000066400000000000000000000064351262340046500163100ustar00rootroot00000000000000/* * drivers/block/sun4i_nand/nfd/mbr.h * * (C) Copyright 2007-2012 * Allwinner Technology Co., Ltd. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #ifndef __MBR_H__ #define __MBR_H__ #include "types.h" #define MBR_MAGIC "softw411" #define MBR_VERSION 0x200 #define nand_part nand_part_a20 #define checkmbrs checkmbrs_a20 #define MAX_PART_COUNT 120 //max part count #define MBR_COPY_NUM 4 //mbr backup count #define MBR_START_ADDRESS 0x0 //mbr start address #define MBR_SIZE 1024*16 //mbr size #define MBR_RESERVED (MBR_SIZE - 32 - (MAX_PART_COUNT * 128)) //mbr reserved space // extern struct __NandDriverGlobal_t NandDriverInfo; // extern struct __NandStorageInfo_t NandStorageInfo; #define DiskSize (SECTOR_CNT_OF_SINGLE_PAGE * PAGE_CNT_OF_PHY_BLK * BLOCK_CNT_OF_DIE * \ DIE_CNT_OF_CHIP * NandStorageInfo.ChipCnt / 1024 * DATA_BLK_CNT_OF_ZONE) struct nand_disk{ unsigned long size; unsigned long offset; unsigned char type; }; /* part info */ typedef struct nand_tag_PARTITION{ unsigned int addrhi; //起始地å€, 以扇区为å•ä½ unsigned int addrlo; // unsigned int lenhi; //长度 unsigned int lenlo; // unsigned char classname[16]; //次设备å unsigned char name[16]; //主设备å unsigned int user_type; //用户类型 unsigned int keydata; //关键数æ®ï¼Œè¦æ±‚é‡äº§ä¸ä¸¢å¤± unsigned int ro; //读写属性 unsigned char res[68]; //ä¿ç•™æ•°æ®ï¼ŒåŒ¹é…分区信æ¯128字节 }__attribute__ ((packed))PARTITION; /* mbr info */ typedef struct nand_tag_MBR{ unsigned int crc32; // crc 1k - 4 unsigned int version; // 版本信æ¯ï¼Œ 0x00000100 unsigned char magic[8]; //"softw411" unsigned int copy; //分数 unsigned int index; //第几个MBR备份 unsigned int PartCount; //分区个数 unsigned int stamp[1]; //å¯¹é½ PARTITION array[MAX_PART_COUNT]; // unsigned char res[MBR_RESERVED]; }__attribute__ ((packed)) MBR; int mbr2disks(struct nand_disk* disk_array); #endif //__MBR_H__ sunxi-tools-1.3/nand-part-main.c000066400000000000000000000055161262340046500166440ustar00rootroot00000000000000/* * (C) Copyright 2013 * Patrick H Wood, All rights reserved. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * */ #include #include #include #include #include "nand-common.h" void usage(const char *cmd) { printf("usage: %s [-f a10|a20] nand-device\n", cmd); printf(" %s nand-device 'name2 len2 [usertype2]' ['name3 len3 [usertype3]'] ...\n", cmd); printf(" %s [-f a10|a20] nand-device start1 'name1 len1 [usertype1]' ['name2 len2 [usertype2]'] ...\n", cmd); } typedef struct tag_CRC32_DATA { __u32 CRC; //intçš„å¤§å°æ˜¯32ä½ __u32 CRC_32_Tbl[256]; //用æ¥ä¿å­˜ç è¡¨ }CRC32_DATA_t; __u32 calc_crc32(void * buffer, __u32 length) { __u32 i, j; CRC32_DATA_t crc32; // __u32 CRC32 = 0xffffffff; //设置åˆå§‹å€¼ crc32.CRC = 0; for( i = 0; i < 256; ++i)//用++i以æé«˜æ•ˆçއ { crc32.CRC = i; for( j = 0; j < 8 ; ++j) { //这个循环实际上就是用"计算法"æ¥æ±‚å–CRCçš„æ ¡éªŒç  if(crc32.CRC & 1) crc32.CRC = (crc32.CRC >> 1) ^ 0xEDB88320; else //0xEDB88320就是CRC-32多项表达å¼çš„值 crc32.CRC >>= 1; } crc32.CRC_32_Tbl[i] = crc32.CRC; } CRC32 = 0xffffffff; //设置åˆå§‹å€¼ for( i = 0; i < length; ++i) { CRC32 = crc32.CRC_32_Tbl[(CRC32^((unsigned char*)buffer)[i]) & 0xff] ^ (CRC32>>8); } //return CRC32; return CRC32^0xffffffff; } int main (int argc, char **argv) { char *nand = "/dev/nand"; const char *cmd = argv[0]; int fd; int force = 0; // force write even if magics and CRCs don't match argc--; argv++; if (argc > 1) { if (!strcmp(argv[0], "-f")) { if (!strcasecmp(argv[1], "a10")) force = 10; else if (!strcasecmp(argv[1], "a20")) force = 20; else { usage(cmd); return -1; } argc -= 2; argv += 2; } } if (argc > 0) { nand = argv[0]; argc--; argv++; } fd = open(nand, O_RDWR); if (fd < 0) { usage(cmd); return -2; } if (force == 10) return nand_part_a10 (argc, argv, cmd, fd, force); if (force == 20) return nand_part_a20 (argc, argv, cmd, fd, force); if (checkmbrs_a10(fd)) return nand_part_a10 (argc, argv, cmd, fd, force); if (checkmbrs_a20(fd)) return nand_part_a20 (argc, argv, cmd, fd, force); } sunxi-tools-1.3/nand-part.c000066400000000000000000000176361262340046500157300ustar00rootroot00000000000000/* * mbr.c * (C) Copyright 2012 * Patrick H Wood, All rights reserved. * Heavily modified from the Allwinner file drivers/block/sun4i_nand/nfd/mbr.c. * (Allwinner copyright block retained below.) * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * */ /* * drivers/block/sun4i_nand/nfd/mbr.c * (C) Copyright 2007-2012 * Allwinner Technology Co., Ltd. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include /* BLKRRPART */ #include "nand-common.h" // so far, only known formats are for A10 and A20 #if defined(A10) # include "nand-part-a10.h" #elif defined(A20) # include "nand-part-a20.h" #endif #define MAX_NAME 16 static void printmbrheader(MBR *mbr) { printf("mbr: version 0x%08x, magic %8.8s\n", mbr->version, mbr->magic); } static MBR *_get_mbr(int fd, int mbr_num, int force) { MBR *mbr; /*request mbr space*/ mbr = malloc(sizeof(MBR)); if(mbr == NULL) { printf("%s : request memory fail\n",__FUNCTION__); return NULL; } /*get mbr from nand device*/ lseek(fd,MBR_START_ADDRESS + MBR_SIZE*mbr_num,SEEK_SET); if(read(fd,mbr,MBR_SIZE) == MBR_SIZE) { /*checksum*/ printf("check partition table copy %d: ", mbr_num); printmbrheader(mbr); if (force) { strncpy((char *)mbr->magic, MBR_MAGIC, 8); mbr->version = MBR_VERSION; return mbr; } if(strncmp((char *)mbr->magic, MBR_MAGIC, 8)) { printf("magic %8.8s is not %8s\n", mbr->magic, MBR_MAGIC); return NULL; } if(mbr->version != MBR_VERSION) { printf("version 0x%08x is not 0x%08x\n", mbr->version, MBR_VERSION); return NULL; } if(*(__u32 *)mbr == calc_crc32((__u32 *)mbr + 1,MBR_SIZE - 4)) { printf("OK\n"); return mbr; } printf("BAD!\n"); } return NULL; } static __s32 _free_mbr(MBR *mbr) { if(mbr) { free(mbr); mbr = 0; } return 0; } static void printmbr(MBR *mbr) { unsigned int part_cnt; printmbrheader(mbr); printf("%d partitions\n", mbr->PartCount); for(part_cnt = 0; part_cnt < mbr->PartCount && part_cnt < MAX_PART_COUNT; part_cnt++) { printf("partition %2d: class = %12s, name = %12s, partition start = %8d, partition size = %8d user_type=%d\n", part_cnt + 1, mbr->array[part_cnt].classname, mbr->array[part_cnt].name, mbr->array[part_cnt].addrlo, mbr->array[part_cnt].lenlo, mbr->array[part_cnt].user_type); } } int checkmbrs(int fd) { int i; MBR *mbrs[MBR_COPY_NUM]; MBR *mbr = NULL; memset((void *) mbrs, 0, sizeof(mbrs)); for (i = 0; i < MBR_COPY_NUM; i++) { mbrs[i] = _get_mbr(fd, i, 0); if (mbrs[i]) mbr = mbrs[i]; } if (!mbr) { printf("all partition tables are bad!\n"); for (i = 0; i < MBR_COPY_NUM; i++) { if (mbrs[i]) _free_mbr(mbrs[i]); } return 0; } printmbr(mbr); for (i = 0; i < MBR_COPY_NUM; i++) { if (mbrs[i]) _free_mbr(mbrs[i]); } return 1; } static int writembrs(int fd, char names[][MAX_NAME], __u32 start, __u32 *lens, unsigned int *user_types, int nparts, int partoffset, int force) { unsigned int part_cnt = 0; int i; char yn = 'n'; MBR *mbrs[MBR_COPY_NUM]; MBR *mbr = NULL; FILE *backup; memset((void *) mbrs, 0, sizeof(mbrs)); for (i = 0; i < MBR_COPY_NUM; i++) { mbrs[i] = _get_mbr(fd, i, force); if (mbrs[i]) mbr = mbrs[i]; } if (!mbr) { printf("all partition tables are bad!\n"); for (i = 0; i < MBR_COPY_NUM; i++) { if (mbrs[i]) _free_mbr(mbrs[i]); } return 0; } // back up mbr data backup = fopen("nand_mbr.backup", "w"); if (!backup) { printf("can't open nand_mbr.backup to back up mbr data\n"); for (i = 0; i < MBR_COPY_NUM; i++) { if (mbrs[i]) _free_mbr(mbrs[i]); } return 0; } fprintf(backup, "%d ", mbr->array[0].addrlo); for(part_cnt = 0; part_cnt < mbr->PartCount && part_cnt < MAX_PART_COUNT; part_cnt++) { fprintf(backup, "'%s %d %d' ", mbr->array[part_cnt].name, mbr->array[part_cnt].lenlo, mbr->array[part_cnt].user_type); } fprintf(backup, "\n"); fclose(backup); mbr->PartCount = nparts + partoffset; if (partoffset) start = mbr->array[0].addrlo + mbr->array[0].lenlo; for(i = 0; i < nparts; i++) { strcpy((char *)mbr->array[i+partoffset].name, names[i]); strcpy((char *)mbr->array[i+partoffset].classname, "DISK"); memset((void *) mbr->array[i+partoffset].res, 0, sizeof(mbr->array[i+partoffset].res)); mbr->array[i+partoffset].user_type = user_types[i]; mbr->array[i+partoffset].ro = 0; mbr->array[i+partoffset].addrhi = 0; mbr->array[i+partoffset].lenhi = 0; mbr->array[i+partoffset].addrlo = start; mbr->array[i+partoffset].lenlo = lens[i]; start += lens[i]; } printf("\nready to write new partition tables:\n"); printmbr(mbr); for (i = 0; i < MBR_COPY_NUM; i++) { if (mbrs[i]) _free_mbr(mbrs[i]); } printf("\nwrite new partition tables? (Y/N)\n"); read(0, &yn, 1); if (yn != 'Y' && yn != 'y') { printf("aborting\n"); return 0; } for (i = 0; i < MBR_COPY_NUM; i++) { mbr->index = i; // calculate new checksum *(__u32 *)mbr = calc_crc32((__u32 *)mbr + 1,MBR_SIZE - 4); lseek(fd,MBR_START_ADDRESS + MBR_SIZE*i,SEEK_SET); write(fd,mbr,MBR_SIZE); } if (ioctl(fd, BLKRRPART, NULL)) perror("Failed rereading partition table"); return 1; } int nand_part (int argc, char **argv, const char *cmd, int fd, int force) { int partoffset = 0; int i; char names[MAX_PART_COUNT][MAX_NAME]; __u32 lens[MAX_PART_COUNT]; unsigned int user_types[MAX_PART_COUNT]; __u32 start; // parse name/len arguments memset((void *) user_types, 0, sizeof(user_types)); if (argc > 0) { if (sscanf(argv[0], "%u", &start) != 1) { partoffset++; if (force) { printf("if using -f, must set info for first partition\n"); usage(cmd); close(fd); return -3; } } else { argc--; argv++; } if (start < MBR_SIZE * MBR_COPY_NUM / 512) { printf("Partition 1 starting offset must be at least %d\n", MBR_SIZE * MBR_COPY_NUM / 512); close(fd); return -3; } for (i = 0; i < argc; i++) { if (sscanf(argv[i], "%s %d %d", names[i], &lens[i], &user_types[i]) < 2) { printf("bad 'name len' argument\n"); usage(cmd); close(fd); return -3; } } } checkmbrs(fd); if (argc > MAX_PART_COUNT - partoffset) { printf("too many partitions specified (MAX 14)\n"); usage(cmd); close(fd); return -2; } if (argc > 0) { if (writembrs(fd, names, start, lens, user_types, argc, partoffset, force)) { printf("\nverifying new partition tables:\n"); checkmbrs(fd); printf("rereading partition table... returned %d\n", ioctl(fd, BLKRRPART, 0)); } } close(fd); return 0; } sunxi-tools-1.3/phoenix_info.c000066400000000000000000000107741262340046500165250ustar00rootroot00000000000000/* * Copyright (C) 2012 Henrik Nordstrom * * 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, see . */ /* Needs _BSD_SOURCE for htole and letoh */ /* glibc 2.20+ also requires _DEFAULT_SOURCE */ #define _DEFAULT_SOURCE #define _BSD_SOURCE #include #include #include #include #include "endian_compat.h" struct phoenix_ptable { char signature[16]; /* "PHOENIX_CARD_IMG" */ unsigned int unknown1; /* 0x00200100 */ unsigned short parts; /* Number of partitions */ unsigned short unknown2; /* 0x0001 */ unsigned char pad[8]; struct phoenix_entry { unsigned int start; /* 512 bytes blocks */ unsigned int size; /* bytes */ unsigned int unknown; /* ???? */ unsigned int sig; /* "add\0" */ } part[62]; } ptable; static int save_part(struct phoenix_ptable *ptable, int part, const char *dest, FILE *in) { int l = strlen(dest) + 16; char outname[l]; FILE *out; char *buf = NULL; int ret = 0; snprintf(outname, l, dest, part); if (part > ptable->parts) { fprintf(stderr, "ERROR: Part index out of range\n"); return -1; } buf = malloc(ptable->part[part].size); if (!buf) goto err; if (strcmp(outname, "-") != 0) out = fopen(outname, "wb"); else out = stdout; if (!out) goto err; if (fseek(in, ptable->part[part].start * 0x200, SEEK_SET) == -1) goto err; if (fread(buf, ptable->part[part].size, 1, in) != 1) goto err; if (fwrite(buf, ptable->part[part].size, 1, out) != 1) goto err; ret = 0; _exit: if (buf) free(buf); if (out != stdout) fclose(out); return ret; err: perror(NULL); ret = -1; goto _exit; } static void usage(char **argv) { printf("Usage: %s [options] [phoenix_image]\n" " -v verbose\n" " -q quiet\n" " -p N part number\n" " -o X destination directory, file or pattern (%%d for part number)\n" " -s save all parts\n" , argv[0] ); } int main(int argc, char **argv) { int i; FILE *in = stdin; int verbose = 1; int save_parts = 0; int part = -1; int opt; const char *dest = "%d.img"; while ((opt = getopt(argc, argv, "vqso:p:?")) != -1) { switch(opt) { case 'v': verbose++; break; case 'q': if (verbose) verbose--; break; case 'o': dest = optarg; save_parts = 1; break; case 'p': save_parts = 1; part = atoi(optarg); break; case 's': save_parts = 1; break; default: usage(argv); exit(1); break; } } if (save_parts && !strchr(dest, '%')) { const char *t = dest; if (!*t) t = "./"; if (t[strlen(t)-1] == '/' || !part) { int l = strlen(t) + strlen("/%d.img") + 1; char *tmp = malloc(l); snprintf(tmp, l, "%s/%%d.img", optarg); t = tmp; } dest = t; } if (argc > optind + 1) { usage(argv); exit(1); } if (optind < argc ) { in = fopen(argv[optind], "rb"); } fseek(in, 0x1C00, SEEK_CUR); fread(&ptable, 1, 0x400, in); if (strncmp(ptable.signature, "PHOENIX_CARD_IMG", 16) != 0) { fprintf(stderr, "ERROR: Not a phoenix image\n"); exit(1); } if (verbose > 1) { printf("???? : %08x\n", le32toh(ptable.unknown1)); printf("Parts : %d\n", le16toh(ptable.parts)); printf("???? : %08x\n", le16toh(ptable.unknown2)); printf("pad : %02x%02x%02x%02x%02x%02x%02x%02x\n", ptable.pad[0], ptable.pad[1], ptable.pad[2], ptable.pad[3], ptable.pad[4], ptable.pad[5], ptable.pad[6], ptable.pad[7]); printf("\n"); } for (i = 0; i < le16toh(ptable.parts); i++) { if (verbose && (part == -1 || part == i)) { printf("part %d:\n", i); printf("\tstart: 0x%08x (%u / 0x%08x)\n", le32toh(ptable.part[i].start)*512, le32toh(ptable.part[i].start), le32toh(ptable.part[i].start)); printf("\tsize : %u\n", le32toh(ptable.part[i].size)); printf("\t?????: %08x\n", le32toh(ptable.part[i].unknown)); if (verbose > 1 || le32toh(ptable.part[i].sig) != 0x00646461) printf("\tsig??: %08x\n", le32toh(ptable.part[i].sig)); printf("\n"); } if (save_parts && (part == -1 || part == i)) { save_part(&ptable, i, dest, in); } } } sunxi-tools-1.3/pio.c000066400000000000000000000225311262340046500146210ustar00rootroot00000000000000/* * (C) Copyright 2011 Henrik Nordstrom * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ /* needs _BSD_SOURCE for htole and letoh */ /* glibc 2.20+ also requires _DEFAULT_SOURCE */ #define _DEFAULT_SOURCE #define _BSD_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "endian_compat.h" #define PIO_REG_SIZE 0x228 /*0x300*/ #define PIO_PORT_SIZE 0x24 struct pio_status { int mul_sel; int pull; int drv_level; int data; }; #define PIO_REG_CFG(B, N, I) ((B) + (N)*0x24 + ((I)<<2) + 0x00) #define PIO_REG_DLEVEL(B, N, I) ((B) + (N)*0x24 + ((I)<<2) + 0x14) #define PIO_REG_PULL(B, N, I) ((B) + (N)*0x24 + ((I)<<2) + 0x1C) #define PIO_REG_DATA(B, N) ((B) + (N)*0x24 + 0x10) #define PIO_NR_PORTS 9 /* A-I */ #define LE32TOH(X) le32toh(*((uint32_t*)(X))) static int pio_get(const char *buf, uint32_t port, uint32_t port_num, struct pio_status *pio) { uint32_t val; uint32_t port_num_func, port_num_pull; uint32_t offset_func, offset_pull; port_num_func = port_num >> 3; offset_func = ((port_num & 0x07) << 2); port_num_pull = port_num >> 4; offset_pull = ((port_num & 0x0f) << 1); /* func */ val = LE32TOH(PIO_REG_CFG(buf, port, port_num_func)); pio->mul_sel = (val>>offset_func) & 0x07; /* pull */ val = LE32TOH(PIO_REG_PULL(buf, port, port_num_pull)); pio->pull = (val>>offset_pull) & 0x03; /* dlevel */ val = LE32TOH(PIO_REG_DLEVEL(buf, port, port_num_pull)); pio->drv_level = (val>>offset_pull) & 0x03; /* i/o data */ if (pio->mul_sel > 1) pio->data = -1; else { val = LE32TOH(PIO_REG_DATA(buf, port)); pio->data = (val >> port_num) & 0x01; } return 1; } static int pio_set(char *buf, uint32_t port, uint32_t port_num, struct pio_status *pio) { uint32_t *addr, val; uint32_t port_num_func, port_num_pull; uint32_t offset_func, offset_pull; port_num_func = port_num >> 3; offset_func = ((port_num & 0x07) << 2); port_num_pull = port_num >> 4; offset_pull = ((port_num & 0x0f) << 1); /* func */ if (pio->mul_sel >= 0) { addr = (uint32_t*)PIO_REG_CFG(buf, port, port_num_func); val = le32toh(*addr); val &= ~(0x07 << offset_func); val |= (pio->mul_sel & 0x07) << offset_func; *addr = htole32(val); } /* pull */ if (pio->pull >= 0) { addr = (uint32_t*)PIO_REG_PULL(buf, port, port_num_pull); val = le32toh(*addr); val &= ~(0x03 << offset_pull); val |= (pio->pull & 0x03) << offset_pull; *addr = htole32(val); } /* dlevel */ if (pio->drv_level >= 0) { addr = (uint32_t*)PIO_REG_DLEVEL(buf, port, port_num_pull); val = le32toh(*addr); val &= ~(0x03 << offset_pull); val |= (pio->drv_level & 0x03) << offset_pull; *addr = htole32(val); } /* data */ if (pio->data >= 0) { addr = (uint32_t*)PIO_REG_DATA(buf, port); val = le32toh(*addr); if (pio->data) val |= (0x01 << port_num); else val &= ~(0x01 << port_num); *addr = htole32(val); } return 1; } static void pio_print(int port, int port_nr, struct pio_status *pio) { printf("P%c%d", 'A'+port, port_nr); printf("<%x>", pio->mul_sel); printf("<%x>", pio->pull); printf("<%x>", pio->drv_level); if (pio->data >= 0) printf("<%x>", pio->data); fputc('\n', stdout); } static void print(const char *buf) { int port, i; struct pio_status pio; for (port=0; port < PIO_NR_PORTS; port++) { for (i=0; i<32; i++) { if (pio_get(buf, port, i, &pio)) { pio_print(port, i, &pio); } } } } static const char *argv0; static void usage(int rc ) { fprintf(stderr, "usage: %s -m|-i input [-o output] pin..\n", argv0); fprintf(stderr," -m mmap - read pin state from system\n"); fprintf(stderr," -i read pin state from file\n"); fprintf(stderr," -o save pin state data to file\n"); fprintf(stderr," print Show all pins\n"); fprintf(stderr," Pxx Show pin\n"); fprintf(stderr," Pxx Configure pin\n"); fprintf(stderr," Pxx=data,drive Configure GPIO output\n"); fprintf(stderr," Pxx*count Oscillate GPIO output (mmap mode only)\n"); fprintf(stderr," Pxx?pull Configure GPIO input\n"); fprintf(stderr," clean Clean input pins\n"); fprintf(stderr, "\n mode 0-7, 0=input, 1=ouput, 2-7 I/O function\n"); fprintf(stderr, " pull 0=none, 1=up, 2=down\n"); fprintf(stderr, " drive 0-3, I/O drive level\n"); exit(rc); } static void parse_pin(int *port, int *pin, const char *name) { if (*name == 'P') name++; *port = *name++ - 'A'; *pin = atoi(name); } static void cmd_show_pin(char *buf, const char *pin) { int port, port_nr; struct pio_status pio; parse_pin(&port, &port_nr, pin); if (!pio_get(buf, port, port_nr, &pio)) usage(1); pio_print(port, port_nr, &pio); } static int parse_int(int *dst, const char *in) { int value; char *next; errno = 0; value = strtol(in, &next, 0); if (!errno && next != in) { *dst = value; return 0; } return -1; } static void cmd_set_pin(char *buf, const char *pin) { int port, port_nr; const char *t = pin; struct pio_status pio; parse_pin(&port, &port_nr, pin); if (!pio_get(buf, port, port_nr, &pio)) usage(1); if ((t = strchr(pin, '='))) { pio.mul_sel = 1; if (t) { t++; parse_int(&pio.data, t); } if (t) t = strchr(t, ','); if (t) { t++; parse_int(&pio.drv_level, t); } } else if ((t = strchr(pin, '?'))) { pio.mul_sel = 0; pio.data = 0; pio.drv_level = 0; if (t) { t++; parse_int(&pio.pull, t); } } else if ((t = strchr(pin, '<'))) { if (t) { t++; parse_int(&pio.mul_sel, t); } if (t) t = strchr(t, '<'); if (t) { t++; parse_int(&pio.pull, t); } if (t) t = strchr(t, '<'); if (t) { t++; parse_int(&pio.drv_level, t); } if (t) t = strchr(t, '<'); if (t) { t++; parse_int(&pio.data, t); } } pio_set(buf, port, port_nr, &pio); } static void cmd_oscillate(char *buf, const char *pin) { int port, port_nr; const char *t = pin; int i, n = 0; uint32_t *addr, val; parse_pin(&port, &port_nr, pin); { struct pio_status pio; if (!pio_get(buf, port, port_nr, &pio)) usage(1); pio.mul_sel = 1; pio_set(buf, port, port_nr, &pio); } addr = (uint32_t*)PIO_REG_DATA(buf, port); t = strchr(pin, '*'); parse_int(&n, t+1); val = le32toh(*addr); for (i = 0; i < n; i++) { val ^= 1 << port_nr; *addr = htole32(val); } } static void cmd_clean(char *buf) { int port, i; struct pio_status pio; for (port=0; port < PIO_NR_PORTS; port++) { for (i=0; i<32; i++) { if (pio_get(buf, port, i, &pio)) { if (pio.mul_sel == 0) { pio.data = 0; pio_set(buf, port, i, &pio); } } } } } static int do_command(char *buf, const char **args, int argc) { const char *command = args[0]; if (*command == 'P') { if (strchr(command, '<')) cmd_set_pin(buf, command); else if (strchr(command, '=')) cmd_set_pin(buf, command); else if (strchr(command, '?')) cmd_set_pin(buf, command); else if (strchr(command, '*')) cmd_oscillate(buf, command); else cmd_show_pin(buf, command); } else if (strcmp(command, "print") == 0) print(buf); else if (strcmp(command, "clean") == 0) cmd_clean(buf); else usage(1); return 1; } int main(int argc, char **argv) { int opt; FILE *in = NULL; FILE *out = NULL; const char *in_name = NULL; const char *out_name = NULL; char buf_[PIO_REG_SIZE]; char *buf = buf_; int do_mmap = 0; argv0 = argv[0]; while ((opt = getopt(argc, argv, "i:o:m")) != -1) { switch(opt) { case '?': usage(0); case 'm': do_mmap = 1; break; case 'i': in_name = optarg; break; case 'o': out_name = optarg; break; } } if (!in_name && !do_mmap) usage(1); if (do_mmap) { int pagesize = sysconf(_SC_PAGESIZE); int fd = open("/dev/mem",O_RDWR); int addr = 0x01c20800 & ~(pagesize-1); int offset = 0x01c20800 & (pagesize-1); if (fd == -1) { perror("open /dev/mem"); exit(1); } buf = mmap(NULL, (0x800 + pagesize - 1) & ~(pagesize-1), PROT_WRITE|PROT_READ, MAP_SHARED, fd, addr); if (!buf) { perror("mmap PIO"); exit(1); } close(fd); buf += offset; } if (in_name) { if (strcmp(in_name, "-") == 0) { in = stdin; } else { in = fopen(in_name, "rb"); if (!in) { perror("open input"); exit(1); } } } if (in) { if (fread(buf, PIO_REG_SIZE, 1, in) != 1) { perror("read input"); exit(1); } if (in != stdin) fclose(in); } while(optind < argc) { optind += do_command(buf, (const char **)(argv + optind), argc - optind); } if (out_name) { if (strcmp(out_name, "-") == 0) { out = stdout; } else { out = fopen(out_name, "wb"); if (!out) { perror("open output"); exit(1); } } if (fwrite(buf, PIO_REG_SIZE, 1, out) != 1) { perror("write output"); exit(1); } } } sunxi-tools-1.3/script.c000066400000000000000000000134051262340046500153360ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #include "common.h" #include #include #include #include #include "script.h" /* */ struct script *script_new(void) { struct script *script; if ((script = malloc(sizeof(*script)))) list_init(&script->sections); return script; } void script_delete(struct script *script) { struct list_entry *o; assert(script); while ((o = list_last(&script->sections))) { struct script_section *section = container_of(o, struct script_section, sections); script_section_delete(section); } free(script); } /* */ struct script_section *script_section_new(struct script *script, const char *name) { struct script_section *section; assert(script); assert(name && *name); if ((section = malloc(sizeof(*section)))) { size_t l = strlen(name); if (l>31) /* truncate */ l=31; memcpy(section->name, name, l); section->name[l] = '\0'; list_init(§ion->entries); list_append(&script->sections, §ion->sections); } return section; } void script_section_delete(struct script_section *section) { struct list_entry *o; assert(section); while ((o = list_last(§ion->entries))) { struct script_entry *entry = container_of(o, struct script_entry, entries); script_entry_delete(entry); } if (!list_empty(§ion->sections)) list_remove(§ion->sections); } struct script_section *script_find_section(struct script *script, const char *name) { struct list_entry *o; struct script_section *section; assert(script); assert(name); for (o = list_first(&script->sections); o; o = list_next(&script->sections, o)) { section = container_of(o, struct script_section, sections); if (strcmp(section->name, name) == 0) return section; } return NULL; } /* */ static inline void script_entry_append(struct script_section *section, struct script_entry *entry, enum script_value_type type, const char *name) { size_t l; assert(section); assert(entry); assert(name); l = strlen(name); if (l>31) /* truncate */ l=31; memcpy(entry->name, name, l); entry->name[l] = '\0'; entry->type = type; list_append(§ion->entries, &entry->entries); } void script_entry_delete(struct script_entry *entry) { void *container; assert(entry); assert(entry->type == SCRIPT_VALUE_TYPE_SINGLE_WORD || entry->type == SCRIPT_VALUE_TYPE_STRING || entry->type == SCRIPT_VALUE_TYPE_GPIO || entry->type == SCRIPT_VALUE_TYPE_NULL); if (!list_empty(&entry->entries)) list_remove(&entry->entries); switch(entry->type) { case SCRIPT_VALUE_TYPE_SINGLE_WORD: container = container_of(entry, struct script_single_entry, entry); break; case SCRIPT_VALUE_TYPE_STRING: container = container_of(entry, struct script_string_entry, entry); break; case SCRIPT_VALUE_TYPE_GPIO: container = container_of(entry, struct script_gpio_entry, entry); break; case SCRIPT_VALUE_TYPE_NULL: container = container_of(entry, struct script_null_entry, entry); break; default: abort(); } free(container); } struct script_null_entry *script_null_entry_new(struct script_section *section, const char *name) { struct script_null_entry *entry; assert(section); assert(name && *name); if ((entry = malloc(sizeof(*entry)))) { script_entry_append(section, &entry->entry, SCRIPT_VALUE_TYPE_NULL, name); } return entry; } struct script_single_entry *script_single_entry_new(struct script_section *section, const char *name, uint32_t value) { struct script_single_entry *entry; assert(section); assert(name && *name); if ((entry = malloc(sizeof(*entry)))) { entry->value = value; script_entry_append(section, &entry->entry, SCRIPT_VALUE_TYPE_SINGLE_WORD, name); } return entry; } struct script_string_entry *script_string_entry_new(struct script_section *section, const char *name, size_t l, const char *s) { struct script_string_entry *entry; assert(section); assert(name); assert(s); if ((entry = malloc(sizeof(*entry)+l+1))) { entry->l = l; memcpy(entry->string, s, l); entry->string[l] = '\0'; script_entry_append(section, &entry->entry, SCRIPT_VALUE_TYPE_STRING, name); } return entry; } struct script_gpio_entry *script_gpio_entry_new(struct script_section *section, const char *name, unsigned port, unsigned num, int32_t data[4]) { struct script_gpio_entry *entry; assert(section); assert(name && *name); if ((entry = malloc(sizeof(*entry)))) { entry->port = port; entry->port_num = num; for (int i=0; i<4; i++) entry->data[i] = data[i]; script_entry_append(section, &entry->entry, SCRIPT_VALUE_TYPE_GPIO, name); } return entry; } struct script_entry *script_find_entry(struct script_section *section, const char *name) { struct list_entry *o; struct script_entry *ep; assert(section); assert(name); for (o = list_first(§ion->entries); o; o = list_next(§ion->entries, o)) { ep = container_of(o, struct script_entry, entries); if (strcmp(ep->name, name) == 0) return ep; } return NULL; } sunxi-tools-1.3/script.h000066400000000000000000000064271262340046500153510ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #ifndef _SUNXI_TOOLS_SCRIPT_H #define _SUNXI_TOOLS_SCRIPT_H #define GPIO_BANK_MAX 13 /* N */ /** head of the data tree */ struct script { struct list_entry sections; }; /** head of each section */ struct script_section { char name[32]; struct list_entry sections; struct list_entry entries; }; /** types of values */ enum script_value_type { SCRIPT_VALUE_TYPE_SINGLE_WORD = 1, SCRIPT_VALUE_TYPE_STRING, SCRIPT_VALUE_TYPE_MULTI_WORD, SCRIPT_VALUE_TYPE_GPIO, SCRIPT_VALUE_TYPE_NULL, }; /** generic entry */ struct script_entry { char name[32]; enum script_value_type type; struct list_entry entries; }; /** null entry */ struct script_null_entry { struct script_entry entry; }; /** entry with 32b value */ struct script_single_entry { struct script_entry entry; uint32_t value; }; /** entry with string value */ struct script_string_entry { struct script_entry entry; size_t l; char string[]; }; /** entry describing a GPIO */ struct script_gpio_entry { struct script_entry entry; unsigned port, port_num; int32_t data[4]; }; /** create a new script tree */ struct script *script_new(void); /** deletes a tree recursively */ void script_delete(struct script *); /** create a new section appended to a given tree */ struct script_section *script_section_new(struct script *script, const char *name); /** deletes a section recursvely and removes it from the script */ void script_section_delete(struct script_section *section); /** find existing section */ struct script_section *script_find_section(struct script *script, const char *name); /** deletes an entry and removes it from the section */ void script_entry_delete(struct script_entry *entry); /** create a new empty/null entry appended to a section */ struct script_null_entry *script_null_entry_new(struct script_section *section, const char *name); /** create a new single word entry appended to a section */ struct script_single_entry *script_single_entry_new(struct script_section *section, const char *name, uint32_t value); /** create a new string entry appended to a section */ struct script_string_entry *script_string_entry_new(struct script_section *section, const char *name, size_t l, const char *s); /** create a new GPIO entry appended to a section */ struct script_gpio_entry *script_gpio_entry_new(struct script_section *script, const char *name, unsigned port, unsigned num, int32_t data[4]); /** find existing entry in a giving section */ struct script_entry *script_find_entry(struct script_section *section, const char *name); #endif sunxi-tools-1.3/script_bin.c000066400000000000000000000225221262340046500161660ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #include "common.h" #include #include #include #include #include #include "script.h" #include "script_bin.h" #define pr_info(...) errf("fexc-bin: " __VA_ARGS__) #define pr_err(...) errf("E: fexc-bin: " __VA_ARGS__) #ifdef DEBUG #define pr_debug(...) errf("D: fexc-bin: " __VA_ARGS__) #else #define pr_debug(...) #endif #define PTR(B, OFF) (void*)((char*)(B)+(OFF)) #define WORDS(S) (((S)+(sizeof(uint32_t)-1))/(sizeof(uint32_t))) /* * generator */ size_t script_bin_size(struct script *script, size_t *sections, size_t *entries) { size_t words = 0, bin_size = 0; struct list_entry *ls, *le; struct script_section *section; struct script_entry *entry; struct script_string_entry *string; *sections = *entries = 0; /* count */ for (ls = list_first(&script->sections); ls; ls = list_next(&script->sections, ls)) { section = container_of(ls, struct script_section, sections); size_t c = 0; for (le = list_first(§ion->entries); le; le = list_next(§ion->entries, le)) { size_t size = 0; entry = container_of(le, struct script_entry, entries); c++; switch(entry->type) { case SCRIPT_VALUE_TYPE_NULL: case SCRIPT_VALUE_TYPE_SINGLE_WORD: size = sizeof(uint32_t); break; case SCRIPT_VALUE_TYPE_STRING: string = container_of(entry, struct script_string_entry, entry); size = string->l; break; case SCRIPT_VALUE_TYPE_GPIO: size = sizeof(struct script_bin_gpio_value); break; default: abort(); } words += WORDS(size); } *sections += 1; *entries += c; } bin_size = sizeof(struct script_bin_head) + (*sections)*sizeof(struct script_bin_section) + (*entries)*sizeof(struct script_bin_entry) + words*sizeof(uint32_t); pr_debug("sections:%zu entries:%zu data:%zu/%zu -> %zu\n", *sections, *entries, words, words*sizeof(uint32_t), bin_size); return bin_size; } int script_generate_bin(void *bin, size_t UNUSED(bin_size), struct script *script, size_t sections, size_t entries) { struct script_bin_head *head; struct script_bin_section *section; struct script_bin_entry *entry; void *data; struct list_entry *ls, *le; head = bin; section = head->section; entry = (void*)section+sections*sizeof(*section); data = (void*)entry+entries*sizeof(*entry); pr_debug("head....:%p\n", head); pr_debug("section.:%p (offset:%zu, each:%zu)\n", section, (void*)section-bin, sizeof(*section)); pr_debug("entry...:%p (offset:%zu, each:%zu)\n", entry, (void*)entry-bin, sizeof(*entry)); pr_debug("data....:%p (offset:%zu)\n", data, (void*)data-bin); head->sections = sections; head->version[0] = 0; head->version[1] = 1; head->version[2] = 2; for (ls = list_first(&script->sections); ls; ls = list_next(&script->sections, ls)) { struct script_section *s; size_t c = 0; s = container_of(ls, struct script_section, sections); memcpy(section->name, s->name, strlen(s->name)); section->offset = ((void*)entry-bin)>>2; for (le = list_first(&s->entries); le; le = list_next(&s->entries, le)) { struct script_entry *e; e = container_of(le, struct script_entry, entries); size_t size = 0; memcpy(entry->name, e->name, strlen(e->name)); entry->offset = ((void*)data-bin)>>2; entry->pattern = (e->type<<16); switch(e->type) { case SCRIPT_VALUE_TYPE_SINGLE_WORD: { struct script_single_entry *single; int32_t *bdata = data; single = container_of(e, struct script_single_entry, entry); *bdata = single->value; size = sizeof(*bdata); }; break; case SCRIPT_VALUE_TYPE_STRING: { struct script_string_entry *string; string = container_of(e, struct script_string_entry, entry); size = string->l; memcpy(data, string->string, size); /* align */ size += sizeof(uint32_t)-1; size /= sizeof(uint32_t); size *= sizeof(uint32_t); }; break; case SCRIPT_VALUE_TYPE_MULTI_WORD: abort(); case SCRIPT_VALUE_TYPE_GPIO: { struct script_gpio_entry *gpio; struct script_bin_gpio_value *bdata = data; gpio = container_of(e, struct script_gpio_entry, entry); bdata->port = gpio->port; bdata->port_num = gpio->port_num; bdata->mul_sel = gpio->data[0]; bdata->pull = gpio->data[1]; bdata->drv_level = gpio->data[2]; bdata->data = gpio->data[3]; size = sizeof(*bdata); }; break; case SCRIPT_VALUE_TYPE_NULL: size = sizeof(uint32_t); break; } data += size; entry->pattern |= (size>>2); pr_debug("%s.%s <%p> (type:%d, words:%d (%zu), offset:%d)\n", section->name, entry->name, entry, (entry->pattern>>16) & 0xffff, (entry->pattern>>0) & 0xffff, size, entry->offset); c++; entry++; } section->length = c; pr_debug("%s <%p> (length:%d, offset:%d)\n", section->name, section, section->length, section->offset); section++; } return 1; } /* * decompiler */ static int decompile_section(void *bin, size_t bin_size, const char *filename, struct script_bin_section *section, struct script *script) { struct script_bin_entry *entry; struct script_section *s; int size; if ((section->offset < 0) || (section->offset > (int)(bin_size / 4))) { pr_err("Malformed data: invalid section offset: %d\n", section->offset); return 0; } size = bin_size - 4 * section->offset; if ((section->length < 0) || (section->length > (size / (int)sizeof(struct script_bin_entry)))) { pr_err("Malformed data: invalid section length: %d\n", section->length); return 0; } if ((s = script_section_new(script, section->name)) == NULL) goto malloc_error; entry = PTR(bin, section->offset<<2); for (int i = section->length; i--; entry++) { void *data = PTR(bin, entry->offset<<2); unsigned type, words; type = (entry->pattern >> 16) & 0xffff; words = (entry->pattern >> 0) & 0xffff; switch(type) { case SCRIPT_VALUE_TYPE_SINGLE_WORD: { uint32_t *v = data; if (words != 1) { pr_err("%s: %s.%s: invalid length %d (assuming %d)\n", filename, section->name, entry->name, words, 1); } if (!script_single_entry_new(s, entry->name, *v)) goto malloc_error; }; break; case SCRIPT_VALUE_TYPE_STRING: { size_t bytes = words << 2; const char *p, *pe, *v = data; for(p=v, pe=v+bytes; *p && p!=pe; p++) ; /* seek end-of-string */ if (!script_string_entry_new(s, entry->name, p-v, v)) goto malloc_error; }; break; case SCRIPT_VALUE_TYPE_GPIO: { struct script_bin_gpio_value *gpio = data; int32_t v[4]; if (words != 6) { pr_err("%s: %s.%s: invalid length %d (assuming %d)\n", filename, section->name, entry->name, words, 6); } else if (gpio->port == 0xffff) { ; /* port:power */ } else if (gpio->port < 1 || gpio->port > GPIO_BANK_MAX) { pr_err("%s: %s.%s: unknown GPIO port bank %c (%u)\n", filename, section->name, entry->name, 'A'+gpio->port, gpio->port); goto failure; } v[0] = gpio->mul_sel; v[1] = gpio->pull; v[2] = gpio->drv_level; v[3] = gpio->data; if (!script_gpio_entry_new(s, entry->name, gpio->port, gpio->port_num, v)) goto malloc_error; }; break; case SCRIPT_VALUE_TYPE_NULL: if (!*entry->name) { pr_err("%s: empty entry in section: %s\n", filename, section->name); } else if (!script_null_entry_new(s, entry->name)) { goto malloc_error; } break; default: pr_err("%s: %s.%s: unknown type %d\n", filename, section->name, entry->name, type); goto failure; } } return 1; malloc_error: pr_err("%s: %s\n", "malloc", strerror(errno)); failure: return 0; } #define SCRIPT_BIN_VERSION_LIMIT 0x10 #define SCRIPT_BIN_SECTION_LIMIT 0x100 int script_decompile_bin(void *bin, size_t bin_size, const char *filename, struct script *script) { int i; struct script_bin_head *head = bin; pr_info("%s: version: %d.%d.%d\n", filename, head->version[0], head->version[1], head->version[2]); pr_info("%s: size: %zu (%d sections)\n", filename, bin_size, head->sections); if (head->sections > SCRIPT_BIN_SECTION_LIMIT) { pr_err("Malformed data: too many sections (%d).\n", head->sections); return 0; } if ((head->version[0] > SCRIPT_BIN_VERSION_LIMIT) || (head->version[1] > SCRIPT_BIN_VERSION_LIMIT) || (head->version[2] > SCRIPT_BIN_VERSION_LIMIT)) { pr_err("Malformed data: version %d.%d.%d.\n", head->version[0], head->version[1], head->version[2]); return 0; } /* TODO: SANITY: compare head.sections with bin_size */ for (i=0; i < head->sections; i++) { struct script_bin_section *section = &head->section[i]; if (!decompile_section(bin, bin_size, filename, section, script)) return 0; } return 1; } sunxi-tools-1.3/script_bin.h000066400000000000000000000032571262340046500161770ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #ifndef _SUNXI_TOOLS_SCRIPT_BIN_H #define _SUNXI_TOOLS_SCRIPT_BIN_H /** binary representation of the head of a section */ struct script_bin_section { char name[32]; int32_t length; int32_t offset; }; /** binary representation of the head of the script file */ struct script_bin_head { int32_t sections; int32_t version[3]; struct script_bin_section section[]; }; /** binary representation of the head of an entry */ struct script_bin_entry { char name[32]; int32_t offset; int32_t pattern; }; /** binary representation of a GPIO */ struct script_bin_gpio_value { int32_t port; int32_t port_num; int32_t mul_sel; int32_t pull; int32_t drv_level; int32_t data; }; size_t script_bin_size(struct script *script, size_t *sections, size_t *entries); int script_generate_bin(void *bin, size_t bin_size, struct script *script, size_t sections, size_t entries); int script_decompile_bin(void *bin, size_t bin_size, const char *filename, struct script *script); #endif sunxi-tools-1.3/script_extractor.c000066400000000000000000000023511262340046500174270ustar00rootroot00000000000000/* * Copyright (C) 2015 Olliver Schinagl * * 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, see . */ #include #include #include #include #include #include #define SCRIPT_START 0x43000000 #define SCRIPT_SIZE 0x20000 int main(int argc, char *argv[]) { char *addr; int fd; int i; int size; fd = open("/dev/mem", O_RDONLY); size = SCRIPT_SIZE; if (argc) size = atoi(argv[1]); addr = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, SCRIPT_START); for (i = 0; i < SCRIPT_SIZE; i++) putchar(addr[i]); munmap(NULL, SCRIPT_SIZE); close(fd); return 0; } sunxi-tools-1.3/script_fex.c000066400000000000000000000205451262340046500162030ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #include "common.h" #include #include #include #include #include #include "script.h" #include "script_fex.h" #define MAX_LINE 255 #define pr_info(...) errf("fexc-fex: " __VA_ARGS__) #define pr_err(...) errf("E: fexc-fex: " __VA_ARGS__) #ifdef DEBUG #define pr_debug(...) errf("D: fexc-fex: " __VA_ARGS__) #else #define pr_debug(...) #endif /* * generator */ static inline size_t strlen2(const char *s) { size_t l = strlen(s); const char *p = &s[l-1]; while (l && *p >= '0' && *p <= '9') { l--; p--; } return l; } static int find_full_match(const char *s, size_t l, const char **list) { while (*list) { if (memcmp(s, *list, l) == 0) return 1; list++; } return 0; } /** */ static int decompile_single_mode(const char *name) { static const char *hexa_entries[] = { "dram_baseaddr", "dram_zq", "dram_tpr", "dram_emr", "g2d_size", "rtp_press_threshold", "rtp_sensitive_level", "ctp_twi_addr", "csi_twi_addr", "csi_twi_addr_b", "tkey_twi_addr", "lcd_gamma_tbl_", "gsensor_twi_addr", NULL }; size_t l = strlen2(name); if (find_full_match(name, l, hexa_entries)) return 0; else return -1; } int script_generate_fex(FILE *out, const char *UNUSED(filename), struct script *script) { struct list_entry *ls, *le; struct script_section *section; struct script_entry *entry; for (ls = list_first(&script->sections); ls; ls = list_next(&script->sections, ls)) { section = container_of(ls, struct script_section, sections); fprintf(out, "[%s]\n", section->name); for (le = list_first(§ion->entries); le; le = list_next(§ion->entries, le)) { entry = container_of(le, struct script_entry, entries); switch(entry->type) { case SCRIPT_VALUE_TYPE_SINGLE_WORD: { int mode = decompile_single_mode(entry->name); struct script_single_entry *single; single = container_of(entry, struct script_single_entry, entry); fprintf(out, "%s = ", entry->name); if (mode < 0) fprintf(out, "%d", single->value); else if (mode > 0) fprintf(out, "0x%0*x", mode, single->value); else fprintf(out, "0x%x", single->value); fputc('\n', out); }; break; case SCRIPT_VALUE_TYPE_STRING: { struct script_string_entry *string; string = container_of(entry, struct script_string_entry, entry); fprintf(out, "%s = \"%.*s\"\n", entry->name, (int)string->l, string->string); }; break; case SCRIPT_VALUE_TYPE_MULTI_WORD: abort(); case SCRIPT_VALUE_TYPE_GPIO: { char port = 'A'-1; struct script_gpio_entry *gpio; gpio = container_of(entry, struct script_gpio_entry, entry); if (gpio->port == 0xffff) { fprintf(out, "%s = port:power%u", entry->name, gpio->port_num); } else { port += gpio->port; fprintf(out, "%s = port:P%c%02u", entry->name, port, gpio->port_num); } for (const int *p = gpio->data, *pe = p+4; p != pe; p++) { if (*p == -1) fputs("", out); else fprintf(out, "<%d>", *p); } fputc('\n', out); }; break; case SCRIPT_VALUE_TYPE_NULL: fprintf(out, "%s =\n", entry->name); break; } } fputc('\n', out); } return 1; } /* * parser */ /** find first not blank char */ static inline char *skip_blank(char *p) { while(isblank(*p)) p++; return p; } /** trim out blank chars at the end of a string */ static inline char *rtrim(const char *s, char *p) { if (p>s) { while (p!=s && isblank(*--p)) ; *++p='\0'; } return p; } /** */ int script_parse_fex(FILE *in, const char *filename, struct script *script) { char buffer[MAX_LINE+1]; int ok = 1; struct script_section *last_section = NULL; /* TODO: deal with longer lines correctly (specially in comments) */ for(size_t line = 1; ok && fgets(buffer, sizeof(buffer), in); line++) { char *s = skip_blank(buffer); /* beginning */ char *pe = s; /* \0... to be found */ if (*pe) while (*++pe) ; if (pe>s && pe[-1] == '\n') { if (pe>s+1 && pe[-2] == '\r') pe -= 2; else pe -= 1; *pe = '\0'; } pe = rtrim(s, pe); if (pe == s || *s == ';' || *s == '#') continue; /* empty */ else if (*s == '[') { /* section */ char *p = ++s; while (isalnum(*p) || *p == '_') p++; if (*p == ']' && *(p+1) == '\0') { *p = '\0'; if ((last_section = script_section_new(script, s))) continue; perror("malloc"); } else if (*p) { errf("E: %s:%zu: invalid character at %zu.\n", filename, line, p-buffer+1); } else { errf("E: %s:%zu: incomplete section declaration.\n", filename, line); } ok = 0; } else { /* key = value */ const char *key = s; char *mark, *p = s; if (!last_section) { errf("E: %s:%zu: data must follow a section.\n", filename, line); goto parse_error; }; while (isalnum(*p) || *p == '_') p++; mark = p; p = skip_blank(p); if (*p != '=') goto invalid_char_at_p; *mark = '\0'; /* truncate key */ p = skip_blank(p+1); if (*p == '\0') { /* NULL */ if (script_null_entry_new(last_section, key)) continue; perror("malloc"); } else if (pe > p+1 && *p == '"' && pe[-1] == '"') { /* string */ p++; *--pe = '\0'; if (script_string_entry_new(last_section, key, pe-p, p)) { pr_debug("%s.%s = \"%.*s\"\n", last_section->name, key, (int)(pe-p), p); continue; } perror("malloc"); } else if (memcmp("port:", p, 5) == 0) { /* GPIO */ p += 5; if (p[0] == 'P' && (p[1] < 'A' || p[1] > ('A' + GPIO_BANK_MAX))) ; else if (*p != 'P' && memcmp(p, "power", 5) != 0) ; else { char *end; int port; long v; if (*p == 'P') { /* port:PXN */ port = p[1] - 'A' + 1; p += 2; } else { /* port:powerN */ port = 0xffff; p += 5; } v = strtol(p, &end, 10); if (end == p) goto invalid_char_at_p; else if (v<0 || v>255) { errf("E: %s:%zu: port out of range at %zu (%ld).\n", filename, line, p-buffer+1, v); } else { int data[] = {-1,-1,-1,-1}; int port_num = v; p = end; for (int i=0; *p && i<4; i++) { if (memcmp(p, "", 9) == 0) { p += 9; continue; } else if (*p == '<') { v = strtol(++p, &end, 10); if (end == p) { ; } else if (v<0 || v>INT32_MAX) { errf("E: %s:%zu: value out of range at %zu (%ld).\n", filename, line, p-buffer+1, v); goto parse_error; } else if (*end != '>') { p = end; } else { p = end+1; data[i] = v; continue; } } break; } if (*p) goto invalid_char_at_p; if (script_gpio_entry_new(last_section, key, port, port_num, data)) { pr_debug("%s.%s = GPIO %d.%d (%d,%d,%d,%d)\n", last_section->name, key, port, port_num, data[0], data[1], data[2], data[3]); continue; } perror("malloc"); } } } else if (isdigit(*p) || (*p == '-' && isdigit(*(p+1)))) { long long v = 0; char *end; v = strtoll(p, &end, 0); p = end; if (p != pe) { goto invalid_char_at_p; } else if (v > UINT32_MAX) { errf("E: %s:%zu: value out of range %lld.\n", filename, line, v); } else if (script_single_entry_new(last_section, key, v)) { pr_debug("%s.%s = %lld\n", last_section->name, key, v); continue; } } else { goto invalid_char_at_p; } goto parse_error; invalid_char_at_p: errf("E: %s:%zu: invalid character at %zu.\n", filename, line, p-buffer+1); parse_error: ok = 0; } }; if (ferror(in)) ok = 0; return ok; } sunxi-tools-1.3/script_fex.h000066400000000000000000000016621262340046500162070ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #ifndef _SUBXI_TOOLS_SCRIPT_FEX_H #define _SUBXI_TOOLS_SCRIPT_FEX_H int script_parse_fex(FILE *in, const char *filename, struct script *script); int script_generate_fex(FILE *out, const char *filename, struct script *script); #endif sunxi-tools-1.3/script_uboot.c000066400000000000000000000135701262340046500165510ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #include "common.h" #include #include #include #include "script.h" #include "script_uboot.h" #define pr_info(...) errf("fexc-uboot: " __VA_ARGS__) #define pr_err(...) errf("E: fexc-uboot: " __VA_ARGS__) #ifdef DEBUG #define pr_debug(...) errf("D: fexc-uboot: " __VA_ARGS__) #else #define pr_debug(...) #endif struct members { const char *name; const char *translation; int mode; }; #define foreach_member(I, T) for (const struct members *I = T; \ I < T+ARRAY_SIZE(T); I++) /* */ static inline void out_u32_member(FILE *out, const char *key, int hexa, struct script_single_entry *val) { const char *fmt; if (hexa) fmt = "\t.%s = %#x,\n"; else fmt = "\t.%s = %u,\n"; fprintf(out, fmt, key, val->value); } static inline void out_gpio_member(FILE *out, const char *key, struct script_gpio_entry *gpio) { fprintf(out, "\t.%s = ", key); if (gpio->port == 0xffff) fprintf(out, "GPIO_AXP_CFG(%u", gpio->port_num); else fprintf(out, "GPIO_CFG(%u, %u", gpio->port, gpio->port_num); for (const int *p = gpio->data, *pe = p+4; p != pe; p++) { if (*p == -1) fputs(", 0xff", out); else fprintf(out, ", %u", *p); } fputs("),\n", out); } static inline void out_null_member(FILE *out, const char *key) { fprintf(out, "\t/* %s is NULL */\n", key); } static inline int out_member(FILE *out, const char *key, int mode, struct script_entry *ep) { switch (ep->type) { case SCRIPT_VALUE_TYPE_SINGLE_WORD: out_u32_member(out, key, mode, container_of(ep, struct script_single_entry, entry)); break; case SCRIPT_VALUE_TYPE_NULL: out_null_member(out, key); break; case SCRIPT_VALUE_TYPE_GPIO: out_gpio_member(out, key, container_of(ep, struct script_gpio_entry, entry)); break; default: return 0; } return 1; } /* * DRAM */ static struct members dram_members[] = { { .name="dram_clock" }, { .name="dram_clk", .translation="clock" }, { .name="dram_type" }, { .name="dram_rank_num" }, { .name="dram_density" }, { .name="dram_chip_density", .translation="density" }, { .name="dram_io_width" }, { .name="dram_bus_width" }, { .name="dram_cas" }, { .name="dram_zq" }, { .name="dram_odt_en" }, { .name="dram_size" }, { .name="dram_tpr0", .mode=1 }, { .name="dram_tpr1", .mode=1 }, { .name="dram_tpr2", .mode=1 }, { .name="dram_tpr3", .mode=1 }, { .name="dram_tpr4", .mode=1 }, { .name="dram_tpr5", .mode=1 }, { .name="dram_emr1", .mode=1 }, { .name="dram_emr2", .mode=1 }, { .name="dram_emr3", .mode=1 }, }; static int generate_dram_struct(FILE *out, struct script_section *sp) { struct script_entry *ep; const char *key; int ret = 1; fprintf(out, "static struct dram_para dram_para = {\n"); foreach_member(mp, dram_members) { ep = script_find_entry(sp, mp->name); if (!ep) continue; key = (mp->translation) ? mp->translation : mp->name+5; if (!out_member(out, key, mp->mode, ep)) { pr_err("dram_para: %s: invalid field\n", ep->name); ret = 0; } } fprintf(out, "};\n"); fputs("\nunsigned long sunxi_dram_init(void)\n" "{\n\treturn dramc_init(&dram_para);\n}\n", out); return ret; } #if 0 /* * PMU */ static struct members pmu_members[] = { { .name = "pmu_used2" }, { .name = "pmu_para" }, { .name = "pmu_adpdet" }, { .name = "pmu_shutdown_chgcur" }, { .name = "pmu_shutdown_chgcur2" }, { .name = "pmu_pwroff_vol" }, { .name = "pmu_pwron_vol" }, }; static int generate_pmu_struct(FILE *out, struct script_section *target, struct script_section *pmu_para) { struct list_entry *le; struct script_section *sp; struct script_entry *ep; const char *key; int ret = 1; fputs("\nstatic struct pmu_para pmu_para = {\n", out); sp = target; for (le = list_first(&sp->entries); le; le = list_next(&sp->entries, le)) { ep = container_of(le, struct script_entry, entries); if (!out_member(out, ep->name, 0, ep)) { pr_err("target: %s: invalid field\n", ep->name); ret = 0; } } foreach_member(mp, pmu_members) { ep = script_find_entry(pmu_para, mp->name); if (!ep) continue; key = (mp->translation) ? mp->translation : mp->name+4; if (!out_member(out, key, mp->mode, ep)) { pr_err("pmu_para: %s: invalid field\n", mp->name); ret = 0; } } fputs("};\n", out); fputs("\nint sunxi_pmu_init(void)\n" "{\n\treturn PMU_init(&pmu_para);\n}\n", out); return ret; (void) pmu_para; } #endif int script_generate_uboot(FILE *out, const char *UNUSED(filename), struct script *script) { struct { const char *name; struct script_section *sp; } sections[] = { { "dram_para", NULL }, #if 0 { "target", NULL }, { "pmu_para", NULL }, #endif }; for (unsigned i=0; i\n" #if 0 "#include \n" #endif "#include \n\n", out); generate_dram_struct(out, sections[0].sp); #if 0 generate_pmu_struct(out, sections[1].sp, sections[2].sp); #endif return 1; } sunxi-tools-1.3/script_uboot.h000066400000000000000000000015521262340046500165530ustar00rootroot00000000000000/* * Copyright (C) 2012 Alejandro Mery * * 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, see . */ #ifndef _SUBXI_TOOLS_SCRIPT_UBOOT_H #define _SUBXI_TOOLS_SCRIPT_UBOOT_H int script_generate_uboot(FILE *out, const char *filename, struct script *script); #endif