pax_global_header00006660000000000000000000000064130050664240014512gustar00rootroot0000000000000052 comment=40e6ff5db830ed7a202acb2d5115c5abbf1bfece sunxi-tools-1.4.1/000077500000000000000000000000001300506642400140215ustar00rootroot00000000000000sunxi-tools-1.4.1/.gitignore000066400000000000000000000001421300506642400160060ustar00rootroot00000000000000bin2fex fex2bin sunxi-bootinfo sunxi-fel sunxi-fexc sunxi-nand-part sunxi-pio version.h *.o *.swp sunxi-tools-1.4.1/.travis.yml000066400000000000000000000020151300506642400161300ustar00rootroot00000000000000# use container-based infrastructure sudo: false language: c # treat all warnings as errors env: EXTRA_CFLAGS=-Werror os: - linux - osx compiler: - gcc - clang # OSX uses Apple's flavor of clang anyway, so there's no point in trying "gcc". # This excludes the "gcc" compiler from the build matrix for OSX: matrix: exclude: - os: osx compiler: gcc # take care of the libusb dependency for Linux addons: apt: packages: - libusb-1.0-0-dev # take care of the libusb dependency for Mac OS X; on Linux use "make all" later before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install libusb; else export TARGET="all CROSS_COMPILE="; fi # build using the Makefile script: - make ${TARGET} && make misc # when on Linux: run/simulate a test install after_success: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make install-all install-misc DESTDIR=/tmp PREFIX=/sunxi-tools; fi # turn off email notifications notifications: - email: false sunxi-tools-1.4.1/LICENSE.md000066400000000000000000000427451300506642400154410ustar00rootroot00000000000000### 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. ### 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. one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands \`show w' and \`show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than \`show w' and \`show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the [GNU Lesser General Public License](http://www.gnu.org/licenses/lgpl.html) instead of this License.sunxi-tools-1.4.1/Makefile000066400000000000000000000140261300506642400154640ustar00rootroot00000000000000# 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 $(EXTRA_CFLAGS) CFLAGS += -std=c99 $(DEFINES) CFLAGS += -Iinclude/ DEFINES = -D_POSIX_C_SOURCE=200112L # Define _BSD_SOURCE, necessary to expose all endian conversions properly. # See http://linux.die.net/man/3/endian DEFINES += -D_BSD_SOURCE # glibc 2.20+ also requires _DEFAULT_SOURCE DEFINES += -D_DEFAULT_SOURCE ifeq (NetBSD,$(OS)) # add explicit _NETBSD_SOURCE, see https://github.com/linux-sunxi/sunxi-tools/pull/22 DEFINES += -D_NETBSD_SOURCE endif # 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-meminfo sunxi-pio # Misc tools (of more "exotic" nature) not part of our default build / install MISC_TOOLS = phoenix_info sunxi-nand-image-builder # ARM binaries and images # Note: To use this target, set/adjust CROSS_COMPILE and MKSUNXIBOOT if needed BINFILES = fel-pio.bin jtag-loop.sunxi fel-sdboot.sunxi uart0-helloworld-sdboot.sunxi CROSS_COMPILE ?= arm-none-eabi- MKSUNXIBOOT ?= mksunxiboot DESTDIR ?= PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin .PHONY: all clean tools target-tools install install-tools install-target-tools tools: $(TOOLS) $(FEXC_LINKS) target-tools: $(TARGET_TOOLS) all: tools target-tools misc: $(MISC_TOOLS) binfiles: $(BINFILES) install: install-tools install-all: 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 install-misc: $(MISC_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 version.h *.o *.elf *.sunxi *.bin *.nm *.orig $(TOOLS) $(TARGET_TOOLS) $(MISC_TOOLS): Makefile common.h version.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)` ifeq ($(OS),Windows_NT) # Windows lacks mman.h / mmap() DEFINES += -DNO_MMAP # portable_endian.h relies on winsock2 LIBS += -lws2_32 endif sunxi-fel: fel.c fel-to-spl-thunk.h progress.c progress.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) phoenix_info: phoenix_info.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) %.bin: %.elf $(CROSS_COMPILE)objcopy -O binary $< $@ %.sunxi: %.bin $(MKSUNXIBOOT) $< $@ fel-pio.bin: fel-pio.elf fel-pio.nm ARM_ELF_FLAGS = -Os -marm -fpic -Wall ARM_ELF_FLAGS += -fno-common -fno-builtin -ffreestanding -nostdinc -fno-strict-aliasing ARM_ELF_FLAGS += -mno-thumb-interwork -fno-stack-protector -fno-toplevel-reorder ARM_ELF_FLAGS += -Wstrict-prototypes -Wno-format-nonliteral -Wno-format-security fel-pio.elf: fel-pio.c fel-pio.lds $(CROSS_COMPILE)gcc -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T fel-pio.lds fel-pio.nm: fel-pio.elf $(CROSS_COMPILE)nm $< | grep -v " _" >$@ jtag-loop.elf: jtag-loop.c jtag-loop.lds $(CROSS_COMPILE)gcc -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T jtag-loop.lds -Wl,-N fel-sdboot.elf: fel-sdboot.S fel-sdboot.lds $(CROSS_COMPILE)gcc -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T fel-sdboot.lds -Wl,-N uart0-helloworld-sdboot.elf: uart0-helloworld-sdboot.c uart0-helloworld-sdboot.lds $(CROSS_COMPILE)gcc -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T uart0-helloworld-sdboot.lds -Wl,-N boot_head_sun3i.elf: boot_head.S boot_head.lds $(CROSS_COMPILE)gcc -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T boot_head.lds -Wl,-N -DMACHID=0x1094 boot_head_sun4i.elf: boot_head.S boot_head.lds $(CROSS_COMPILE)gcc -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T boot_head.lds -Wl,-N -DMACHID=0x1008 boot_head_sun5i.elf: boot_head.S boot_head.lds $(CROSS_COMPILE)gcc -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T boot_head.lds -Wl,-N -DMACHID=0x102A sunxi-bootinfo: bootinfo.c # target tools TARGET_CFLAGS = -g -O0 -Wall -Wextra -std=c99 $(DEFINES) -Iinclude/ -static sunxi-pio: pio.c $(CROSS_COMPILE)gcc $(TARGET_CFLAGS) -o $@ $< sunxi-meminfo: meminfo.c $(CROSS_COMPILE)gcc $(TARGET_CFLAGS) -o $@ $< sunxi-script_extractor: script_extractor.c $(CROSS_COMPILE)gcc $(TARGET_CFLAGS) -o $@ $< version.h: @./autoversion.sh > $@ .gitignore: Makefile @for x in $(TOOLS) $(FEXC_LINKS) $(TARGET_TOOLS) version.h '*.o' '*.swp'; do \ echo "$$x"; \ done | sort -V > $@ sunxi-tools-1.4.1/README.md000066400000000000000000000121651300506642400153050ustar00rootroot00000000000000# sunxi-tools [![License](http://img.shields.io/badge/License-GPL-green.svg)](LICENSE.md) [![Build Status](https://travis-ci.org/linux-sunxi/sunxi-tools.svg?branch=master)](https://travis-ci.org/linux-sunxi/sunxi-tools) [![Releases](https://img.shields.io/github/release/linux-sunxi/sunxi-tools.svg)](https://github.com/linux-sunxi/sunxi-tools/releases) Copyright (C) 2012 Alejandro Mery
For a full list of contributors, see [this link](https://github.com/linux-sunxi/sunxi-tools/contributors) or use the command `git shortlog -se --no-merges`. Tools to help hacking Allwinner A10 (aka sun4i) based devices and its successors ([sunxi][]), 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 `sunxi-fexc` to decompile a _script.bin_ blob back into `.fex` format used by Allwinner's SDK to configure the boards. ### fex2bin compatiblity shortcut to call `sunxi-fexc` to compile a `.fex` file into the binary form used by the legacy 3.4 kernel ("linux‑sunxi"). ### sunxi-fel script interface for talking to the FEL USB handler built in to the CPU. You activate [FEL mode] by pushing the _uboot_ / _recovery_ button at poweron. See http://linux-sunxi.org/FEL/USBBoot for a detailed usage guide. ### fel-gpio Simple wrapper (script) around `fel-pio` and `sunxi-fel` to allow GPIO manipulations via FEL ### fel-sdboot ARM native sdcard bootloader forcing the device into FEL mode ### uart0-helloworld-sdboot ARM native sdcard bootloader, which is only printing a short "hello" message to the UART0 serial console. Because it relies on runtime SoC type detection, this single image is bootable on a wide range of Allwinner devices and can be used for testing. Additionally, it may serve as a template/example for developing simple bare metal code (LED blinking and other similar GPIO related things). ### fel-pio ARM native helper (binary) for `fel-gpio` ### sunxi-pio Manipulate PIO register dumps ### sunxi-nand-part Tool for manipulating Allwinner NAND partition tables ### sunxi-nand-image-builder Tool used to create raw NAND images (including boot0 images) ### 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. ### sunxi-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- sunxi-meminfo ### sunxi-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_. To build this, get a toolchain and run: make CROSS_COMPILE=arm-linux-gnueabihf- sunxi-script_extractor --- ## Building Compilation requires the development version of *libusb-1.0* (include header and library) to be installed for `sunxi-fel`. Unless you explicitly pass *LIBUSB_CFLAGS* and *LIBUSB_LIBS* to the make utility, `pkg-config` is also needed. Available build targets: * `make tools` builds tools that are useful on the host. This is what most people will want, and our default target (when simply using `make`). * `make target-tools` builds tools that are intended for the target (Allwinner SoC), using a cross-compiler. The toolchain prefix *CROSS_COMPILE* defaults to `arm-none-eabi-`, adjust it if needed.
_Hint:_ When compiling 'natively' on the target platform you may simply use an empty toolchain prefix here (`make target-tools CROSS_COMPILE=` or `make all CROSS_COMPILE=`). * `make all` builds both *tools* and *target-tools*. * `make install-tools` builds *tools* and then copies/installs them to a filesystem location. The destination is affected by settings for `DESTDIR`, `PREFIX` and possibly `BINDIR`. For details, please refer to the *Makefile*. You may use `make install` as a shortcut for this. * `make install-target-tools` builds *target-tools* and then copies/installs them to a filesystem location selected by `DESTDIR`, `PREFIX` and possibly `BINDIR` - see `make install-tools` above. * `make install-all` builds and installs both *tools* and *target-tools*. * `make misc` builds miscellaneous (host) utilities that are not part of our 'standard' suite. Currently this means `phoenix_info` and `sunxi-nand-image-builder`. There is no dedicated "install" target for these, you need to copy them manuallly. * `make install-misc` builds *misc* and installs the resulting binaries. ## License This software is licensed under the terms of GPLv2+ as defined by the Free Software Foundation, details can be read in the [LICENSE.md](LICENSE.md) file. [sunxi]: http://linux-sunxi.org [fel mode]: http://linux-sunxi.org/FEL sunxi-tools-1.4.1/adb-devprobe.sh000077500000000000000000000033731300506642400167200ustar00rootroot00000000000000#!/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.4.1/autoversion.sh000077500000000000000000000010021300506642400167270ustar00rootroot00000000000000# # This script auto-updates a VERSION string definition. # It outputs informational messages to stderr, while the actual # output (on stdout) can easily be redirected to a file. # LATEST_RELEASE="v1.4.1" if VER=`git describe --tags --dirty --always`; then echo "Setting version information: ${VER}" >&2 else VER=${LATEST_RELEASE} echo "Unable to determine current version (using \"${VER}\" as fallback)" >&2 fi echo >&2 echo "/* Auto-generated information. DO NOT EDIT */" echo "#define VERSION \"${VER}\"" sunxi-tools-1.4.1/bin/000077500000000000000000000000001300506642400145715ustar00rootroot00000000000000sunxi-tools-1.4.1/bin/fel-pio.bin000077500000000000000000000001341300506642400166170ustar00rootroot00000000000000  :S/(2 0S//( sunxi-tools-1.4.1/bin/fel-pio.nm000066400000000000000000000000561300506642400164610ustar00rootroot0000000000000000002000 T pio_to_sram 00002004 T sram_to_pio sunxi-tools-1.4.1/bin/fel-sdboot.sunxi000066400000000000000000000200001300506642400177070ustar00rootroot00000000000000eGON.BT0n' SPL   / sunxi-tools-1.4.1/bin/jtag-loop.sunxi000066400000000000000000000010001300506642400175440ustar00rootroot00000000000000 eGON.BT0dreDDDsunxi-tools-1.4.1/bin/ramboot.scr000066400000000000000000000003661300506642400167520ustar00rootroot00000000000000'Vp}cQe# 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.4.1/bin/ramboot.uboot-sh000066400000000000000000000002561300506642400177210ustar00rootroot00000000000000# 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.4.1/bin/uart0-helloworld-sdboot.sunxi000066400000000000000000000200001300506642400223450ustar00rootroot00000000000000eGON.BT0\ SPL2$!@-@5;"$P20%+1  /$0Q5; ,, /2$ 5;03/@-@@8@-@P8@/R"<R009&0 5$ )$ $ 0"(0 /\80#60Ps/ 00 %6R 0P0Ps//0,0 %6R0PP//00Q60Ps/0360Ps/d0960Ps/D060Ps/$0Z=Ps/0l (l "("/@-P 6D7A7<PP 36434.P 3e4c'$P P ()) P @@- 0    0@ 0/@-@ S T0S@-vPdjX0P D0R  80R$P PeGON.BT0 Hello from Allwinner A10! Allwinner A10s! Allwinner A13! Allwinner A20! Allwinner A31/A31s! Allwinner A64! Allwinner H3! unknown Allwinner SoC! Returning back to FEL. Booted from MMC0, entering an infinite loop. Booted from SPI0, entering an infinite loop. Booted from unknown media, entering an infinite loop. @-fxPPPPP PPS_P P P `HTDH@<<Fsunxi-tools-1.4.1/boot_head.S000066400000000000000000000026071300506642400160760ustar00rootroot00000000000000/* * 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.4.1/boot_head.lds000066400000000000000000000016611300506642400164550ustar00rootroot00000000000000/* * 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) } /DISCARD/ : { *(.dynstr*) } /DISCARD/ : { *(.dynamic*) } /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } /DISCARD/ : { *(.note*) } } sunxi-tools-1.4.1/bootinfo.c000066400000000000000000000306351300506642400160130ustar00rootroot00000000000000/* * (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 "common.h" #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 *)(uintptr_t)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 *UNUSED(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"); } static void usage(const char *cmd) { puts("sunxi-bootinfo " VERSION "\n"); printf("Usage: %s []\n", cmd); printf(" With no given, will read from stdin instead\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) { if (*argv[1] == '-') usage(argv[0]); 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.4.1/common.h000066400000000000000000000026231300506642400154650ustar00rootroot00000000000000/* * 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 */ #include "version.h" /* auto-generated VERSION string */ /** flag 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__) #endif /* _SUNXI_TOOLS_COMMON_H */ sunxi-tools-1.4.1/fel-copy.c000066400000000000000000000027601300506642400157100ustar00rootroot00000000000000/* * (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.4.1/fel-gpio000077500000000000000000000033011300506642400154460ustar00rootroot00000000000000#!/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.4.1/fel-pio.c000066400000000000000000000031151300506642400155200ustar00rootroot00000000000000/* * (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.4.1/fel-pio.lds000066400000000000000000000016551300506642400160670ustar00rootroot00000000000000/* * 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) } /DISCARD/ : { *(.dynstr*) } /DISCARD/ : { *(.dynamic*) } /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } /DISCARD/ : { *(.note*) } } sunxi-tools-1.4.1/fel-sdboot.S000066400000000000000000000042611300506642400162060ustar00rootroot00000000000000/* * Copyright (C) 2016 Bernhard Nortmann * * Based on previous works * Copyright (C) 2016 Siarhei Siamashka * 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 utility stub (bootloader code) to force the device into * FEL mode, by jumping directly to the corresponding (N-)BROM entry point. * * Build instructions: * make fel-sdboot.sunxi * * If needed, adjust CROSS_COMPILE and MKSUNXIBOOT according to your * toolchain, e.g. * make fel-sdboot.sunxi CROSS_COMPILE=armv7a-hardfloat-linux-gnueabi- \ * MKSUNXIBOOT=/usr/local/bin/mksunxiboot * * * Install instructions: * dd if=fel-sdboot.sunxi of=/dev/sdX bs=1024 seek=8 */ SCTRL .req r0 .equ V_BIT, (1 << 13) .equ BROM_ENTRY_LOW, 0x00000020 .equ BROM_ENTRY_HIGH, 0xFFFF0020 /* * In cases where insufficient padding is added by an old mksunxiboot, * _start may be 0x20, which means that the instruction at 0x28 could get * corrupted by the BROM - see https://patchwork.ozlabs.org/patch/622173/ * * Apply a workaround to avoid (= skip over) that memory location. * _main would be at 0x30 in that particular case. With newer (properly * fixed) versions of mksunxiboot, this code ends up at higher addresses * and will be moot, but harmless. */ _start: b _main nop nop nop _main: mrc p15, 0, SCTRL, c1, c0, 0 tst SCTRL, #V_BIT @ test SCTRL.V moveq lr, #BROM_ENTRY_LOW ldrne lr, =BROM_ENTRY_HIGH bx lr sunxi-tools-1.4.1/fel-sdboot.lds000066400000000000000000000016551300506642400165720ustar00rootroot00000000000000/* * 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) } /DISCARD/ : { *(.dynstr*) } /DISCARD/ : { *(.dynamic*) } /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } /DISCARD/ : { *(.note*) } } sunxi-tools-1.4.1/fel-to-spl-thunk.S000066400000000000000000000116231300506642400172610ustar00rootroot00000000000000/* * 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.4.1/fel-to-spl-thunk.h000066400000000000000000000112201300506642400172770ustar00rootroot00000000000000 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.4.1/fel.c000066400000000000000000001636571300506642400147550ustar00rootroot00000000000000/* * 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 . */ #include "common.h" #include "portable_endian.h" #include "progress.h" #include #include #include #include #include #include #include #include #include #include #include #include static const uint16_t AW_USB_VENDOR_ID = 0x1F3A; static const uint16_t AW_USB_PRODUCT_ID = 0xEFE8; /* a helper function to report libusb errors */ void usb_error(int rc, const char *caption, int exitcode) { if (caption) fprintf(stderr, "%s ", caption); #if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102) fprintf(stderr, "ERROR %d: %s\n", rc, libusb_strerror(rc)); #else /* assume that libusb_strerror() is missing in the libusb API */ fprintf(stderr, "ERROR %d\n", rc); #endif if (exitcode != 0) exit(exitcode); } 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, size_t length, bool progress) { /* * With no progress notifications, we'll use the maximum chunk size. * Otherwise, it's useful to lower the size (have more chunks) to get * more frequent status updates. 128 KiB per request seem suitable. */ size_t max_chunk = progress ? 128 * 1024 : AW_USB_MAX_BULK_SEND; size_t chunk; int rc, sent; while (length > 0) { chunk = length < max_chunk ? length : max_chunk; rc = libusb_bulk_transfer(usb, ep, (void *)data, chunk, &sent, timeout); if (rc != 0) usb_error(rc, "usb_bulk_send()", 2); length -= sent; data += sent; if (progress) progress_update(sent); /* notification after each chunk */ } } 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) usb_error(rc, "usb_bulk_recv()", 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 = { .signature = "AWUC", .request = htole16(type), .length = htole32(length), .unknown1 = htole32(0x0c000000) }; req.length2 = req.length; usb_bulk_send(usb, AW_USB_FEL_BULK_EP_OUT, &req, sizeof(req), false); } 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, bool progress) { aw_send_usb_request(usb, AW_USB_WRITE, len); usb_bulk_send(usb, AW_USB_FEL_BULK_EP_OUT, data, len, progress); 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, false); 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 = { .request = htole32(type), .address = htole32(addr), .length = htole32(length) }; aw_usb_write(usb, &req, sizeof(req), false); } 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 0x1689: soc_name="A64"; 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) { aw_send_fel_request(usb, AW_FEL_1_WRITE, offset, len); aw_usb_write(usb, buf, len, false); 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); } /* * This function is a higher-level wrapper for the FEL write functionality. * Unlike aw_fel_write() above - which is reserved for internal use - this * routine is meant to be called from "user" code, and supports (= allows) * progress callbacks. * The return value represents elapsed time in seconds (needed for execution). */ double aw_write_buffer(libusb_device_handle *usb, void *buf, uint32_t offset, size_t len, bool progress) { /* 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, (uint32_t)(offset + len), uboot_entry, uboot_entry + uboot_size); exit(1); } double start = gettime(); aw_send_fel_request(usb, AW_FEL_1_WRITE, offset, len); aw_usb_write(usb, buf, len, progress); aw_read_fel_status(usb); return gettime() - start; } 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("%08zx: ", offset + j); for (i = 0; i < 16; i++) { if (j + i < size) printf("%02x ", buf[j+i]); else printf("__ "); } putchar(' '); for (i = 0; i < 16; i++) { if (j + i >= size) putchar('.'); else putchar(isprint(buf[j+i]) ? buf[j+i] : '.'); } putchar('\n'); } } unsigned int file_size(const char *filename) { struct stat st; if (stat(filename, &st) != 0) { fprintf(stderr, "stat() error on file \"%s\": %s\n", filename, strerror(errno)); exit(1); } if (!S_ISREG(st.st_mode)) { fprintf(stderr, "error: \"%s\" is not a regular file\n", filename); exit(1); } return st.st_size; } 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 (true) { 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_write_buffer(usb, buf, offset, size, false); } /* * 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. * * Also for performance reasons, we optionally want to have MMU enabled with * optimal section attributes configured (the code from the BROM should use * I-cache, writing data to the DRAM area should use write combining). The * reason is that the BROM FEL protocol implementation moves data using the * CPU somewhere on the performance critical path when transferring data over * USB. The older SoC variants (A10/A13/A20/A31/A23) already have MMU enabled * and we only need to adjust section attributes. The BROM in newer SoC variants * (A33/A83T/H3) doesn't enable MMU anymore, so we need to find some 16K of * spare space in SRAM to place the translation table there and specify it as * the 'mmu_tt_addr' field in the 'soc_sram_info' structure. The 'mmu_tt_addr' * address must be 16K aligned. */ 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 */ bool needs_l2en; /* Set the L2EN bit */ uint32_t mmu_tt_addr; /* MMU translation table address */ uint32_t sid_addr; /* base address for SID_KEY[0-3] registers */ uint32_t rvbar_reg; /* MMIO address of RVBARADDR0_L register */ 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 0x7D00-0x7FFF contain something * importantant too (overwriting them kills FEL). On A10/A13/A20 we can use * the SRAM sections A3/A4 (0x8000-0xBFFF) for this purpose. */ sram_swap_buffers a10_a13_a20_sram_swap_buffers[] = { /* 0x1C00-0x1FFF (IRQ stack) */ { .buf1 = 0x01C00, .buf2 = 0xA400, .size = 0x0400 }, /* 0x5C00-0x6FFF (Stack) */ { .buf1 = 0x05C00, .buf2 = 0xA800, .size = 0x1400 }, /* 0x7C00-0x7FFF (Something important) */ { .buf1 = 0x07C00, .buf2 = 0xBC00, .size = 0x0400 }, { .size = 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 B at 0x20000-0x2FFFF instead. In the FEL mode, * the MMU translation table is allocated by the BROM at 0x20000. But we can * also safely use it as the backup storage because the MMU is temporarily * disabled during the time of the SPL execution. */ sram_swap_buffers a31_sram_swap_buffers[] = { { .buf1 = 0x01800, .buf2 = 0x20000, .size = 0x800 }, { .buf1 = 0x05C00, .buf2 = 0x20800, .size = 0x8000 - 0x5C00 }, { .size = 0 } /* End of the table */ }; /* * A64 has 32KiB of SRAM A at 0x10000 and a large SRAM C at 0x18000. SRAM A * and SRAM C reside in the address space back-to-back without any gaps, thus * representing a singe large contiguous area. Everything is the same as on * A10/A13/A20, but just shifted by 0x10000. */ sram_swap_buffers a64_sram_swap_buffers[] = { /* 0x11C00-0x11FFF (IRQ stack) */ { .buf1 = 0x11C00, .buf2 = 0x1A400, .size = 0x0400 }, /* 0x15C00-0x16FFF (Stack) */ { .buf1 = 0x15C00, .buf2 = 0x1A800, .size = 0x1400 }, /* 0x17C00-0x17FFF (Something important) */ { .buf1 = 0x17C00, .buf2 = 0x1BC00, .size = 0x0400 }, { .size = 0 } /* End of the table */ }; /* * Use the SRAM section at 0x44000 as the backup storage. 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 ar100_abusing_sram_swap_buffers[] = { { .buf1 = 0x01800, .buf2 = 0x44000, .size = 0x800 }, { .buf1 = 0x05C00, .buf2 = 0x44800, .size = 0x8000 - 0x5C00 }, { .size = 0 } /* End of the table */ }; /* * A80 has 40KiB SRAM A1 at 0x10000 where the SPL has to be loaded to. The * secure SRAM B at 0x20000 is used as backup area for FEL stacks and data. */ sram_swap_buffers a80_sram_swap_buffers[] = { { .buf1 = 0x11800, .buf2 = 0x20000, .size = 0x800 }, { .buf1 = 0x15400, .buf2 = 0x20800, .size = 0x18000 - 0x15400 }, { .size = 0 } /* End of the table */ }; soc_sram_info soc_sram_info_table[] = { { .soc_id = 0x1623, /* Allwinner A10 */ .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .needs_l2en = true, .sid_addr = 0x01C23800, }, { .soc_id = 0x1625, /* Allwinner A13 */ .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .needs_l2en = true, .sid_addr = 0x01C23800, }, { .soc_id = 0x1651, /* Allwinner A20 */ .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .sid_addr = 0x01C23800, }, { .soc_id = 0x1650, /* Allwinner A23 */ .scratch_addr = 0x1000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sid_addr = 0x01C23800, }, { .soc_id = 0x1633, /* Allwinner A31 */ .scratch_addr = 0x1000, .thunk_addr = 0x22E00, .thunk_size = 0x200, .swap_buffers = a31_sram_swap_buffers, }, { .soc_id = 0x1667, /* Allwinner A33 */ .scratch_addr = 0x1000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sid_addr = 0x01C23800, }, { .soc_id = 0x1689, /* Allwinner A64 */ .spl_addr = 0x10000, .scratch_addr = 0x11000, .thunk_addr = 0x1A200, .thunk_size = 0x200, .swap_buffers = a64_sram_swap_buffers, .sid_addr = 0x01C14200, .rvbar_reg = 0x017000A0, }, { .soc_id = 0x1673, /* Allwinner A83T */ .scratch_addr = 0x1000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sid_addr = 0x01C14200, }, { .soc_id = 0x1680, /* Allwinner H3 */ .scratch_addr = 0x1000, .mmu_tt_addr = 0x8000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .sid_addr = 0x01C14200, }, { .soc_id = 0x1639, /* Allwinner A80 */ .spl_addr = 0x10000, .scratch_addr = 0x11000, .thunk_addr = 0x23400, .thunk_size = 0x200, .swap_buffers = a80_sram_swap_buffers, }, { .swap_buffers = NULL } /* 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 }, { .size = 0 } /* End of the table */ }; soc_sram_info generic_sram_info = { .scratch_addr = 0x1000, .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 uint32_t aw_read_arm_cp_reg(libusb_device_handle *usb, soc_sram_info *sram_info, uint32_t coproc, uint32_t opc1, uint32_t crn, uint32_t crm, uint32_t opc2) { uint32_t val = 0; uint32_t opcode = 0xEE000000 | (1 << 20) | (1 << 4) | ((opc1 & 7) << 21) | ((crn & 15) << 16) | ((coproc & 15) << 8) | ((opc2 & 7) << 5) | (crm & 15); uint32_t arm_code[] = { htole32(opcode), /* mrc coproc, opc1, r0, crn, crm, opc2 */ htole32(0xe58f0000), /* str r0, [pc] */ 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 + 12, &val, sizeof(val)); return le32toh(val); } void aw_write_arm_cp_reg(libusb_device_handle *usb, soc_sram_info *sram_info, uint32_t coproc, uint32_t opc1, uint32_t crn, uint32_t crm, uint32_t opc2, uint32_t val) { uint32_t opcode = 0xEE000000 | (0 << 20) | (1 << 4) | ((opc1 & 7) << 21) | ((crn & 15) << 16) | ((coproc & 15) << 8) | ((opc2 & 7) << 5) | (crm & 15); uint32_t arm_code[] = { htole32(0xe59f000c), /* ldr r0, [pc, #12] */ htole32(opcode), /* mcr coproc, opc1, r0, crn, crm, opc2 */ htole32(0xf57ff04f), /* dsb sy */ htole32(0xf57ff06f), /* isb sy */ htole32(0xe12fff1e), /* bx lr */ htole32(val) }; aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); } /* multiple "readl" from sequential addresses to a destination buffer */ void aw_fel_readl_n(libusb_device_handle *usb, uint32_t addr, uint32_t *dst, size_t count) { soc_sram_info *sram_info = aw_fel_get_sram_info(usb); uint32_t val; uint32_t arm_code[] = { htole32(0xe59f0010), /* ldr r0, [pc, #16] */ htole32(0xe5901000), /* ldr r1, [r0] */ htole32(0xe58f100c), /* str r1, [pc, #12] */ htole32(0xe2800004), /* add r0, r0, #4 */ htole32(0xe58f0000), /* str r0, [pc] */ htole32(0xe12fff1e), /* bx lr */ htole32(addr), /* value goes here */ }; /* scratch buffer setup: transfers ARM code and also sets the addr */ aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); while (count-- > 0) { /* * Since the scratch code auto-increments addr, we can simply * execute it repeatedly for sequential "readl"s; retrieving * one uint32_t each time. */ aw_fel_execute(usb, sram_info->scratch_addr); aw_fel_read(usb, sram_info->scratch_addr + 28, &val, sizeof(val)); *dst++ = le32toh(val); } } /* "readl" of a single value */ uint32_t aw_fel_readl(libusb_device_handle *usb, uint32_t addr) { uint32_t val; aw_fel_readl_n(usb, addr, &val, 1); return val; } /* multiple "writel" from a source buffer to sequential addresses */ void aw_fel_writel_n(libusb_device_handle *usb, uint32_t addr, uint32_t *src, size_t count) { if (count == 0) return; /* on zero count, do not access *src at all */ soc_sram_info *sram_info = aw_fel_get_sram_info(usb); uint32_t arm_code[] = { htole32(0xe59f0010), /* ldr r0, [pc, #16] */ htole32(0xe59f1010), /* ldr r1, [pc, #16] */ htole32(0xe5801000), /* str r1, [r0] */ htole32(0xe2800004), /* add r0, r0, #4 */ htole32(0xe58f0000), /* str r0, [pc] */ htole32(0xe12fff1e), /* bx lr */ htole32(addr), htole32(*src++) }; /* scratch buffer setup: transfers ARM code, addr and first value */ aw_fel_write(usb, arm_code, sram_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(usb, sram_info->scratch_addr); /* stores first value */ while (--count > 0) { /* * Subsequent transfers only need to set up the next value * to store (since the scratch code auto-increments addr). */ uint32_t val = htole32(*src++); aw_fel_write(usb, &val, sram_info->scratch_addr + 28, sizeof(val)); aw_fel_execute(usb, sram_info->scratch_addr); } } /* "writel" of a single value */ void aw_fel_writel(libusb_device_handle *usb, uint32_t addr, uint32_t val) { aw_fel_writel_n(usb, addr, &val, 1); } void aw_fel_print_sid(libusb_device_handle *usb) { soc_sram_info *soc_info = aw_fel_get_sram_info(usb); if (soc_info->sid_addr) { pr_info("SID key (e-fuses) at 0x%08X\n", soc_info->sid_addr); uint32_t key[4]; aw_fel_readl_n(usb, soc_info->sid_addr, key, 4); unsigned int i; /* output SID in "xxxxxxxx:xxxxxxxx:xxxxxxxx:xxxxxxxx" format */ for (i = 0; i <= 3; i++) printf("%08x%c", key[i], i < 3 ? ':' : '\n'); } else { printf("SID registers for your SoC (id=%04X) are unknown or inaccessible.\n", soc_info->soc_id); } } 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) { return aw_read_arm_cp_reg(usb, sram_info, 15, 0, 2, 0, 0); } uint32_t aw_get_ttbcr(libusb_device_handle *usb, soc_sram_info *sram_info) { return aw_read_arm_cp_reg(usb, sram_info, 15, 0, 2, 0, 2); } uint32_t aw_get_dacr(libusb_device_handle *usb, soc_sram_info *sram_info) { return aw_read_arm_cp_reg(usb, sram_info, 15, 0, 3, 0, 0); } uint32_t aw_get_sctlr(libusb_device_handle *usb, soc_sram_info *sram_info) { return aw_read_arm_cp_reg(usb, sram_info, 15, 0, 1, 0, 0); } void aw_set_ttbr0(libusb_device_handle *usb, soc_sram_info *sram_info, uint32_t ttbr0) { return aw_write_arm_cp_reg(usb, sram_info, 15, 0, 2, 0, 0, ttbr0); } void aw_set_ttbcr(libusb_device_handle *usb, soc_sram_info *sram_info, uint32_t ttbcr) { return aw_write_arm_cp_reg(usb, sram_info, 15, 0, 2, 0, 2, ttbcr); } void aw_set_dacr(libusb_device_handle *usb, soc_sram_info *sram_info, uint32_t dacr) { aw_write_arm_cp_reg(usb, sram_info, 15, 0, 3, 0, 0, dacr); } void aw_set_sctlr(libusb_device_handle *usb, soc_sram_info *sram_info, uint32_t sctlr) { aw_write_arm_cp_reg(usb, sram_info, 15, 0, 1, 0, 0, sctlr); } /* * Reconstruct the same MMU translation table as used by the A20 BROM. * We are basically reverting the changes, introduced in newer SoC * variants. This works fine for the SoC variants with the memory * layout similar to A20 (the SRAM is in the first megabyte of the * address space and the BROM is in the last megabyte of the address * space). */ uint32_t *aw_generate_mmu_translation_table(void) { uint32_t *tt = malloc(4096 * sizeof(uint32_t)); uint32_t i; /* * Direct mapping using 1MB sections with TEXCB=00000 (Strongly * ordered) for all memory except the first and the last sections, * which have TEXCB=00100 (Normal). Domain bits are set to 1111 * and AP bits are set to 11, but this is mostly irrelevant. */ for (i = 0; i < 4096; i++) tt[i] = 0x00000DE2 | (i << 20); tt[0x000] |= 0x1000; tt[0xFFF] |= 0x1000; return tt; } uint32_t *aw_backup_and_disable_mmu(libusb_device_handle *usb, soc_sram_info *sram_info) { uint32_t *tt = NULL; uint32_t sctlr, ttbr0, ttbcr, dacr; 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 */ }; /* * Below are some checks for the register values, which are known * to be initialized in this particular way by the existing BROM * implementations. We don't strictly need them to exactly match, * but still have these safety guards in place in order to detect * and review any potential configuration changes in future SoC * variants (if one of these checks fails, then it is not a serious * problem but more likely just an indication that one of these * checks needs to be relaxed). */ /* Basically, ignore M/Z/I/V/UNK bits and expect no TEX remap */ sctlr = aw_get_sctlr(usb, sram_info); if ((sctlr & ~((0x7 << 11) | (1 << 6) | 1)) != 0x00C50038) { fprintf(stderr, "Unexpected SCTLR (%08X)\n", sctlr); exit(1); } if (!(sctlr & 1)) { pr_info("MMU is not enabled by BROM\n"); return NULL; } dacr = aw_get_dacr(usb, sram_info); if (dacr != 0x55555555) { fprintf(stderr, "Unexpected DACR (%08X)\n", dacr); exit(1); } ttbcr = aw_get_ttbcr(usb, sram_info); if (ttbcr != 0x00000000) { fprintf(stderr, "Unexpected TTBCR (%08X)\n", ttbcr); exit(1); } ttbr0 = aw_get_ttbr0(usb, sram_info); 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); if (!tt && sram_info->mmu_tt_addr) { if (sram_info->mmu_tt_addr & 0x3FFF) { fprintf(stderr, "SPL: 'mmu_tt_addr' must be 16K aligned\n"); exit(1); } pr_info("Generating the new MMU translation table at 0x%08X\n", sram_info->mmu_tt_addr); /* * These settings are used by the BROM in A10/A13/A20 and * we replicate them here when enabling the MMU. The DACR * value 0x55555555 means that accesses are checked against * the permission bits in the translation tables for all * domains. The TTBCR value 0x00000000 means that the short * descriptor translation table format is used, TTBR0 is used * for all the possible virtual addresses (N=0) and that the * translation table must be aligned at a 16K boundary. */ aw_set_dacr(usb, sram_info, 0x55555555); aw_set_ttbcr(usb, sram_info, 0x00000000); aw_set_ttbr0(usb, sram_info, sram_info->mmu_tt_addr); tt = aw_generate_mmu_translation_table(); } 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_write_buffer(usb, buf + HEADER_SIZE, load_addr, data_size, false); /* 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); free(buf); } /* * 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, uint32_t uEnv_length) { 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, uEnv length = %u\n", script_address, uEnv_length); uint32_t transfer[] = { htole32(script_address), htole32(uEnv_length) }; aw_fel_write(usb, transfer, sram_info->spl_addr + 0x18, sizeof(transfer)); } } 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; } /* * This function stores a given entry point to the RVBAR address for CPU0, * and then writes the Reset Management Register to request a warm boot. * It is useful with some AArch64 transitions, e.g. when passing control to * ARM Trusted Firmware (ATF) during the boot process of Pine64. * * The code was inspired by * https://github.com/apritzel/u-boot/commit/fda6bd1bf285c44f30ea15c7e6231bf53c31d4a8 */ void aw_rmr_request(libusb_device_handle *usb, uint32_t entry_point, bool aarch64) { soc_sram_info *soc_info = aw_fel_get_sram_info(usb); if (!soc_info->rvbar_reg) { fprintf(stderr, "ERROR: Can't issue RMR request!\n" "RVBAR is not supported or unknown for your SoC (id=%04X).\n", soc_info->soc_id); return; } uint32_t rmr_mode = (1 << 1) | (aarch64 ? 1 : 0); /* RR, AA64 flag */ uint32_t arm_code[] = { htole32(0xe59f0028), /* ldr r0, [rvbar_reg] */ htole32(0xe59f1028), /* ldr r1, [entry_point] */ htole32(0xe5801000), /* str r1, [r0] */ htole32(0xf57ff04f), /* dsb sy */ htole32(0xf57ff06f), /* isb sy */ htole32(0xe59f101c), /* ldr r1, [rmr_mode] */ htole32(0xee1c0f50), /* mrc 15, 0, r0, cr12, cr0, {2}*/ htole32(0xe1800001), /* orr r0, r0, r1 */ htole32(0xee0c0f50), /* mcr 15, 0, r0, cr12, cr0, {2}*/ htole32(0xf57ff06f), /* isb sy */ htole32(0xe320f003), /* loop: wfi */ htole32(0xeafffffd), /* b */ htole32(soc_info->rvbar_reg), htole32(entry_point), htole32(rmr_mode) }; /* scratch buffer setup: transfers ARM code and parameter values */ aw_fel_write(usb, arm_code, soc_info->scratch_addr, sizeof(arm_code)); /* execute the thunk code (triggering a warm reset on the SoC) */ pr_info("Store entry point 0x%08X to RVBAR 0x%08X, " "and request warm reset with RMR mode %u...", entry_point, soc_info->rvbar_reg, rmr_mode); aw_fel_execute(usb, soc_info->scratch_addr); pr_info(" done.\n"); } /* check buffer for magic "#=uEnv", indicating uEnv.txt compatible format */ static bool is_uEnv(void *buffer, size_t size) { if (size <= 6) return false; /* insufficient size */ return memcmp(buffer, "#=uEnv", 6) == 0; } /* private helper function, gets used for "write*" and "multi*" transfers */ static unsigned int file_upload(libusb_device_handle *handle, size_t count, size_t argc, char **argv, progress_cb_t callback) { if (argc < count * 2) { fprintf(stderr, "error: too few arguments for uploading %zu files\n", count); exit(1); } /* get all file sizes, keeping track of total bytes */ size_t size = 0; unsigned int i; for (i = 0; i < count; i++) size += file_size(argv[i * 2 + 1]); progress_start(callback, size); /* set total size and progress callback */ /* now transfer each file in turn */ for (i = 0; i < count; i++) { void *buf = load_file(argv[i * 2 + 1], &size); if (size > 0) { uint32_t offset = strtoul(argv[i * 2], NULL, 0); aw_write_buffer(handle, buf, offset, size, callback != NULL); /* If we 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, 0); if (is_uEnv(buf, size)) /* uEnv-style data */ pass_fel_information(handle, offset, size); } free(buf); } return i; /* return number of files that were processed */ } /* open libusb handle to desired FEL device */ static libusb_device_handle *open_fel_device(int busnum, int devnum, uint16_t vendor_id, uint16_t product_id) { libusb_device_handle *result = NULL; if (busnum < 0 || devnum < 0) { /* With the default values (busnum -1, devnum -1) we don't care * for a specific USB device; so let libusb open the first * device that matches VID/PID. */ result = libusb_open_device_with_vid_pid(NULL, vendor_id, product_id); if (!result) { 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); } return result; } /* look for specific bus and device number */ pr_info("Selecting USB Bus %03d Device %03d\n", busnum, devnum); bool found = false; ssize_t rc, i; libusb_device **list; rc = libusb_get_device_list(NULL, &list); if (rc < 0) usb_error(rc, "libusb_get_device_list()", 1); for (i = 0; i < rc; i++) { if (libusb_get_bus_number(list[i]) == busnum && libusb_get_device_address(list[i]) == devnum) { found = true; /* bus:devnum matched */ struct libusb_device_descriptor desc; libusb_get_device_descriptor(list[i], &desc); if (desc.idVendor != vendor_id || desc.idProduct != product_id) { fprintf(stderr, "ERROR: Bus %03d Device %03d not a FEL device " "(expected %04x:%04x, got %04x:%04x)\n", busnum, devnum, vendor_id, product_id, desc.idVendor, desc.idProduct); exit(1); } /* open handle to this specific device (incrementing its refcount) */ rc = libusb_open(list[i], &result); if (rc != 0) usb_error(rc, "libusb_open()", 1); break; } } libusb_free_device_list(list, true); if (!found) { fprintf(stderr, "ERROR: Bus %03d Device %03d not found in libusb device list\n", busnum, devnum); exit(1); } return result; } int main(int argc, char **argv) { bool uboot_autostart = false; /* flag for "uboot" command = U-Boot autostart */ bool pflag_active = false; /* -p switch, causing "write" to output progress */ libusb_device_handle *handle; int busnum = -1, devnum = -1; #if defined(__linux__) int iface_detached = -1; #endif if (argc <= 1) { puts("sunxi-fel " VERSION "\n"); printf("Usage: %s [options] command arguments... [command...]\n" " -v, --verbose Verbose logging\n" " -p, --progress \"write\" transfers show a progress bar\n" " -d, --dev bus:devnum Use specific USB bus and device number\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" " reset64 address RMR request for AArch64 warm boot\n" " readl address Read 32-bit value from device memory\n" " writel address value Write 32-bit value to device memory\n" " read address length file Write memory contents into file\n" " write address file Store file contents into memory\n" " write-with-progress addr file \"write\" with progress bar\n" " write-with-gauge addr file Output progress for \"dialog --gauge\"\n" " write-with-xgauge addr file Extended gauge output (updates prompt)\n" " multi[write] # addr file ... \"write-with-progress\" multiple files,\n" " sharing a common progress status\n" " multi[write]-with-gauge ... like their \"write-with-*\" counterpart,\n" " multi[write]-with-xgauge ... but following the 'multi' syntax:\n" " <#> addr file [addr file [...]]\n" " echo-gauge \"some text\" Update prompt/caption for gauge output\n" " ver[sion] Show BROM version\n" " sid Retrieve and output 128-bit SID key\n" " clear address length Clear memory\n" " fill address length value Fill memory\n" , argv[0] ); exit(0); } /* process all "prefix"-type arguments first */ while (argc > 1) { if (strcmp(argv[1], "--verbose") == 0 || strcmp(argv[1], "-v") == 0) verbose = true; else if (strcmp(argv[1], "--progress") == 0 || strcmp(argv[1], "-p") == 0) pflag_active = true; else if (strncmp(argv[1], "--dev", 5) == 0 || strncmp(argv[1], "-d", 2) == 0) { char *dev_arg = argv[1]; dev_arg += strspn(dev_arg, "-dev="); /* skip option chars, ignore '=' */ if (*dev_arg == 0 && argc > 2) { /* at end of argument, use the next one instead */ dev_arg = argv[2]; argc -= 1; argv += 1; } if (sscanf(dev_arg, "%d:%d", &busnum, &devnum) != 2 || busnum <= 0 || devnum <= 0) { fprintf(stderr, "ERROR: Expected 'bus:devnum', got '%s'.\n", dev_arg); exit(1); } } else break; /* no valid (prefix) option detected, exit loop */ argc -= 1; argv += 1; } int rc = libusb_init(NULL); assert(rc == 0); handle = open_fel_device(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID); assert(handle != NULL); 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); } 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 (strcmp(argv[1], "readl") == 0 && argc > 2) { printf("0x%08x\n", aw_fel_readl(handle, strtoul(argv[2], NULL, 0))); skip = 2; } else if (strcmp(argv[1], "writel") == 0 && argc > 3) { aw_fel_writel(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 (strcmp(argv[1], "reset64") == 0 && argc > 2) { aw_rmr_request(handle, strtoul(argv[2], NULL, 0), true); /* Cancel U-Boot autostart, and stop processing args */ uboot_autostart = false; break; } else if (strncmp(argv[1], "ver", 3) == 0) { aw_fel_print_version(handle); } else if (strcmp(argv[1], "sid") == 0) { aw_fel_print_sid(handle); } else if (strcmp(argv[1], "write") == 0 && argc > 3) { skip += 2 * file_upload(handle, 1, argc - 2, argv + 2, pflag_active ? progress_bar : NULL); } else if (strcmp(argv[1], "write-with-progress") == 0 && argc > 3) { skip += 2 * file_upload(handle, 1, argc - 2, argv + 2, progress_bar); } else if (strcmp(argv[1], "write-with-gauge") == 0 && argc > 3) { skip += 2 * file_upload(handle, 1, argc - 2, argv + 2, progress_gauge); } else if (strcmp(argv[1], "write-with-xgauge") == 0 && argc > 3) { skip += 2 * file_upload(handle, 1, argc - 2, argv + 2, progress_gauge_xxx); } else if ((strcmp(argv[1], "multiwrite") == 0 || strcmp(argv[1], "multi") == 0) && argc > 4) { size_t count = strtoul(argv[2], NULL, 0); /* file count */ skip = 2 + 2 * file_upload(handle, count, argc - 3, argv + 3, progress_bar); } else if ((strcmp(argv[1], "multiwrite-with-gauge") == 0 || strcmp(argv[1], "multi-with-gauge") == 0) && argc > 4) { size_t count = strtoul(argv[2], NULL, 0); /* file count */ skip = 2 + 2 * file_upload(handle, count, argc - 3, argv + 3, progress_gauge); } else if ((strcmp(argv[1], "multiwrite-with-xgauge") == 0 || strcmp(argv[1], "multi-with-xgauge") == 0) && argc > 4) { size_t count = strtoul(argv[2], NULL, 0); /* file count */ skip = 2 + 2 * file_upload(handle, count, argc - 3, argv + 3, progress_gauge_xxx); } else if ((strcmp(argv[1], "echo-gauge") == 0) && argc > 2) { skip = 2; printf("XXX\n0\n%s\nXXX\n", argv[2]); fflush(stdout); } 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]); uboot_autostart = (uboot_entry > 0 && uboot_size > 0); if (!uboot_autostart) 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_autostart) { pr_info("Starting U-Boot (0x%08X).\n", uboot_entry); aw_fel_execute(handle, uboot_entry); } libusb_release_interface(handle, 0); #if defined(__linux__) if (iface_detached >= 0) libusb_attach_kernel_driver(handle, iface_detached); #endif libusb_close(handle); libusb_exit(NULL); return 0; } sunxi-tools-1.4.1/fexc.c000066400000000000000000000167501300506642400151230ustar00rootroot00000000000000/* * 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 #ifndef NO_MMAP #include #endif #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; #ifndef NO_MMAP } 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; #endif } 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); #ifndef NO_MMAP else if (munmap(bin, bin_size) == -1) { pr_err("%s: %s: %s\n", filename, "munmap", strerror(errno)); } #endif 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) { fputs("sunxi-fexc " VERSION "\n\n", stderr); 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?"; if (app_mode != 0) opt_string += 4; /* disallow -I and -O */ 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.4.1/fexc.h000066400000000000000000000016431300506642400151230ustar00rootroot00000000000000/* * 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.4.1/include/000077500000000000000000000000001300506642400154445ustar00rootroot00000000000000sunxi-tools-1.4.1/include/list.h000066400000000000000000000042241300506642400165720ustar00rootroot00000000000000/* * 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_LIST_H #define _SUNXI_TOOLS_LIST_H /* * 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_LIST_H */ sunxi-tools-1.4.1/include/portable_endian.h000066400000000000000000000056511300506642400207520ustar00rootroot00000000000000// "License": Public Domain // I, Mathias Panzenböck, place this file hereby into the public domain. Use it at your own risk for whatever you like. // In case there are jurisdictions that don't support putting things in the public domain you can also consider it to // be "dual licensed" under the BSD, MIT and Apache licenses, if you want to. This code is trivial anyway. Consider it // an example on how to get the endian conversion functions on different platforms. #ifndef PORTABLE_ENDIAN_H__ #define PORTABLE_ENDIAN_H__ #if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) # define __WINDOWS__ #endif #if defined(__linux__) || defined(__CYGWIN__) # include #elif defined(__APPLE__) # include # define htobe16(x) OSSwapHostToBigInt16(x) # define htole16(x) OSSwapHostToLittleInt16(x) # define be16toh(x) OSSwapBigToHostInt16(x) # define le16toh(x) OSSwapLittleToHostInt16(x) # define htobe32(x) OSSwapHostToBigInt32(x) # define htole32(x) OSSwapHostToLittleInt32(x) # define be32toh(x) OSSwapBigToHostInt32(x) # define le32toh(x) OSSwapLittleToHostInt32(x) # define htobe64(x) OSSwapHostToBigInt64(x) # define htole64(x) OSSwapHostToLittleInt64(x) # define be64toh(x) OSSwapBigToHostInt64(x) # define le64toh(x) OSSwapLittleToHostInt64(x) # define __BYTE_ORDER BYTE_ORDER # define __BIG_ENDIAN BIG_ENDIAN # define __LITTLE_ENDIAN LITTLE_ENDIAN # define __PDP_ENDIAN PDP_ENDIAN #elif defined(__OpenBSD__) || defined(__FreeBSD__) # include #elif defined(__NetBSD__) || defined(__DragonFly__) # include # define be16toh(x) betoh16(x) # define le16toh(x) letoh16(x) # define be32toh(x) betoh32(x) # define le32toh(x) letoh32(x) # define be64toh(x) betoh64(x) # define le64toh(x) letoh64(x) #elif defined(__WINDOWS__) # include # include # if BYTE_ORDER == LITTLE_ENDIAN # define htobe16(x) htons(x) # define htole16(x) (x) # define be16toh(x) ntohs(x) # define le16toh(x) (x) # define htobe32(x) htonl(x) # define htole32(x) (x) # define be32toh(x) ntohl(x) # define le32toh(x) (x) # define htobe64(x) htonll(x) # define htole64(x) (x) # define be64toh(x) ntohll(x) # define le64toh(x) (x) # elif BYTE_ORDER == BIG_ENDIAN /* that would be xbox 360 */ # define htobe16(x) (x) # define htole16(x) __builtin_bswap16(x) # define be16toh(x) (x) # define le16toh(x) __builtin_bswap16(x) # define htobe32(x) (x) # define htole32(x) __builtin_bswap32(x) # define be32toh(x) (x) # define le32toh(x) __builtin_bswap32(x) # define htobe64(x) (x) # define htole64(x) __builtin_bswap64(x) # define be64toh(x) (x) # define le64toh(x) __builtin_bswap64(x) # else # error byte order not supported # endif # define __BYTE_ORDER BYTE_ORDER # define __BIG_ENDIAN BIG_ENDIAN # define __LITTLE_ENDIAN LITTLE_ENDIAN # define __PDP_ENDIAN PDP_ENDIAN #else # error platform not supported #endif #endif sunxi-tools-1.4.1/include/types.h000066400000000000000000000023541300506642400167650ustar00rootroot00000000000000/* * (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.4.1/jtag-loop.S000066400000000000000000000024601300506642400160430ustar00rootroot00000000000000/* * (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.4.1/jtag-loop.c000066400000000000000000000024731300506642400160670ustar00rootroot00000000000000/* * (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.4.1/jtag-loop.lds000066400000000000000000000016551300506642400164300ustar00rootroot00000000000000/* * 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) } /DISCARD/ : { *(.dynstr*) } /DISCARD/ : { *(.dynamic*) } /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } /DISCARD/ : { *(.note*) } } sunxi-tools-1.4.1/meminfo.c000066400000000000000000000461751300506642400156340ustar00rootroot00000000000000/* * 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 #include "common.h" 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 = { .baseaddr = 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) { puts("sunxi-meminfo " VERSION "\n"); 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.4.1/nand-common.h000066400000000000000000000021671300506642400164060ustar00rootroot00000000000000/* * (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.4.1/nand-image-builder.c000066400000000000000000000703521300506642400176200ustar00rootroot00000000000000/* * Generic binary BCH encoding/decoding library * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. * * For the BCH implementation: * * Copyright © 2011 Parrot S.A. * * Author: Ivan Djelic * * See also: * http://lxr.free-electrons.com/source/lib/bch.c * * For the randomizer and image builder implementation: * * Copyright © 2016 NextThing Co. * Copyright © 2016 Free Electrons * * Author: Boris Brezillon * */ #include #include #include #include #include #include #include "common.h" #include "portable_endian.h" #if defined(CONFIG_BCH_CONST_PARAMS) #define GF_M(_p) (CONFIG_BCH_CONST_M) #define GF_T(_p) (CONFIG_BCH_CONST_T) #define GF_N(_p) ((1 << (CONFIG_BCH_CONST_M))-1) #else #define GF_M(_p) ((_p)->m) #define GF_T(_p) ((_p)->t) #define GF_N(_p) ((_p)->n) #endif #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) #define BCH_ECC_WORDS(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 32) #define BCH_ECC_BYTES(_p) DIV_ROUND_UP(GF_M(_p)*GF_T(_p), 8) #ifndef dbg #define dbg(_fmt, args...) do {} while (0) #endif #define cpu_to_be32 htobe32 #define kfree free #define BCH_PRIMITIVE_POLY 0x5803 struct image_info { int ecc_strength; int ecc_step_size; int page_size; int oob_size; int usable_page_size; int eraseblock_size; int scramble; int boot0; off_t offset; const char *source; const char *dest; }; /** * struct bch_control - BCH control structure * @m: Galois field order * @n: maximum codeword size in bits (= 2^m-1) * @t: error correction capability in bits * @ecc_bits: ecc exact size in bits, i.e. generator polynomial degree (<=m*t) * @ecc_bytes: ecc max size (m*t bits) in bytes * @a_pow_tab: Galois field GF(2^m) exponentiation lookup table * @a_log_tab: Galois field GF(2^m) log lookup table * @mod8_tab: remainder generator polynomial lookup tables * @ecc_buf: ecc parity words buffer * @ecc_buf2: ecc parity words buffer * @xi_tab: GF(2^m) base for solving degree 2 polynomial roots * @syn: syndrome buffer * @cache: log-based polynomial representation buffer * @elp: error locator polynomial * @poly_2t: temporary polynomials of degree 2t */ struct bch_control { unsigned int m; unsigned int n; unsigned int t; unsigned int ecc_bits; unsigned int ecc_bytes; /* private: */ uint16_t *a_pow_tab; uint16_t *a_log_tab; uint32_t *mod8_tab; uint32_t *ecc_buf; uint32_t *ecc_buf2; unsigned int *xi_tab; unsigned int *syn; int *cache; struct gf_poly *elp; struct gf_poly *poly_2t[4]; }; static int fls(int x) { int r = 32; if (!x) return 0; if (!(x & 0xffff0000u)) { x <<= 16; r -= 16; } if (!(x & 0xff000000u)) { x <<= 8; r -= 8; } if (!(x & 0xf0000000u)) { x <<= 4; r -= 4; } if (!(x & 0xc0000000u)) { x <<= 2; r -= 2; } if (!(x & 0x80000000u)) { x <<= 1; r -= 1; } return r; } /* * represent a polynomial over GF(2^m) */ struct gf_poly { unsigned int deg; /* polynomial degree */ unsigned int c[0]; /* polynomial terms */ }; /* given its degree, compute a polynomial size in bytes */ #define GF_POLY_SZ(_d) (sizeof(struct gf_poly)+((_d)+1)*sizeof(unsigned int)) /* polynomial of degree 1 */ struct gf_poly_deg1 { struct gf_poly poly; unsigned int c[2]; }; /* * same as encode_bch(), but process input data one byte at a time */ static void encode_bch_unaligned(struct bch_control *bch, const unsigned char *data, unsigned int len, uint32_t *ecc) { int i; const uint32_t *p; const int l = BCH_ECC_WORDS(bch)-1; while (len--) { p = bch->mod8_tab + (l+1)*(((ecc[0] >> 24)^(*data++)) & 0xff); for (i = 0; i < l; i++) ecc[i] = ((ecc[i] << 8)|(ecc[i+1] >> 24))^(*p++); ecc[l] = (ecc[l] << 8)^(*p); } } /* * convert ecc bytes to aligned, zero-padded 32-bit ecc words */ static void load_ecc8(struct bch_control *bch, uint32_t *dst, const uint8_t *src) { uint8_t pad[4] = {0, 0, 0, 0}; unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; for (i = 0; i < nwords; i++, src += 4) dst[i] = (src[0] << 24)|(src[1] << 16)|(src[2] << 8)|src[3]; memcpy(pad, src, BCH_ECC_BYTES(bch)-4*nwords); dst[nwords] = (pad[0] << 24)|(pad[1] << 16)|(pad[2] << 8)|pad[3]; } /* * convert 32-bit ecc words to ecc bytes */ static void store_ecc8(struct bch_control *bch, uint8_t *dst, const uint32_t *src) { uint8_t pad[4]; unsigned int i, nwords = BCH_ECC_WORDS(bch)-1; for (i = 0; i < nwords; i++) { *dst++ = (src[i] >> 24); *dst++ = (src[i] >> 16) & 0xff; *dst++ = (src[i] >> 8) & 0xff; *dst++ = (src[i] >> 0) & 0xff; } pad[0] = (src[nwords] >> 24); pad[1] = (src[nwords] >> 16) & 0xff; pad[2] = (src[nwords] >> 8) & 0xff; pad[3] = (src[nwords] >> 0) & 0xff; memcpy(dst, pad, BCH_ECC_BYTES(bch)-4*nwords); } /** * encode_bch - calculate BCH ecc parity of data * @bch: BCH control structure * @data: data to encode * @len: data length in bytes * @ecc: ecc parity data, must be initialized by caller * * The @ecc parity array is used both as input and output parameter, in order to * allow incremental computations. It should be of the size indicated by member * @ecc_bytes of @bch, and should be initialized to 0 before the first call. * * The exact number of computed ecc parity bits is given by member @ecc_bits of * @bch; it may be less than m*t for large values of t. */ static void encode_bch(struct bch_control *bch, const uint8_t *data, unsigned int len, uint8_t *ecc) { const unsigned int l = BCH_ECC_WORDS(bch)-1; unsigned int i, mlen; unsigned long m; uint32_t w, r[l+1]; const uint32_t * const tab0 = bch->mod8_tab; const uint32_t * const tab1 = tab0 + 256*(l+1); const uint32_t * const tab2 = tab1 + 256*(l+1); const uint32_t * const tab3 = tab2 + 256*(l+1); const uint32_t *pdata, *p0, *p1, *p2, *p3; if (ecc) { /* load ecc parity bytes into internal 32-bit buffer */ load_ecc8(bch, bch->ecc_buf, ecc); } else { memset(bch->ecc_buf, 0, sizeof(r)); } /* process first unaligned data bytes */ m = ((uintptr_t)data) & 3; if (m) { mlen = (len < (4-m)) ? len : 4-m; encode_bch_unaligned(bch, data, mlen, bch->ecc_buf); data += mlen; len -= mlen; } /* process 32-bit aligned data words */ pdata = (uint32_t *)data; mlen = len/4; data += 4*mlen; len -= 4*mlen; memcpy(r, bch->ecc_buf, sizeof(r)); /* * split each 32-bit word into 4 polynomials of weight 8 as follows: * * 31 ...24 23 ...16 15 ... 8 7 ... 0 * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt * tttttttt mod g = r0 (precomputed) * zzzzzzzz 00000000 mod g = r1 (precomputed) * yyyyyyyy 00000000 00000000 mod g = r2 (precomputed) * xxxxxxxx 00000000 00000000 00000000 mod g = r3 (precomputed) * xxxxxxxx yyyyyyyy zzzzzzzz tttttttt mod g = r0^r1^r2^r3 */ while (mlen--) { /* input data is read in big-endian format */ w = r[0]^cpu_to_be32(*pdata++); p0 = tab0 + (l+1)*((w >> 0) & 0xff); p1 = tab1 + (l+1)*((w >> 8) & 0xff); p2 = tab2 + (l+1)*((w >> 16) & 0xff); p3 = tab3 + (l+1)*((w >> 24) & 0xff); for (i = 0; i < l; i++) r[i] = r[i+1]^p0[i]^p1[i]^p2[i]^p3[i]; r[l] = p0[l]^p1[l]^p2[l]^p3[l]; } memcpy(bch->ecc_buf, r, sizeof(r)); /* process last unaligned bytes */ if (len) encode_bch_unaligned(bch, data, len, bch->ecc_buf); /* store ecc parity bytes into original parity buffer */ if (ecc) store_ecc8(bch, ecc, bch->ecc_buf); } static inline int modulo(struct bch_control *bch, unsigned int v) { const unsigned int n = GF_N(bch); while (v >= n) { v -= n; v = (v & n) + (v >> GF_M(bch)); } return v; } /* * shorter and faster modulo function, only works when v < 2N. */ static inline int mod_s(struct bch_control *bch, unsigned int v) { const unsigned int n = GF_N(bch); return (v < n) ? v : v-n; } static inline int deg(unsigned int poly) { /* polynomial degree is the most-significant bit index */ return fls(poly)-1; } /* Galois field basic operations: multiply, divide, inverse, etc. */ static inline unsigned int gf_mul(struct bch_control *bch, unsigned int a, unsigned int b) { return (a && b) ? bch->a_pow_tab[mod_s(bch, bch->a_log_tab[a]+ bch->a_log_tab[b])] : 0; } static inline unsigned int gf_sqr(struct bch_control *bch, unsigned int a) { return a ? bch->a_pow_tab[mod_s(bch, 2*bch->a_log_tab[a])] : 0; } static inline unsigned int a_pow(struct bch_control *bch, int i) { return bch->a_pow_tab[modulo(bch, i)]; } static inline int a_log(struct bch_control *bch, unsigned int x) { return bch->a_log_tab[x]; } /* * generate Galois field lookup tables */ static int build_gf_tables(struct bch_control *bch, unsigned int poly) { unsigned int i, x = 1; const unsigned int k = 1 << deg(poly); /* primitive polynomial must be of degree m */ if (k != (1u << GF_M(bch))) return -1; for (i = 0; i < GF_N(bch); i++) { bch->a_pow_tab[i] = x; bch->a_log_tab[x] = i; if (i && (x == 1)) /* polynomial is not primitive (a^i=1 with 0a_pow_tab[GF_N(bch)] = 1; bch->a_log_tab[0] = 0; return 0; } /* * compute generator polynomial remainder tables for fast encoding */ static void build_mod8_tables(struct bch_control *bch, const uint32_t *g) { int i, j, b, d; uint32_t data, hi, lo, *tab; const int l = BCH_ECC_WORDS(bch); const int plen = DIV_ROUND_UP(bch->ecc_bits+1, 32); const int ecclen = DIV_ROUND_UP(bch->ecc_bits, 32); memset(bch->mod8_tab, 0, 4*256*l*sizeof(*bch->mod8_tab)); for (i = 0; i < 256; i++) { /* p(X)=i is a small polynomial of weight <= 8 */ for (b = 0; b < 4; b++) { /* we want to compute (p(X).X^(8*b+deg(g))) mod g(X) */ tab = bch->mod8_tab + (b*256+i)*l; data = i << (8*b); while (data) { d = deg(data); /* subtract X^d.g(X) from p(X).X^(8*b+deg(g)) */ data ^= g[0] >> (31-d); for (j = 0; j < ecclen; j++) { hi = (d < 31) ? g[j] << (d+1) : 0; lo = (j+1 < plen) ? g[j+1] >> (31-d) : 0; tab[j] ^= hi|lo; } } } } } /* * build a base for factoring degree 2 polynomials */ static int build_deg2_base(struct bch_control *bch) { const int m = GF_M(bch); int i, j, r; unsigned int sum, x, y, remaining, ak = 0, xi[m]; /* find k s.t. Tr(a^k) = 1 and 0 <= k < m */ for (i = 0; i < m; i++) { for (j = 0, sum = 0; j < m; j++) sum ^= a_pow(bch, i*(1 << j)); if (sum) { ak = bch->a_pow_tab[i]; break; } } /* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */ remaining = m; memset(xi, 0, sizeof(xi)); for (x = 0; (x <= GF_N(bch)) && remaining; x++) { y = gf_sqr(bch, x)^x; for (i = 0; i < 2; i++) { r = a_log(bch, y); if (y && (r < m) && !xi[r]) { bch->xi_tab[r] = x; xi[r] = 1; remaining--; dbg("x%d = %x\n", r, x); break; } y ^= ak; } } /* should not happen but check anyway */ return remaining ? -1 : 0; } static void *bch_alloc(size_t size, int *err) { void *ptr; ptr = malloc(size); if (ptr == NULL) *err = 1; return ptr; } /* * compute generator polynomial for given (m,t) parameters. */ static uint32_t *compute_generator_polynomial(struct bch_control *bch) { const unsigned int m = GF_M(bch); const unsigned int t = GF_T(bch); int n, err = 0; unsigned int i, j, nbits, r, word, *roots; struct gf_poly *g; uint32_t *genpoly; g = bch_alloc(GF_POLY_SZ(m*t), &err); roots = bch_alloc((bch->n+1)*sizeof(*roots), &err); genpoly = bch_alloc(DIV_ROUND_UP(m*t+1, 32)*sizeof(*genpoly), &err); if (err) { kfree(genpoly); genpoly = NULL; goto finish; } /* enumerate all roots of g(X) */ memset(roots , 0, (bch->n+1)*sizeof(*roots)); for (i = 0; i < t; i++) { for (j = 0, r = 2*i+1; j < m; j++) { roots[r] = 1; r = mod_s(bch, 2*r); } } /* build generator polynomial g(X) */ g->deg = 0; g->c[0] = 1; for (i = 0; i < GF_N(bch); i++) { if (roots[i]) { /* multiply g(X) by (X+root) */ r = bch->a_pow_tab[i]; g->c[g->deg+1] = 1; for (j = g->deg; j > 0; j--) g->c[j] = gf_mul(bch, g->c[j], r)^g->c[j-1]; g->c[0] = gf_mul(bch, g->c[0], r); g->deg++; } } /* store left-justified binary representation of g(X) */ n = g->deg+1; i = 0; while (n > 0) { nbits = (n > 32) ? 32 : n; for (j = 0, word = 0; j < nbits; j++) { if (g->c[n-1-j]) word |= 1u << (31-j); } genpoly[i++] = word; n -= nbits; } bch->ecc_bits = g->deg; finish: kfree(g); kfree(roots); return genpoly; } /** * free_bch - free the BCH control structure * @bch: BCH control structure to release */ static void free_bch(struct bch_control *bch) { unsigned int i; if (bch) { kfree(bch->a_pow_tab); kfree(bch->a_log_tab); kfree(bch->mod8_tab); kfree(bch->ecc_buf); kfree(bch->ecc_buf2); kfree(bch->xi_tab); kfree(bch->syn); kfree(bch->cache); kfree(bch->elp); for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) kfree(bch->poly_2t[i]); kfree(bch); } } /** * init_bch - initialize a BCH encoder/decoder * @m: Galois field order, should be in the range 5-15 * @t: maximum error correction capability, in bits * @prim_poly: user-provided primitive polynomial (or 0 to use default) * * Returns: * a newly allocated BCH control structure if successful, NULL otherwise * * This initialization can take some time, as lookup tables are built for fast * encoding/decoding; make sure not to call this function from a time critical * path. Usually, init_bch() should be called on module/driver init and * free_bch() should be called to release memory on exit. * * You may provide your own primitive polynomial of degree @m in argument * @prim_poly, or let init_bch() use its default polynomial. * * Once init_bch() has successfully returned a pointer to a newly allocated * BCH control structure, ecc length in bytes is given by member @ecc_bytes of * the structure. */ static struct bch_control *init_bch(int m, int t, unsigned int prim_poly) { int err = 0; unsigned int i, words; uint32_t *genpoly; struct bch_control *bch = NULL; const int min_m = 5; const int max_m = 15; /* default primitive polynomials */ static const unsigned int prim_poly_tab[] = { 0x25, 0x43, 0x83, 0x11d, 0x211, 0x409, 0x805, 0x1053, 0x201b, 0x402b, 0x8003, }; #if defined(CONFIG_BCH_CONST_PARAMS) if ((m != (CONFIG_BCH_CONST_M)) || (t != (CONFIG_BCH_CONST_T))) { printk(KERN_ERR "bch encoder/decoder was configured to support " "parameters m=%d, t=%d only!\n", CONFIG_BCH_CONST_M, CONFIG_BCH_CONST_T); goto fail; } #endif if ((m < min_m) || (m > max_m)) /* * values of m greater than 15 are not currently supported; * supporting m > 15 would require changing table base type * (uint16_t) and a small patch in matrix transposition */ goto fail; /* sanity checks */ if ((t < 1) || (m*t >= ((1 << m)-1))) /* invalid t value */ goto fail; /* select a primitive polynomial for generating GF(2^m) */ if (prim_poly == 0) prim_poly = prim_poly_tab[m-min_m]; bch = malloc(sizeof(*bch)); if (bch == NULL) goto fail; memset(bch, 0, sizeof(*bch)); bch->m = m; bch->t = t; bch->n = (1 << m)-1; words = DIV_ROUND_UP(m*t, 32); bch->ecc_bytes = DIV_ROUND_UP(m*t, 8); bch->a_pow_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_pow_tab), &err); bch->a_log_tab = bch_alloc((1+bch->n)*sizeof(*bch->a_log_tab), &err); bch->mod8_tab = bch_alloc(words*1024*sizeof(*bch->mod8_tab), &err); bch->ecc_buf = bch_alloc(words*sizeof(*bch->ecc_buf), &err); bch->ecc_buf2 = bch_alloc(words*sizeof(*bch->ecc_buf2), &err); bch->xi_tab = bch_alloc(m*sizeof(*bch->xi_tab), &err); bch->syn = bch_alloc(2*t*sizeof(*bch->syn), &err); bch->cache = bch_alloc(2*t*sizeof(*bch->cache), &err); bch->elp = bch_alloc((t+1)*sizeof(struct gf_poly_deg1), &err); for (i = 0; i < ARRAY_SIZE(bch->poly_2t); i++) bch->poly_2t[i] = bch_alloc(GF_POLY_SZ(2*t), &err); if (err) goto fail; err = build_gf_tables(bch, prim_poly); if (err) goto fail; /* use generator polynomial for computing encoding tables */ genpoly = compute_generator_polynomial(bch); if (genpoly == NULL) goto fail; build_mod8_tables(bch, genpoly); kfree(genpoly); err = build_deg2_base(bch); if (err) goto fail; return bch; fail: free_bch(bch); return NULL; } static void swap_bits(uint8_t *buf, int len) { int i, j; for (j = 0; j < len; j++) { uint8_t byte = buf[j]; buf[j] = 0; for (i = 0; i < 8; i++) { if (byte & (1 << i)) buf[j] |= (1 << (7 - i)); } } } static uint16_t lfsr_step(uint16_t state, int count) { state &= 0x7fff; while (count--) state = ((state >> 1) | ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; return state; } static uint16_t default_scrambler_seeds[] = { 0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72, 0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436, 0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d, 0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130, 0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56, 0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55, 0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb, 0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17, 0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62, 0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064, 0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126, 0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e, 0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3, 0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b, 0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d, 0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db, }; static uint16_t brom_scrambler_seeds[] = { 0x4a80 }; static void scramble(const struct image_info *info, int page, uint8_t *data, int datalen) { uint16_t state; int i; /* Boot0 is always scrambled no matter the command line option. */ if (info->boot0) { state = brom_scrambler_seeds[0]; } else { unsigned seedmod = info->eraseblock_size / info->page_size; /* Bail out earlier if the user didn't ask for scrambling. */ if (!info->scramble) return; if (seedmod > ARRAY_SIZE(default_scrambler_seeds)) seedmod = ARRAY_SIZE(default_scrambler_seeds); state = default_scrambler_seeds[page % seedmod]; } /* Prepare the initial state... */ state = lfsr_step(state, 15); /* and start scrambling data. */ for (i = 0; i < datalen; i++) { data[i] ^= state; state = lfsr_step(state, 8); } } static int write_page(const struct image_info *info, uint8_t *buffer, FILE *src, FILE *rnd, FILE *dst, struct bch_control *bch, int page) { int steps = info->usable_page_size / info->ecc_step_size; int eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8); off_t pos = ftell(dst); size_t pad, cnt; int i; if (eccbytes % 2) eccbytes++; memset(buffer, 0xff, info->page_size + info->oob_size); cnt = fread(buffer, 1, info->usable_page_size, src); if (!cnt) { if (!feof(src)) { fprintf(stderr, "Failed to read data from the source\n"); return -1; } else { return 0; } } fwrite(buffer, info->page_size + info->oob_size, 1, dst); for (i = 0; i < info->usable_page_size; i++) { if (buffer[i] != 0xff) break; } /* We leave empty pages at 0xff. */ if (i == info->usable_page_size) return 0; /* Restore the source pointer to read it again. */ fseek(src, -cnt, SEEK_CUR); /* Randomize unused space if scrambling is required. */ if (info->scramble) { int offs; if (info->boot0) { offs = steps * (info->ecc_step_size + eccbytes + 4); cnt = info->page_size + info->oob_size - offs; fread(buffer + offs, 1, cnt, rnd); } else { offs = info->page_size + (steps * (eccbytes + 4)); cnt = info->page_size + info->oob_size - offs; memset(buffer + offs, 0xff, cnt); scramble(info, page, buffer + offs, cnt); } fseek(dst, pos + offs, SEEK_SET); fwrite(buffer + offs, cnt, 1, dst); } for (i = 0; i < steps; i++) { int ecc_offs, data_offs; uint8_t *ecc; memset(buffer, 0xff, info->ecc_step_size + eccbytes + 4); ecc = buffer + info->ecc_step_size + 4; if (info->boot0) { data_offs = i * (info->ecc_step_size + eccbytes + 4); ecc_offs = data_offs + info->ecc_step_size + 4; } else { data_offs = i * info->ecc_step_size; ecc_offs = info->page_size + 4 + (i * (eccbytes + 4)); } cnt = fread(buffer, 1, info->ecc_step_size, src); if (!cnt && !feof(src)) { fprintf(stderr, "Failed to read data from the source\n"); return -1; } pad = info->ecc_step_size - cnt; if (pad) { if (info->scramble && info->boot0) fread(buffer + cnt, 1, pad, rnd); else memset(buffer + cnt, 0xff, pad); } memset(ecc, 0, eccbytes); swap_bits(buffer, info->ecc_step_size + 4); encode_bch(bch, buffer, info->ecc_step_size + 4, ecc); swap_bits(buffer, info->ecc_step_size + 4); swap_bits(ecc, eccbytes); scramble(info, page, buffer, info->ecc_step_size + 4 + eccbytes); fseek(dst, pos + data_offs, SEEK_SET); fwrite(buffer, info->ecc_step_size, 1, dst); fseek(dst, pos + ecc_offs - 4, SEEK_SET); fwrite(ecc - 4, eccbytes + 4, 1, dst); } /* Fix BBM. */ fseek(dst, pos + info->page_size, SEEK_SET); memset(buffer, 0xff, 2); fwrite(buffer, 2, 1, dst); /* Make dst pointer point to the next page. */ fseek(dst, pos + info->page_size + info->oob_size, SEEK_SET); return 0; } static int create_image(const struct image_info *info) { off_t page = info->offset / info->page_size; struct bch_control *bch; FILE *src, *dst, *rnd; uint8_t *buffer; bch = init_bch(14, info->ecc_strength, BCH_PRIMITIVE_POLY); if (!bch) { fprintf(stderr, "Failed to init the BCH engine\n"); return -1; } buffer = malloc(info->page_size + info->oob_size); if (!buffer) { fprintf(stderr, "Failed to allocate the NAND page buffer\n"); return -1; } memset(buffer, 0xff, info->page_size + info->oob_size); src = fopen(info->source, "r"); if (!src) { fprintf(stderr, "Failed to open source file (%s)\n", info->source); return -1; } dst = fopen(info->dest, "w"); if (!dst) { fprintf(stderr, "Failed to open dest file (%s)\n", info->dest); return -1; } rnd = fopen("/dev/urandom", "r"); if (!rnd) { fprintf(stderr, "Failed to open /dev/urandom\n"); return -1; } while (!feof(src)) { int ret; ret = write_page(info, buffer, src, rnd, dst, bch, page++); if (ret) return ret; } return 0; } static void display_help(int status) { fprintf(status == EXIT_SUCCESS ? stdout : stderr, "sunxi-nand-image-builder %s\n" "\n" "Usage: sunxi-nand-image-builder [OPTIONS] source-image output-image\n" "\n" "Creates a raw NAND image that can be read by the sunxi NAND controller.\n" "\n" "-h --help Display this help and exit\n" "-c / --ecc=/ ECC config (strength/step-size)\n" "-p --page= Page size\n" "-o --oob= OOB size\n" "-u --usable= Usable page size\n" "-e --eraseblock= Erase block size\n" "-b --boot0 Build a boot0 image.\n" "-s --scramble Scramble data\n" "-a --address= Where the image will be programmed.\n" "\n" "Notes:\n" "All the information you need to pass to this tool should be part of\n" "the NAND datasheet.\n" "\n" "The NAND controller only supports the following ECC configs\n" " Valid ECC strengths: 16, 24, 28, 32, 40, 48, 56, 60 and 64\n" " Valid ECC step size: 512 and 1024\n" "\n" "If you are building a boot0 image, you'll have specify extra options.\n" "These options should be chosen based on the layouts described here:\n" " http://linux-sunxi.org/NAND#More_information_on_BROM_NAND\n" "\n" " --usable should be assigned the 'Hardware page' value\n" " --ecc should be assigned the 'ECC capacity'/'ECC page' values\n" " --usable should be smaller than --page\n" "\n" "The --address option is only required for non-boot0 images that are \n" "meant to be programmed at a non eraseblock aligned offset.\n" "\n" "Examples:\n" " The H27UCG8T2BTR-BC NAND exposes\n" " * 16k pages\n" " * 1280 OOB bytes per page\n" " * 4M eraseblocks\n" " * requires data scrambling\n" " * expects a minimum ECC of 40bits/1024bytes\n" "\n" " A normal image can be generated with\n" " sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -c 40/1024\n" " A boot0 image can be generated with\n" " sunxi-nand-image-builder -p 16384 -o 1280 -e 0x400000 -s -b -u 4096 -c 64/1024\n", VERSION); exit(status); } static int check_image_info(struct image_info *info) { static int valid_ecc_strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 }; int eccbytes, eccsteps; unsigned i; if (!info->page_size) { fprintf(stderr, "--page is missing\n"); return -EINVAL; } if (!info->page_size) { fprintf(stderr, "--oob is missing\n"); return -EINVAL; } if (!info->eraseblock_size) { fprintf(stderr, "--eraseblock is missing\n"); return -EINVAL; } if (info->ecc_step_size != 512 && info->ecc_step_size != 1024) { fprintf(stderr, "Invalid ECC step argument: %d\n", info->ecc_step_size); return -EINVAL; } for (i = 0; i < ARRAY_SIZE(valid_ecc_strengths); i++) { if (valid_ecc_strengths[i] == info->ecc_strength) break; } if (i == ARRAY_SIZE(valid_ecc_strengths)) { fprintf(stderr, "Invalid ECC strength argument: %d\n", info->ecc_strength); return -EINVAL; } eccbytes = DIV_ROUND_UP(info->ecc_strength * 14, 8); if (eccbytes % 2) eccbytes++; eccbytes += 4; eccsteps = info->usable_page_size / info->ecc_step_size; if (info->page_size + info->oob_size < info->usable_page_size + (eccsteps * eccbytes)) { fprintf(stderr, "ECC bytes do not fit in the NAND page, choose a weaker ECC\n"); return -EINVAL; } return 0; } int main(int argc, char **argv) { struct image_info info; memset(&info, 0, sizeof(info)); /* * Process user arguments */ for (;;) { int option_index = 0; char *endptr = NULL; static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"ecc", required_argument, 0, 'c'}, {"page", required_argument, 0, 'p'}, {"oob", required_argument, 0, 'o'}, {"usable", required_argument, 0, 'u'}, {"eraseblock", required_argument, 0, 'e'}, {"boot0", no_argument, 0, 'b'}, {"scramble", no_argument, 0, 's'}, {"address", required_argument, 0, 'a'}, {0, 0, 0, 0}, }; int c = getopt_long(argc, argv, "c:p:o:u:e:ba:sh", long_options, &option_index); if (c == EOF) break; switch (c) { case 'h': display_help(0); break; case 's': info.scramble = 1; break; case 'c': info.ecc_strength = strtol(optarg, &endptr, 0); if (endptr || *endptr == '/') info.ecc_step_size = strtol(endptr + 1, NULL, 0); break; case 'p': info.page_size = strtol(optarg, NULL, 0); break; case 'o': info.oob_size = strtol(optarg, NULL, 0); break; case 'u': info.usable_page_size = strtol(optarg, NULL, 0); break; case 'e': info.eraseblock_size = strtol(optarg, NULL, 0); break; case 'b': info.boot0 = 1; break; case 'a': info.offset = strtoull(optarg, NULL, 0); break; case '?': display_help(-1); break; } } if ((argc - optind) != 2) display_help(-1); info.source = argv[optind]; info.dest = argv[optind + 1]; if (!info.boot0) { info.usable_page_size = info.page_size; } else if (!info.usable_page_size) { if (info.page_size > 8192) info.usable_page_size = 8192; else if (info.page_size > 4096) info.usable_page_size = 4096; else info.usable_page_size = 1024; } if (check_image_info(&info)) display_help(-1); return create_image(&info); } sunxi-tools-1.4.1/nand-part-a10.h000066400000000000000000000051341300506642400164400ustar00rootroot00000000000000/* * 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.4.1/nand-part-a20.h000066400000000000000000000064351300506642400164460ustar00rootroot00000000000000/* * 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.4.1/nand-part-main.c000066400000000000000000000056121300506642400167770ustar00rootroot00000000000000/* * (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" #include "common.h" void usage(const char *cmd) { puts("sunxi-nand-part " VERSION "\n"); 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.4.1/nand-part.c000066400000000000000000000177501300506642400160630ustar00rootroot00000000000000/* * 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 #ifdef __linux__ # include # include /* BLKRRPART */ #endif #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); } #ifdef __linux__ if (ioctl(fd, BLKRRPART, NULL)) perror("Failed rereading partition table"); #endif 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); #ifdef __linux__ printf("rereading partition table... returned %d\n", ioctl(fd, BLKRRPART, 0)); #endif } } close(fd); return 0; } sunxi-tools-1.4.1/phoenix_info.c000066400000000000000000000106401300506642400166530ustar00rootroot00000000000000/* * 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 . */ #include #include #include #include #include "common.h" #include "portable_endian.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 = stdout; 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"); 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) { puts("phoenix-info " VERSION "\n"); 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.4.1/pio.c000066400000000000000000000226251300506642400147630ustar00rootroot00000000000000/* * (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 */ #include #include #include #include #include #include #include #ifndef NO_MMAP #include #endif #include #include #include #include "common.h" #include "portable_endian.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 ) { fputs("sunxi-pio " VERSION "\n\n", stderr); 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 UNUSED(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) { #ifdef NO_MMAP errno = ENOSYS; /* Function not implemented */ perror("mmap PIO"); #else 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; #endif } 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.4.1/progress.c000066400000000000000000000110671300506642400160360ustar00rootroot00000000000000/* * Copyright (C) 2015 Bernhard Nortmann * * 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 "progress.h" #include #include #include /* Less reliable than clock_gettime, but does not require linking with -lrt */ inline double gettime(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + (double)tv.tv_usec / 1000000.; } /* Calculate transfer rate (in bytes per second) */ inline double rate(size_t transferred, double elapsed) { if (elapsed > 0) return (double)transferred / elapsed; return 0.; } /* Estimate remaining time ("ETA") for given transfer rate */ inline double estimate(size_t remaining, double rate) { if (rate > 0) return (double)remaining / rate; return 0.; } /* Return ETA (in seconds) as string, formatted to minutes and seconds */ const char *format_ETA(double remaining) { static char result[6] = ""; int seconds = remaining + 0.5; /* simplistic round() */ if (seconds >= 0 && seconds < 6000) { snprintf(result, sizeof(result), "%02d:%02d", seconds / 60, seconds % 60); return result; } return "--:--"; } /* Private progress state variable */ typedef struct { progress_cb_t callback; size_t total; size_t done; double start; /* start point (timestamp) for rate and ETA calculation */ } progress_private_t; static progress_private_t progress = { .callback = NULL, .start = 0. }; /* 'External' API */ void progress_start(progress_cb_t callback, size_t expected_total) { progress.callback = callback; progress.total = expected_total; progress.done = 0; progress.start = gettime(); /* reset start time */ } /* Update progress status, passing information to the callback function. */ void progress_update(size_t bytes_done) { progress.done += bytes_done; if (progress.callback) progress.callback(progress.total, progress.done); } /* Return relative / "elapsed" time, since progress_start() */ static inline double progress_elapsed(void) { if (progress.start != 0.) return gettime() - progress.start; return 0.; } /* Callback function implementing a simple progress bar written to stdout */ void progress_bar(size_t total, size_t done) { static const int WIDTH = 48; /* # of characters to use for progress bar */ float ratio = total > 0 ? (float)done / total : 0; int i, pos = WIDTH * ratio; double speed = rate(done, progress_elapsed()); double eta = estimate(total - done, speed); printf("\r%3.0f%% [", ratio * 100); /* current percentage */ for (i = 0; i < pos; i++) putchar('='); for (i = pos; i < WIDTH; i++) putchar(' '); if (done < total) printf("]%6.1f kB/s, ETA %s ", kilo(speed), format_ETA(eta)); else /* transfer complete, output totals plus a newline */ printf("] %5.0f kB, %6.1f kB/s\n", kilo(done), kilo(speed)); fflush(stdout); } /* * Progress callback that emits percentage numbers, each on a separate line. * The output is suitable for piping it into "dialog --gauge". * * sunxi-fel multiwrite-with-gauge <...> \ * | dialog --title "FEL upload progress" \ * --gauge "" 5 70 */ void progress_gauge(size_t total, size_t done) { if (total > 0) { printf("%.0f\n", (float)done / total * 100); fflush(stdout); } } /* * A more sophisticated version of progress_gauge() that also updates the * prompt (caption) with additional information. This uses a feature of * the dialog utility that parses "XXX" delimiters - see 'man dialog'. * * sunxi-fel multiwrite-with-xgauge <...> \ * | dialog --title "FEL upload progress" \ * --backtitle "Please wait..." \ * --gauge "" 6 70 */ void progress_gauge_xxx(size_t total, size_t done) { if (total > 0) { double speed = rate(done, progress_elapsed()); double eta = estimate(total - done, speed); printf("XXX\n"); printf("%.0f\n", (float)done / total * 100); if (done < total) printf("%zu of %zu, %.1f kB/s, ETA %s\n", done, total, kilo(speed), format_ETA(eta)); else printf("Done: %.1f kB, at %.1f kB/s\n", kilo(done), kilo(speed)); printf("XXX\n"); fflush(stdout); } } sunxi-tools-1.4.1/progress.h000066400000000000000000000030671300506642400160440ustar00rootroot00000000000000/* * Copyright (C) 2015 Bernhard Nortmann * * 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_PROGRESS_H #define _SUNXI_TOOLS_PROGRESS_H #include /* function pointer type for a progress callback / notification */ typedef void (*progress_cb_t)(size_t total, size_t done); /* conversion helper macros */ #define kilo(value) ((double)(value) / 1000.) /* SI prefix "k" */ #define kibi(value) ((double)(value) / 1024.) /* binary prefix "Ki", "K" */ double gettime(void); double rate(size_t transferred, double elapsed); double estimate(size_t remaining, double rate); void progress_start(progress_cb_t callback, size_t expected_total); void progress_update(size_t bytes_done); /* progress callback implementations for various display styles */ void progress_bar(size_t total, size_t done); void progress_gauge(size_t total, size_t done); void progress_gauge_xxx(size_t total, size_t done); #endif /* _SUNXI_TOOLS_PROGRESS_H */ sunxi-tools-1.4.1/script.c000066400000000000000000000134051300506642400154740ustar00rootroot00000000000000/* * 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.4.1/script.h000066400000000000000000000065011300506642400155000ustar00rootroot00000000000000/* * 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 #include "list.h" #define GPIO_BANK_MAX 14 /* N, (zero-based) index 13 */ /** 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.4.1/script_bin.c000066400000000000000000000231601300506642400163230ustar00rootroot00000000000000/* * 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 #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; for (char *p = entry->name; *p; p++) if (!(isalnum(*p) || *p == '_')) { pr_info("Warning: Malformed entry key \"%s\"\n", entry->name); break; } 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 ", filename, section->name, entry->name); char c = 'A' + gpio->port - 1; if (c >= 'A' && c <= 'Z') pr_err("%c ", c); pr_err("(%u)\n", 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) { unsigned int i; struct script_bin_head *head = bin; if (((head->version[0] & 0x3FFF) > SCRIPT_BIN_VERSION_LIMIT) || (head->version[1] > SCRIPT_BIN_VERSION_LIMIT) || (head->version[2] > SCRIPT_BIN_VERSION_LIMIT)) { pr_err("Malformed data: version %u.%u.%u.\n", head->version[0], head->version[1], head->version[2]); return 0; } if (head->sections > SCRIPT_BIN_SECTION_LIMIT) { pr_err("Malformed data: too many sections (%u).\n", head->sections); return 0; } pr_info("%s: version: %u.%u.%u\n", filename, head->version[0] & 0x3FFF, head->version[1], head->version[2]); pr_info("%s: size: %zu (%u sections)\n", filename, bin_size, head->sections); /* 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.4.1/script_bin.h000066400000000000000000000032611300506642400163300ustar00rootroot00000000000000/* * 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 { uint32_t sections; uint32_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.4.1/script_extractor.c000066400000000000000000000022551300506642400175700ustar00rootroot00000000000000/* * 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 #include #define SCRIPT_START 0x43000000 #define SCRIPT_SIZE 0x20000 int main(void) { char *addr; int fd; int i; fd = open("/dev/mem", O_RDONLY); addr = (char *)mmap(NULL, SCRIPT_SIZE, PROT_READ, MAP_SHARED, fd, SCRIPT_START); for (i = 0; i < SCRIPT_SIZE; i++) putchar(addr[i]); munmap(addr, SCRIPT_SIZE); close(fd); return 0; } sunxi-tools-1.4.1/script_fex.c000066400000000000000000000212271300506642400163370ustar00rootroot00000000000000/* * 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 */ if (*s == ':') { /* see https://github.com/linux-sunxi/sunxi-boards/issues/50 */ errf("Warning: %s:%zu: invalid line, suspecting typo/malformed comment.\n", filename, line); continue; /* ignore this line */ } 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; } errf("E: %s:%zu: parse error at %zu.\n", filename, line, p-buffer+1); 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.4.1/script_fex.h000066400000000000000000000016621300506642400163450ustar00rootroot00000000000000/* * 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.4.1/script_uboot.c000066400000000000000000000135701300506642400167070ustar00rootroot00000000000000/* * 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.4.1/script_uboot.h000066400000000000000000000015521300506642400167110ustar00rootroot00000000000000/* * 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 sunxi-tools-1.4.1/uart0-helloworld-sdboot.c000066400000000000000000000351111300506642400206620ustar00rootroot00000000000000/* * Copyright (C) 2016 Siarhei Siamashka * * 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 . */ /* * Partially based on the uart code from ar100-info * * (C) Copyright 2013 Stefan Kristiansson * * 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 */ /* * Partially based on the sunxi gpio code from U-Boot * * (C) Copyright 2012 Henrik Nordstrom * * Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c: * * (C) Copyright 2007-2011 * Allwinner Technology Co., Ltd. * Tom Cubie * * SPDX-License-Identifier: GPL-2.0+ */ typedef unsigned int u32; #define set_wbit(addr, v) (*((volatile unsigned long *)(addr)) |= (unsigned long)(v)) #define readl(addr) (*((volatile unsigned long *)(addr))) #define writel(v, addr) (*((volatile unsigned long *)(addr)) = (unsigned long)(v)) #define SUNXI_UART0_BASE 0x01C28000 #define SUNXI_PIO_BASE 0x01C20800 #define AW_CCM_BASE 0x01c20000 #define AW_SRAMCTRL_BASE 0x01c00000 /***************************************************************************** * GPIO code, borrowed from U-Boot * *****************************************************************************/ #define SUNXI_GPIO_A 0 #define SUNXI_GPIO_B 1 #define SUNXI_GPIO_C 2 #define SUNXI_GPIO_D 3 #define SUNXI_GPIO_E 4 #define SUNXI_GPIO_F 5 #define SUNXI_GPIO_G 6 #define SUNXI_GPIO_H 7 #define SUNXI_GPIO_I 8 struct sunxi_gpio { u32 cfg[4]; u32 dat; u32 drv[2]; u32 pull[2]; }; struct sunxi_gpio_reg { struct sunxi_gpio gpio_bank[10]; }; #define GPIO_BANK(pin) ((pin) >> 5) #define GPIO_NUM(pin) ((pin) & 0x1F) #define GPIO_CFG_INDEX(pin) (((pin) & 0x1F) >> 3) #define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1F) & 0x7) << 2) #define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4) #define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1) /* GPIO bank sizes */ #define SUNXI_GPIO_A_NR (32) #define SUNXI_GPIO_B_NR (32) #define SUNXI_GPIO_C_NR (32) #define SUNXI_GPIO_D_NR (32) #define SUNXI_GPIO_E_NR (32) #define SUNXI_GPIO_F_NR (32) #define SUNXI_GPIO_G_NR (32) #define SUNXI_GPIO_H_NR (32) #define SUNXI_GPIO_I_NR (32) #define SUNXI_GPIO_NEXT(__gpio) ((__gpio##_START) + (__gpio##_NR) + 0) enum sunxi_gpio_number { SUNXI_GPIO_A_START = 0, SUNXI_GPIO_B_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_A), SUNXI_GPIO_C_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_B), SUNXI_GPIO_D_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_C), SUNXI_GPIO_E_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_D), SUNXI_GPIO_F_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_E), SUNXI_GPIO_G_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_F), SUNXI_GPIO_H_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_G), SUNXI_GPIO_I_START = SUNXI_GPIO_NEXT(SUNXI_GPIO_H), }; /* SUNXI GPIO number definitions */ #define SUNXI_GPA(_nr) (SUNXI_GPIO_A_START + (_nr)) #define SUNXI_GPB(_nr) (SUNXI_GPIO_B_START + (_nr)) #define SUNXI_GPC(_nr) (SUNXI_GPIO_C_START + (_nr)) #define SUNXI_GPD(_nr) (SUNXI_GPIO_D_START + (_nr)) #define SUNXI_GPE(_nr) (SUNXI_GPIO_E_START + (_nr)) #define SUNXI_GPF(_nr) (SUNXI_GPIO_F_START + (_nr)) #define SUNXI_GPG(_nr) (SUNXI_GPIO_G_START + (_nr)) #define SUNXI_GPH(_nr) (SUNXI_GPIO_H_START + (_nr)) #define SUNXI_GPI(_nr) (SUNXI_GPIO_I_START + (_nr)) /* GPIO pin function config */ #define SUNXI_GPIO_INPUT (0) #define SUNXI_GPIO_OUTPUT (1) #define SUN4I_GPB_UART0 (2) #define SUN5I_GPB_UART0 (2) #define SUN6I_GPH_UART0 (2) #define SUN8I_H3_GPA_UART0 (2) #define SUN50I_A64_GPB_UART0 (4) #define SUNXI_GPF_UART0 (4) /* GPIO pin pull-up/down config */ #define SUNXI_GPIO_PULL_DISABLE (0) #define SUNXI_GPIO_PULL_UP (1) #define SUNXI_GPIO_PULL_DOWN (2) int sunxi_gpio_set_cfgpin(u32 pin, u32 val) { u32 cfg; u32 bank = GPIO_BANK(pin); u32 index = GPIO_CFG_INDEX(pin); u32 offset = GPIO_CFG_OFFSET(pin); struct sunxi_gpio *pio = &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank]; cfg = readl(&pio->cfg[0] + index); cfg &= ~(0xf << offset); cfg |= val << offset; writel(cfg, &pio->cfg[0] + index); return 0; } int sunxi_gpio_set_pull(u32 pin, u32 val) { u32 cfg; u32 bank = GPIO_BANK(pin); u32 index = GPIO_PULL_INDEX(pin); u32 offset = GPIO_PULL_OFFSET(pin); struct sunxi_gpio *pio = &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank]; cfg = readl(&pio->pull[0] + index); cfg &= ~(0x3 << offset); cfg |= val << offset; writel(cfg, &pio->pull[0] + index); return 0; } int sunxi_gpio_output(u32 pin, u32 val) { u32 dat; u32 bank = GPIO_BANK(pin); u32 num = GPIO_NUM(pin); struct sunxi_gpio *pio = &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank]; dat = readl(&pio->dat); if(val) dat |= 1 << num; else dat &= ~(1 << num); writel(dat, &pio->dat); return 0; } int sunxi_gpio_input(u32 pin) { u32 dat; u32 bank = GPIO_BANK(pin); u32 num = GPIO_NUM(pin); struct sunxi_gpio *pio = &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank]; dat = readl(&pio->dat); dat >>= num; return (dat & 0x1); } int gpio_direction_input(unsigned gpio) { sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT); return sunxi_gpio_input(gpio); } int gpio_direction_output(unsigned gpio, int value) { sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT); return sunxi_gpio_output(gpio, value); } /***************************************************************************** * Nearly all the Allwinner SoCs are using the same VER_REG register for * * runtime SoC type identification. For additional details see: * * * * https://linux-sunxi.org/SRAM_Controller_Register_Guide * * * * Allwinner A80 is an oddball and has a non-standard address of the VER_REG * * * * Allwinner A10s and A13 are using the same SoC type id, but they can be * * differentiated using a certain part of the SID register. * *****************************************************************************/ #define VER_REG (AW_SRAMCTRL_BASE + 0x24) #define SUN4I_SID_BASE 0x01C23800 static u32 soc_id; void soc_detection_init(void) { u32 midr; asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (midr)); if (((midr >> 4) & 0xFFF) == 0xC0F) { soc_id = 0x1639; /* ARM Cortex-A15, so likely Allwinner A80 */ } else { set_wbit(VER_REG, 1 << 15); soc_id = readl(VER_REG) >> 16; } } int soc_is_a10(void) { return soc_id == 0x1623; } int soc_is_a10s(void) { return (soc_id == 0x1625) && (((readl(SUN4I_SID_BASE + 0x08) >> 12) & 0xf) == 7); } int soc_is_a13(void) { return (soc_id == 0x1625) && !(((readl(SUN4I_SID_BASE + 0x08) >> 12) & 0xf) == 7); } int soc_is_a20(void) { return soc_id == 0x1651; } int soc_is_a31(void) { return soc_id == 0x1633; } int soc_is_a80(void) { return soc_id == 0x1639; } int soc_is_a64(void) { return soc_id == 0x1689; } int soc_is_h3(void) { return soc_id == 0x1680; } /***************************************************************************** * UART is mostly the same on A10/A13/A20/A31/H3/A64, except that newer SoCs * * have changed the APB numbering scheme (A10/A13/A20 used to have APB0 and * * APB1 names, but newer SoCs just have renamed them into APB1 and APB2). * * The constants below are using the new APB numbering convention. * * Also the newer SoCs have introduced the APB2_RESET register, but writing * * to it effectively goes nowhere on older SoCs and is harmless. * *****************************************************************************/ #define CONFIG_CONS_INDEX 1 #define APB2_CFG (AW_CCM_BASE + 0x058) #define APB2_GATE (AW_CCM_BASE + 0x06C) #define APB2_RESET (AW_CCM_BASE + 0x2D8) #define APB2_GATE_UART_SHIFT (16) #define APB2_RESET_UART_SHIFT (16) void clock_init_uart(void) { /* Open the clock gate for UART0 */ set_wbit(APB2_GATE, 1 << (APB2_GATE_UART_SHIFT + CONFIG_CONS_INDEX - 1)); /* Deassert UART0 reset (only needed on A31/A64/H3) */ set_wbit(APB2_RESET, 1 << (APB2_RESET_UART_SHIFT + CONFIG_CONS_INDEX - 1)); } /***************************************************************************** * UART0 pins muxing is different for different SoC variants. * * Allwinner A13 is a bit special, because there are no dedicated UART0 pins * * and they are shared with MMC0. * *****************************************************************************/ void gpio_init(void) { if (soc_is_a10() || soc_is_a20()) { sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUN4I_GPB_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUN4I_GPB_UART0); sunxi_gpio_set_pull(SUNXI_GPB(23), SUNXI_GPIO_PULL_UP); } else if (soc_is_a10s()) { sunxi_gpio_set_cfgpin(SUNXI_GPB(19), SUN5I_GPB_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN5I_GPB_UART0); sunxi_gpio_set_pull(SUNXI_GPB(20), SUNXI_GPIO_PULL_UP); } else if (soc_is_a13()) { /* Disable PB19/PB20 as UART0 to avoid conflict */ gpio_direction_input(SUNXI_GPB(19)); gpio_direction_input(SUNXI_GPB(20)); /* Use SD breakout board to access UART0 on MMC0 pins */ sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUNXI_GPF_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUNXI_GPF_UART0); sunxi_gpio_set_pull(SUNXI_GPF(4), SUNXI_GPIO_PULL_UP); } else if (soc_is_a31()) { sunxi_gpio_set_cfgpin(SUNXI_GPH(20), SUN6I_GPH_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPH(21), SUN6I_GPH_UART0); sunxi_gpio_set_pull(SUNXI_GPH(21), SUNXI_GPIO_PULL_UP); } else if (soc_is_a64()) { sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_A64_GPB_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_A64_GPB_UART0); sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP); } else if (soc_is_h3()) { sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN8I_H3_GPA_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN8I_H3_GPA_UART0); sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP); } else { /* Unknown SoC */ while (1) {} } } /*****************************************************************************/ #define UART0_RBR (SUNXI_UART0_BASE + 0x0) /* receive buffer register */ #define UART0_THR (SUNXI_UART0_BASE + 0x0) /* transmit holding register */ #define UART0_DLL (SUNXI_UART0_BASE + 0x0) /* divisor latch low register */ #define UART0_DLH (SUNXI_UART0_BASE + 0x4) /* divisor latch high register */ #define UART0_IER (SUNXI_UART0_BASE + 0x4) /* interrupt enable reigster */ #define UART0_IIR (SUNXI_UART0_BASE + 0x8) /* interrupt identity register */ #define UART0_FCR (SUNXI_UART0_BASE + 0x8) /* fifo control register */ #define UART0_LCR (SUNXI_UART0_BASE + 0xc) /* line control register */ #define UART0_LSR (SUNXI_UART0_BASE + 0x14) /* line status register */ #define BAUD_115200 (0xD) /* 24 * 1000 * 1000 / 16 / 115200 = 13 */ #define NO_PARITY (0) #define ONE_STOP_BIT (0) #define DAT_LEN_8_BITS (3) #define LC_8_N_1 (NO_PARITY << 3 | ONE_STOP_BIT << 2 | DAT_LEN_8_BITS) void uart0_init(void) { clock_init_uart(); /* select dll dlh */ writel(0x80, UART0_LCR); /* set baudrate */ writel(0, UART0_DLH); writel(BAUD_115200, UART0_DLL); /* set line control */ writel(LC_8_N_1, UART0_LCR); } void uart0_putc(char c) { while (!(readl(UART0_LSR) & (1 << 6))) {} writel(c, UART0_THR); } void uart0_puts(const char *s) { while (*s) { if (*s == '\n') uart0_putc('\r'); uart0_putc(*s++); } } /*****************************************************************************/ /* A workaround for https://patchwork.ozlabs.org/patch/622173 */ void __attribute__((section(".start"))) __attribute__((naked)) start(void) { asm volatile("b main \n" ".long 0xffffffff \n" ".long 0xffffffff \n" ".long 0xffffffff \n"); } enum { BOOT_DEVICE_UNK, BOOT_DEVICE_FEL, BOOT_DEVICE_MMC0, BOOT_DEVICE_SPI }; int get_boot_device(void) { u32 *spl_signature = (void *)0x4; if (soc_is_a64() || soc_is_a80()) spl_signature = (void *)0x10004; /* Check the eGON.BT0 magic in the SPL header */ if (spl_signature[0] != 0x4E4F4765 || spl_signature[1] != 0x3054422E) return BOOT_DEVICE_FEL; u32 boot_dev = spl_signature[9] & 0xFF; /* offset into SPL = 0x28 */ if (boot_dev == 0) return BOOT_DEVICE_MMC0; if (boot_dev == 3) return BOOT_DEVICE_SPI; return BOOT_DEVICE_UNK; } int main(void) { soc_detection_init(); gpio_init(); uart0_init(); uart0_puts("\nHello from "); if (soc_is_a10()) uart0_puts("Allwinner A10!\n"); else if (soc_is_a10s()) uart0_puts("Allwinner A10s!\n"); else if (soc_is_a13()) uart0_puts("Allwinner A13!\n"); else if (soc_is_a20()) uart0_puts("Allwinner A20!\n"); else if (soc_is_a31()) uart0_puts("Allwinner A31/A31s!\n"); else if (soc_is_a64()) uart0_puts("Allwinner A64!\n"); else if (soc_is_h3()) uart0_puts("Allwinner H3!\n"); else uart0_puts("unknown Allwinner SoC!\n"); switch (get_boot_device()) { case BOOT_DEVICE_FEL: uart0_puts("Returning back to FEL.\n"); return 0; case BOOT_DEVICE_MMC0: uart0_puts("Booted from MMC0, entering an infinite loop.\n"); while (1) {} case BOOT_DEVICE_SPI: uart0_puts("Booted from SPI0, entering an infinite loop.\n"); while (1) {} default: uart0_puts("Booted from unknown media, entering an infinite loop.\n"); while (1) {} }; return 0; } sunxi-tools-1.4.1/uart0-helloworld-sdboot.lds000066400000000000000000000017161300506642400212260ustar00rootroot00000000000000/* * Copyright (C) 2016 Siarhei Siamashka * * 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 { . = 0x0000; .start : { *(.start) } .text : { *(.text) } /DISCARD/ : { *(.dynstr*) } /DISCARD/ : { *(.dynamic*) } /DISCARD/ : { *(.plt*) } /DISCARD/ : { *(.interp*) } /DISCARD/ : { *(.gnu*) } /DISCARD/ : { *(.note*) } }