pax_global_header00006660000000000000000000000064133727134560014525gustar00rootroot0000000000000052 comment=6d598a0ed714201380e78130213500be6512942b sunxi-tools-1.4.2~git20181114.6d598a/000077500000000000000000000000001337271345600165005ustar00rootroot00000000000000sunxi-tools-1.4.2~git20181114.6d598a/.gitignore000066400000000000000000000001421337271345600204650ustar00rootroot00000000000000bin2fex fex2bin sunxi-bootinfo sunxi-fel sunxi-fexc sunxi-nand-part sunxi-pio version.h *.o *.swp sunxi-tools-1.4.2~git20181114.6d598a/.travis.yml000066400000000000000000000021211337271345600206050ustar00rootroot00000000000000# use container-based infrastructure sudo: false language: c # treat all warnings as errors, fake cross-toolchain (build everything on host) env: - CFLAGS="-g -O2 -Werror" CROSS_COMPILE="" 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; select make/install target before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; brew install libusb; export TARGET=tools; else export TARGET=all; fi # build (and test) using the Makefile, with a single overall status script: - make ${TARGET} && make misc && make check # run/simulate a test install after_success: - make install-${TARGET} install-misc DESTDIR=/tmp PREFIX=/sunxi-tools # turn off email notifications notifications: - email: false sunxi-tools-1.4.2~git20181114.6d598a/LICENSE.md000066400000000000000000000427451337271345600201200ustar00rootroot00000000000000### 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.2~git20181114.6d598a/Makefile000066400000000000000000000151121337271345600201400ustar00rootroot00000000000000# 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 . # Windows predefines OS in the environment (to "Windows_NT"), otherwise use uname OS ?= $(shell uname) CC ?= gcc DEFAULT_CFLAGS := -std=c99 DEFAULT_CFLAGS += -Wall -Wextra -Wno-unused-result DEFAULT_CFLAGS += -D_POSIX_C_SOURCE=200112L # Define _BSD_SOURCE, necessary to expose all endian conversions properly. # See http://linux.die.net/man/3/endian DEFAULT_CFLAGS += -D_BSD_SOURCE # glibc 2.20+ also requires _DEFAULT_SOURCE DEFAULT_CFLAGS += -D_DEFAULT_SOURCE ifeq ($(OS),NetBSD) # add explicit _NETBSD_SOURCE, see https://github.com/linux-sunxi/sunxi-tools/pull/22 DEFAULT_CFLAGS += -D_NETBSD_SOURCE endif DEFAULT_CFLAGS += -Iinclude/ # Tools useful on host and target TOOLS = sunxi-fexc sunxi-bootinfo sunxi-fel sunxi-nand-part sunxi-pio # Symlinks to sunxi-fexc FEXC_LINKS = bin2fex fex2bin # Tools which are only useful on the target TARGET_TOOLS = sunxi-meminfo # 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 = jtag-loop.sunxi fel-sdboot.sunxi uart0-helloworld-sdboot.sunxi MKSUNXIBOOT ?= mksunxiboot PATH_DIRS := $(shell echo $$PATH | sed -e 's/:/ /g') # Try to guess a suitable default ARM cross toolchain CROSS_DEFAULT := arm-none-eabi- CROSS_COMPILE ?= $(or $(shell ./find-arm-gcc.sh),$(CROSS_DEFAULT)) CROSS_CC := $(CROSS_COMPILE)gcc DESTDIR ?= PREFIX ?= /usr/local BINDIR ?= $(PREFIX)/bin .PHONY: all clean tools target-tools install install-tools install-target-tools .PHONY: check 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: make -C tests/ 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)` ZLIB = zlib ZLIB_CFLAGS ?= `pkg-config --cflags $(ZLIB)` ZLIB_LIBS ?= `pkg-config --libs $(ZLIB)` ifeq ($(OS),Windows_NT) # Windows lacks mman.h / mmap() DEFAULT_CFLAGS += -DNO_MMAP # portable_endian.h relies on winsock2 LIBS += -lws2_32 endif HOST_CFLAGS = $(DEFAULT_CFLAGS) $(CFLAGS) PROGRESS := progress.c progress.h SOC_INFO := soc_info.c soc_info.h FEL_LIB := fel_lib.c fel_lib.h SPI_FLASH:= fel-spiflash.c fel-spiflash.h fel-remotefunc-spi-data-transfer.h sunxi-fel: fel.c thunks/fel-to-spl-thunk.h $(PROGRESS) $(SOC_INFO) $(FEL_LIB) $(SPI_FLASH) $(CC) $(HOST_CFLAGS) $(LIBUSB_CFLAGS) $(ZLIB_CFLAGS) $(LDFLAGS) -o $@ \ $(filter %.c,$^) $(LIBS) $(LIBUSB_LIBS) $(ZLIB_LIBS) sunxi-nand-part: nand-part-main.c nand-part.c nand-part-a10.h nand-part-a20.h $(CC) $(HOST_CFLAGS) -c -o nand-part-main.o nand-part-main.c $(CC) $(HOST_CFLAGS) -c -o nand-part-a10.o nand-part.c -D A10 $(CC) $(HOST_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) $(HOST_CFLAGS) $(LDFLAGS) -o $@ $(filter %.c,$^) $(LIBS) phoenix_info: phoenix_info.c $(CC) $(HOST_CFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) %.bin: %.elf $(CROSS_COMPILE)objcopy -O binary $< $@ %.sunxi: %.bin $(MKSUNXIBOOT) $< $@ 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 jtag-loop.elf: jtag-loop.c jtag-loop.lds $(CROSS_CC) -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T jtag-loop.lds -Wl,-N fel-sdboot.elf: fel-sdboot.S fel-sdboot.lds $(CROSS_CC) -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_CC) -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T uart0-helloworld-sdboot.lds -Wl,-N boot_head_sun3i.elf: boot_head.S boot_head.lds $(CROSS_CC) -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_CC) -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_CC) -g $(ARM_ELF_FLAGS) $< -nostdlib -o $@ -T boot_head.lds -Wl,-N -DMACHID=0x102A sunxi-bootinfo: bootinfo.c # "preprocessed" .h files for inclusion of ARM thunk code headers: make -C thunks/ CROSS_COMPILE=$(CROSS_COMPILE) # target tools TARGET_CFLAGS = $(DEFAULT_CFLAGS) -static $(CFLAGS) sunxi-meminfo: meminfo.c $(CROSS_CC) $(TARGET_CFLAGS) -o $@ $< sunxi-script_extractor: script_extractor.c $(CROSS_CC) $(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 > $@ check: $(FEXC_LINKS) make -C tests/ sunxi-tools-1.4.2~git20181114.6d598a/README.md000066400000000000000000000132751337271345600177670ustar00rootroot00000000000000# 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) [![Commits](https://img.shields.io/github/commits-since/linux-sunxi/sunxi-tools/v1.4.svg)](https://github.com/linux-sunxi/sunxi-tools/compare/v1.4...master) 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`. Command line utilities to work with devices based on [Allwinner SoC]s: sun4i, sun5i, ... - 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 USB communication with the FEL handler built in to the CPU. You usually activate [FEL mode] by pushing the _uboot_ / _recovery_ button at poweron, or by having your device "fail over" to FEL when no other boot option is available. See http://linux-sunxi.org/FEL/USBBoot for a detailed usage guide. When called with no arguments, _sunxi-fel_ will display a short usage summary. _Note:_ Unless you select a specific device using the `--dev` or `--sid` options, the tool will access the first Allwinner device (in FEL mode) that it finds. You can print a list of all FEL devices currently connected/detected with `./sunxi-fel --list --verbose`. ### fel-gpio Simple wrapper (script) around `sunxi-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). ### sunxi-pio Manipulate PIO registers/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 Makefile will try to auto-detect a suitable toolchain prefix, and falls back to `arm-none-eabi-` otherwise. If needed, you may override this by explicitly setting *CROSS_COMPILE*.
_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`. * `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. [allwinner soc]: http://linux-sunxi.org/Allwinner_SoC_Family [fel mode]: http://linux-sunxi.org/FEL sunxi-tools-1.4.2~git20181114.6d598a/adb-devprobe.sh000077500000000000000000000033731337271345600213770ustar00rootroot00000000000000#!/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.2~git20181114.6d598a/autoversion.sh000077500000000000000000000010021337271345600214060ustar00rootroot00000000000000# # 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.2" 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.2~git20181114.6d598a/bin/000077500000000000000000000000001337271345600172505ustar00rootroot00000000000000sunxi-tools-1.4.2~git20181114.6d598a/bin/fel-sdboot.sunxi000066400000000000000000000200001337271345600223660ustar00rootroot00000000000000eGON.BT0n' SPL   / sunxi-tools-1.4.2~git20181114.6d598a/bin/jtag-loop.sunxi000066400000000000000000000010001337271345600222230ustar00rootroot00000000000000 eGON.BT0dreDDDsunxi-tools-1.4.2~git20181114.6d598a/bin/ramboot.scr000066400000000000000000000003661337271345600214310ustar00rootroot00000000000000'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.2~git20181114.6d598a/bin/ramboot.uboot-sh000066400000000000000000000002561337271345600224000ustar00rootroot00000000000000# 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.2~git20181114.6d598a/bin/uart0-helloworld-sdboot.sunxi000066400000000000000000000200001337271345600250240ustar00rootroot00000000000000eGON.BT0U SPL1"$12#5;  /P2"$12#5;  / $0Q 0220/"$05;03/@-@@8@-P@8@Q0@+@  `@ /?S2,S409&0 /5$ )$  $0 #80/hD(0 %6R0  @o/(0,0 %6R0  P//0@-8000Z S$pBPP@@-8000Z S$pPBPd@0l (l "("/@-Q&A#6@QQ7  Q00S 65727JP 3*4'4?P 3X4V003&S $&S()PP  0'S &S ())@@- 0    0@ 0/@-@0S S 09&6QQ7  Q00T SP00Q < Q$0S S///eGON.BT0 Hello from Allwinner A10! Allwinner A10s! Allwinner A13! Allwinner A20! Allwinner A31/A31s! Allwinner A64! Allwinner H2+! Allwinner H3! Allwinner H5! Allwinner R40! Allwinner V3s! 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. @-hAQ`@b #6RH4 P8/P(*0Q&S$ 3&S &S PP0'S 'S &S&2P P P xl`PXMIF>:;73*%!)!?sunxi-tools-1.4.2~git20181114.6d598a/boot_head.S000066400000000000000000000026071337271345600205550ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/boot_head.lds000066400000000000000000000016611337271345600211340ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/bootinfo.c000066400000000000000000000306351337271345600204720ustar00rootroot00000000000000/* * (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.2~git20181114.6d598a/common.h000066400000000000000000000030351337271345600201420ustar00rootroot00000000000000/* * 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 pr_error(...) fprintf(stderr, __VA_ARGS__) /** like pr_error(), but also exit program */ #define pr_fatal(...) \ do { pr_error(__VA_ARGS__); exit(EXIT_FAILURE); } while (0); #endif /* _SUNXI_TOOLS_COMMON_H */ sunxi-tools-1.4.2~git20181114.6d598a/fel-gpio000077500000000000000000000033441337271345600201340ustar00rootroot00000000000000#!/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_base=0x01c20800 pio_size=0x228 sram_addr=0x3000 # read PIO ./sunxi-fel memmove $sram_addr $pio_base $pio_size ./sunxi-fel read $sram_addr $pio_size 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 # write PIO ./sunxi-fel write $sram_addr pio.reg ./sunxi-fel memmove $pio_base $sram_addr $pio_size # (re-)read PIO ./sunxi-fel memmove $sram_addr $pio_base $pio_size ./sunxi-fel read $sram_addr $pio_size 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.2~git20181114.6d598a/fel-remotefunc-compiler.rb000077500000000000000000000122441337271345600235560ustar00rootroot00000000000000#!/usr/bin/env ruby # # (C) Copyright 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 . # if ARGV.size < 2 printf("Usage: #{$PROGRAM_NAME} [c_source_input] [marshalled_header_output]\n\n") printf("This script uses an ARM toolchain to compile native ARM code, and then\n") printf("automatically generates the necessary wrapper code for calling it from\n") printf("the sunxi-fel tool. Executing such compiled pieces of code natively on\n") printf("the device may be needed for the performance critical parts.\n") printf("\nExample input file:\n\n") printf(" unsigned sum(unsigned a, unsigned b)\n") printf(" {\n") printf(" return a + b;\n") printf(" }\n") printf("\n") printf("Using this example code inside of sunxi-fel:\n") printf("\n") printf(" uint32_t a = 1, b = 2, c;\n") printf(" aw_fel_remotefunc_prepare_sum(dev, a, b);\n") printf(" aw_fel_remotefunc_execute(dev, &c);\n") printf(" printf(\"%%d + %%d = %%d\\n\", a, b, c);\n\n") printf("If the returned result is not needed (a void function), then the second\n") printf("argument to the 'aw_fel_remotefunc_execute' function can be NULL.\n\n") exit(1) end def tool_exists(tool_name) `which #{tool_name} > /dev/null 2>&1` return $?.to_i == 0 end def parse_stack_usage(filename) return unless File.exists?(filename) File.read(filename).strip.split("\n").map do |l| if l =~ /\:([^\:\s]+)\s+(\d+)\s+(\S+)/ if $3 != "static" abort sprintf("Non-static stack usage for function '%s'\n", $1) end {function_name: $1, stack_usage: $2.to_i} else abort sprintf("Failed to parse stack usage information '%s'\n", l.strip) end end end toolchains = [ "arm-none-eabi-", "arm-linux-gnueabihf-", "arm-none-linux-gnueabi-", "armv7a-hardfloat-linux-gnueabi-", ] toolchain = toolchains.find { |toolchain| tool_exists("#{toolchain}gcc") } abort "Can't find any usable ARM crosscompiler.\n" unless toolchain # Compile the source file system("#{toolchain}gcc -c -O3 -marm -march=armv7-a -mfloat-abi=soft -fstack-usage -fpic -o #{ARGV[0]}.o #{ARGV[0]}") exit($?.to_i) if $?.to_i != 0 # Read the stack usage information stack_usage = parse_stack_usage("#{ARGV[0]}.su") if stack_usage.size != 1 abort sprintf("Expected only one function in the source file, but got %s.\n", stack_usage.map {|a| "'" + a[:function_name] + "()'" }.join(", ")) end `#{toolchain}size -A #{ARGV[0]}.o`.each_line do |l| if l =~ /(\S+)\s+(\S+)/ if ($1 == ".data" || $1 == ".bss" || $1 == ".rodata") && $2.to_i > 0 abort "Can't have non-empty '.data', '.bss' or '.rodata' section." end end end `#{toolchain}objdump -t #{ARGV[0]}.o`.each_line do |l| if l =~ /\*UND\*/ abort "External references are not allowed: '#{l.strip}'.\n" end end function_name = stack_usage[0][:function_name] # Read the source file and strip multiline C comments sourcefile = File.read(ARGV[0]).gsub(/\/\*.*?\*\//m, "") # Try to find the function and its arguments unless sourcefile =~ /#{function_name}\((.*?)\)/m abort sprintf("Can't find the function '%s()' in the source file.\n", function_name) end # Extract the function argument names function_args = $1.split(",").map {|a| if a.strip =~ /([^\*\s]+)$/ then $1 end } # Check if there is any return value have_retval = !(sourcefile =~ /void\s+#{function_name}/m) ############################################################################### # Generate output file ############################################################################### out = File.open(ARGV[1], "w") out.printf("/* Automatically generated, do not edit! */\n\n") out.printf("static void\n") funcdecl = sprintf("aw_fel_remotefunc_prepare_#{function_name}(feldev_handle *dev,") out.printf("%s\n", funcdecl) out.printf("%s", function_args.map {|a| " " * funcdecl.index("(") + " uint32_t " + a }.join(",\n")) out.printf(")\n{\n") out.printf("\tstatic uint8_t arm_code[] = {\n") `#{toolchain}objdump -d #{ARGV[0]}.o`.each_line {|l| next unless l =~ /(\h+)\:\s+(\h+)\s+(\S+)\s+([^;]*)/ addr = $1 opcode = $2 p1 = $3 p2 = $4.strip opcode = opcode.scan(/../).map {|a| "0x" + a }.reverse.join(", ") out.printf("\t\t%s, /* %4s: %-8s %-34s \x2a/\n", opcode, addr, p1, p2) } out.printf("\t};\n") out.printf("\tuint32_t args[] = {\n\t\t") out.printf("%s\n\t};\n", function_args.join(",\n\t\t")) out.printf("\taw_fel_remotefunc_prepare(dev, %d, arm_code, sizeof(arm_code), %d, args);\n", stack_usage[0][:stack_usage], function_args.size) out.printf("}\n") sunxi-tools-1.4.2~git20181114.6d598a/fel-remotefunc-spi-data-transfer.c000066400000000000000000000125031337271345600251020ustar00rootroot00000000000000/* * Copyright © 2016 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. */ typedef unsigned int u32; typedef unsigned char u8; #define readl(addr) (*((volatile u32 *)(addr))) #define writel(v, addr) (*((volatile u32 *)(addr)) = (u32)(v)) #define readb(addr) (*((volatile u8 *)(addr))) #define writeb(v, addr) (*((volatile u8 *)(addr)) = (u8)(v)) /* * This is a basic full-duplex SPI data transfer function (we are sending a * block of data and receiving the same amount of data back), doing the job * without any help from DMA. And because we can be running in some rather * adverse conditions (with default PMIC settings, low CPU clock speed and * CPU caches disabled), it is necessary to use 32-bit accesses to read/write * the FIFO buffers. As a result, Allwinner A13 with the default 408MHz CPU * clock speed can successfully handle at least 12 MHz SPI clock speed. * * Supports both sun4i and sun6i variants of the SPI controller (they only * need different hardware register addresses passed as arguments). */ static void inline __attribute((always_inline)) spi_data_transfer(void *buf, u32 bufsize, void *spi_ctl_reg, u32 spi_ctl_xch_bitmask, void *spi_fifo_reg, void *spi_tx_reg, void *spi_rx_reg, void *spi_bc_reg, void *spi_tc_reg, void *spi_bcc_reg) { u32 cnt; u32 rxsize = bufsize; u32 txsize = bufsize; u8 *rxbuf8 = buf; u8 *txbuf8 = buf; u32 *rxbuf; u32 *txbuf; u32 cpsr; /* sun6i uses 3 registers, sun4i only needs 2 */ writel(bufsize, spi_bc_reg); writel(bufsize, spi_tc_reg); if (spi_bcc_reg) writel(bufsize, spi_bcc_reg); /* Fill the TX buffer with some initial data */ cnt = (-(u32)txbuf8 & 3) + 60; if (cnt > txsize) cnt = txsize; while (cnt-- > 0) { writeb(*txbuf8++, spi_tx_reg); txsize--; } /* Temporarily disable IRQ & FIQ */ asm volatile("mrs %0, cpsr" : "=r" (cpsr)); asm volatile("msr cpsr_c, %0" :: "r" (cpsr | 0xC0)); /* Start the data transfer */ writel(readl(spi_ctl_reg) | spi_ctl_xch_bitmask, spi_ctl_reg); /* Read the initial unaligned part of the data */ cnt = (-(u32)rxbuf8 & 3); if (cnt > rxsize) cnt = rxsize; while (cnt > 0) { u32 fiforeg = readl(spi_fifo_reg); int rxfifo = fiforeg & 0x7F; if (rxfifo > 0) { *rxbuf8++ = readb(spi_rx_reg); cnt--; rxsize--; } } /* Fast processing of the aligned part (read/write 32-bit at a time) */ rxbuf = (u32 *)rxbuf8; txbuf = (u32 *)txbuf8; while (rxsize >= 4) { u32 fiforeg = readl(spi_fifo_reg); int rxfifo = fiforeg & 0x7F; int txfifo = (fiforeg >> 16) & 0x7F; if (rxfifo >= 4) { *rxbuf++ = readl(spi_rx_reg); rxsize -= 4; } if (txfifo < 60 && txsize >= 4) { writel(*txbuf++, spi_tx_reg); txsize -= 4; } } /* Handle the trailing part pf the data */ rxbuf8 = (u8 *)rxbuf; txbuf8 = (u8 *)txbuf; while (rxsize >= 1) { u32 fiforeg = readl(spi_fifo_reg); int rxfifo = fiforeg & 0x7F; int txfifo = (fiforeg >> 16) & 0x7F; if (rxfifo >= 1) { *rxbuf8++ = readb(spi_rx_reg); rxsize -= 1; } if (txfifo < 60 && txsize >= 1) { writeb(*txbuf8++, spi_tx_reg); txsize -= 1; } } /* Restore CPSR */ asm volatile("msr cpsr_c, %0" :: "r" (cpsr)); } void spi_batch_data_transfer(u8 *buf, void *spi_ctl_reg, u32 spi_ctl_xch_bitmask, void *spi_fifo_reg, void *spi_tx_reg, void *spi_rx_reg, void *spi_bc_reg, void *spi_tc_reg, void *spi_bcc_reg) { u8 wait_for_completion_cmd[2]; u8 *backup_buf; u32 bufsize; while (1) { u32 code = (buf[0] << 8) | buf[1]; /* End of data */ if (code == 0) return; if (code == 0xFFFF) { /* Wait for completion, part 1 */ backup_buf = buf; buf = wait_for_completion_cmd; wait_for_completion_cmd[0] = 0x05; bufsize = 2; } else { /* Normal buffer */ buf += 2; bufsize = code; } spi_data_transfer(buf, bufsize, spi_ctl_reg, spi_ctl_xch_bitmask, spi_fifo_reg, spi_tx_reg, spi_rx_reg, spi_bc_reg, spi_tc_reg, spi_bcc_reg); buf += bufsize; if (code == 0xFFFF) { /* Wait for completion, part 2 */ buf = backup_buf; if (wait_for_completion_cmd[1] & 1) { /* Still busy */ continue; } /* Advance to the next code */ buf = backup_buf + 2; } } } sunxi-tools-1.4.2~git20181114.6d598a/fel-remotefunc-spi-data-transfer.h000066400000000000000000000262721337271345600251170ustar00rootroot00000000000000/* Automatically generated, do not edit! */ static void aw_fel_remotefunc_prepare_spi_batch_data_transfer(feldev_handle *dev, uint32_t buf, uint32_t spi_ctl_reg, uint32_t spi_ctl_xch_bitmask, uint32_t spi_fifo_reg, uint32_t spi_tx_reg, uint32_t spi_rx_reg, uint32_t spi_bc_reg, uint32_t spi_tc_reg, uint32_t spi_bcc_reg) { static uint8_t arm_code[] = { 0xf0, 0x0f, 0x2d, 0xe9, /* 0: push {r4, r5, r6, r7, r8, r9, sl, fp} */ 0x18, 0xd0, 0x4d, 0xe2, /* 4: sub sp, sp, #24 */ 0x38, 0x50, 0x9d, 0xe5, /* 8: ldr r5, [sp, #56] */ 0x3c, 0x60, 0x9d, 0xe5, /* c: ldr r6, [sp, #60] */ 0x06, 0x00, 0x8d, 0xe9, /* 10: stmib sp, {r1, r2} */ 0x00, 0xa0, 0xd0, 0xe5, /* 14: ldrb sl, [r0] */ 0x01, 0x20, 0xd0, 0xe5, /* 18: ldrb r2, [r0, #1] */ 0x0a, 0xa4, 0x92, 0xe1, /* 1c: orrs sl, r2, sl, lsl #8 */ 0x6a, 0x00, 0x00, 0x0a, /* 20: beq 1d0 */ 0xff, 0x2f, 0x0f, 0xe3, /* 24: movw r2, #65535 */ 0x02, 0x00, 0x5a, 0xe1, /* 28: cmp sl, r2 */ 0x18, 0x80, 0x8d, 0x02, /* 2c: addeq r8, sp, #24 */ 0x02, 0x80, 0x80, 0x12, /* 30: addne r8, r0, #2 */ 0x05, 0xb0, 0xa0, 0x03, /* 34: moveq fp, #5 */ 0x48, 0xc0, 0x9d, 0xe5, /* 38: ldr ip, [sp, #72] */ 0x08, 0xb0, 0x68, 0x05, /* 3c: strbeq fp, [r8, #-8]! */ 0x00, 0x10, 0x68, 0xe2, /* 40: rsb r1, r8, #0 */ 0x40, 0x20, 0x9d, 0xe5, /* 44: ldr r2, [sp, #64] */ 0x03, 0x10, 0x01, 0xe2, /* 48: and r1, r1, #3 */ 0x44, 0xb0, 0x9d, 0xe5, /* 4c: ldr fp, [sp, #68] */ 0x0a, 0x70, 0xa0, 0x11, /* 50: movne r7, sl */ 0x0c, 0x00, 0x8d, 0x05, /* 54: streq r0, [sp, #12] */ 0x3c, 0x00, 0x81, 0xe2, /* 58: add r0, r1, #60 */ 0x02, 0x70, 0xa0, 0x03, /* 5c: moveq r7, #2 */ 0x00, 0x00, 0x5c, 0xe3, /* 60: cmp ip, #0 */ 0x00, 0x70, 0x82, 0xe5, /* 64: str r7, [r2] */ 0x08, 0x20, 0xa0, 0xe1, /* 68: mov r2, r8 */ 0x00, 0x70, 0x8b, 0xe5, /* 6c: str r7, [fp] */ 0x00, 0x70, 0x8c, 0x15, /* 70: strne r7, [ip] */ 0x07, 0x00, 0x50, 0xe1, /* 74: cmp r0, r7 */ 0x07, 0x00, 0xa0, 0x21, /* 78: movcs r0, r7 */ 0x00, 0x40, 0x88, 0xe0, /* 7c: add r4, r8, r0 */ 0x01, 0xc0, 0xd2, 0xe4, /* 80: ldrb ip, [r2], #1 */ 0x04, 0x00, 0x52, 0xe1, /* 84: cmp r2, r4 */ 0x00, 0xc0, 0xc5, 0xe5, /* 88: strb ip, [r5] */ 0xfb, 0xff, 0xff, 0x1a, /* 8c: bne 80 */ 0x07, 0x00, 0x60, 0xe0, /* 90: rsb r0, r0, r7 */ 0x00, 0x90, 0x0f, 0xe1, /* 94: mrs r9, CPSR */ 0xc0, 0xc0, 0x89, 0xe3, /* 98: orr ip, r9, #192 */ 0x0c, 0xf0, 0x21, 0xe1, /* 9c: msr CPSR_c, ip */ 0x04, 0xc0, 0x9d, 0xe5, /* a0: ldr ip, [sp, #4] */ 0x07, 0x00, 0x51, 0xe1, /* a4: cmp r1, r7 */ 0x07, 0x10, 0xa0, 0x21, /* a8: movcs r1, r7 */ 0x08, 0xb0, 0x9d, 0xe5, /* ac: ldr fp, [sp, #8] */ 0x00, 0x40, 0x9c, 0xe5, /* b0: ldr r4, [ip] */ 0x01, 0xc0, 0x88, 0xe0, /* b4: add ip, r8, r1 */ 0x00, 0xc0, 0x8d, 0xe5, /* b8: str ip, [sp] */ 0x08, 0xc0, 0xa0, 0xe1, /* bc: mov ip, r8 */ 0x0b, 0x40, 0x84, 0xe1, /* c0: orr r4, r4, fp */ 0x04, 0xb0, 0x9d, 0xe5, /* c4: ldr fp, [sp, #4] */ 0x00, 0x40, 0x8b, 0xe5, /* c8: str r4, [fp] */ 0x00, 0xb0, 0x9d, 0xe5, /* cc: ldr fp, [sp] */ 0x0b, 0x00, 0x5c, 0xe1, /* d0: cmp ip, fp */ 0x06, 0x00, 0x00, 0x0a, /* d4: beq f4 */ 0x00, 0x40, 0x93, 0xe5, /* d8: ldr r4, [r3] */ 0x7f, 0x00, 0x14, 0xe3, /* dc: tst r4, #127 */ 0xfc, 0xff, 0xff, 0x0a, /* e0: beq d8 */ 0x00, 0x40, 0xd6, 0xe5, /* e4: ldrb r4, [r6] */ 0x01, 0x40, 0xcc, 0xe4, /* e8: strb r4, [ip], #1 */ 0x0b, 0x00, 0x5c, 0xe1, /* ec: cmp ip, fp */ 0xf8, 0xff, 0xff, 0x1a, /* f0: bne d8 */ 0x07, 0x10, 0x61, 0xe0, /* f4: rsb r1, r1, r7 */ 0x03, 0x00, 0x51, 0xe3, /* f8: cmp r1, #3 */ 0x12, 0x00, 0x00, 0x9a, /* fc: bls 14c */ 0x00, 0x40, 0x93, 0xe5, /* 100: ldr r4, [r3] */ 0x7f, 0xb0, 0x04, 0xe2, /* 104: and fp, r4, #127 */ 0x54, 0x48, 0xe6, 0xe7, /* 108: ubfx r4, r4, #16, #7 */ 0x03, 0x00, 0x5b, 0xe3, /* 10c: cmp fp, #3 */ 0x04, 0x10, 0x41, 0xc2, /* 110: subgt r1, r1, #4 */ 0x00, 0xb0, 0x96, 0xc5, /* 114: ldrgt fp, [r6] */ 0x04, 0xb0, 0x8c, 0xc4, /* 118: strgt fp, [ip], #4 */ 0x03, 0x00, 0x50, 0xe3, /* 11c: cmp r0, #3 */ 0x00, 0xb0, 0xa0, 0x93, /* 120: movls fp, #0 */ 0x01, 0xb0, 0xa0, 0x83, /* 124: movhi fp, #1 */ 0x3b, 0x00, 0x54, 0xe3, /* 128: cmp r4, #59 */ 0x00, 0xb0, 0xa0, 0xc3, /* 12c: movgt fp, #0 */ 0x00, 0x00, 0x5b, 0xe3, /* 130: cmp fp, #0 */ 0xef, 0xff, 0xff, 0x0a, /* 134: beq f8 */ 0x04, 0x40, 0x92, 0xe4, /* 138: ldr r4, [r2], #4 */ 0x03, 0x00, 0x51, 0xe3, /* 13c: cmp r1, #3 */ 0x04, 0x00, 0x40, 0xe2, /* 140: sub r0, r0, #4 */ 0x00, 0x40, 0x85, 0xe5, /* 144: str r4, [r5] */ 0xec, 0xff, 0xff, 0x8a, /* 148: bhi 100 */ 0x00, 0x00, 0x51, 0xe3, /* 14c: cmp r1, #0 */ 0x10, 0x00, 0x00, 0x0a, /* 150: beq 198 */ 0x00, 0x40, 0x93, 0xe5, /* 154: ldr r4, [r3] */ 0x7f, 0x00, 0x14, 0xe3, /* 158: tst r4, #127 */ 0x54, 0x48, 0xe6, 0xe7, /* 15c: ubfx r4, r4, #16, #7 */ 0x01, 0x10, 0x41, 0x12, /* 160: subne r1, r1, #1 */ 0x00, 0xb0, 0xd6, 0x15, /* 164: ldrbne fp, [r6] */ 0x01, 0xb0, 0xcc, 0x14, /* 168: strbne fp, [ip], #1 */ 0x00, 0xb0, 0x90, 0xe2, /* 16c: adds fp, r0, #0 */ 0x01, 0xb0, 0xa0, 0x13, /* 170: movne fp, #1 */ 0x3b, 0x00, 0x54, 0xe3, /* 174: cmp r4, #59 */ 0x00, 0xb0, 0xa0, 0xc3, /* 178: movgt fp, #0 */ 0x00, 0x00, 0x5b, 0xe3, /* 17c: cmp fp, #0 */ 0xf1, 0xff, 0xff, 0x0a, /* 180: beq 14c */ 0x01, 0x40, 0xd2, 0xe4, /* 184: ldrb r4, [r2], #1 */ 0x00, 0x00, 0x51, 0xe3, /* 188: cmp r1, #0 */ 0x01, 0x00, 0x40, 0xe2, /* 18c: sub r0, r0, #1 */ 0x00, 0x40, 0xc5, 0xe5, /* 190: strb r4, [r5] */ 0xee, 0xff, 0xff, 0x1a, /* 194: bne 154 */ 0x09, 0xf0, 0x21, 0xe1, /* 198: msr CPSR_c, r9 */ 0xff, 0xcf, 0x0f, 0xe3, /* 19c: movw ip, #65535 */ 0x0c, 0x00, 0x5a, 0xe1, /* 1a0: cmp sl, ip */ 0x07, 0x00, 0x88, 0x10, /* 1a4: addne r0, r8, r7 */ 0x99, 0xff, 0xff, 0x1a, /* 1a8: bne 14 */ 0x11, 0x20, 0xdd, 0xe5, /* 1ac: ldrb r2, [sp, #17] */ 0x01, 0x00, 0x12, 0xe3, /* 1b0: tst r2, #1 */ 0x08, 0x00, 0x00, 0x1a, /* 1b4: bne 1dc */ 0x0c, 0xb0, 0x9d, 0xe5, /* 1b8: ldr fp, [sp, #12] */ 0x02, 0x00, 0x8b, 0xe2, /* 1bc: add r0, fp, #2 */ 0x00, 0xa0, 0xd0, 0xe5, /* 1c0: ldrb sl, [r0] */ 0x01, 0x20, 0xd0, 0xe5, /* 1c4: ldrb r2, [r0, #1] */ 0x0a, 0xa4, 0x92, 0xe1, /* 1c8: orrs sl, r2, sl, lsl #8 */ 0x94, 0xff, 0xff, 0x1a, /* 1cc: bne 24 */ 0x18, 0xd0, 0x8d, 0xe2, /* 1d0: add sp, sp, #24 */ 0xf0, 0x0f, 0xbd, 0xe8, /* 1d4: pop {r4, r5, r6, r7, r8, r9, sl, fp} */ 0x1e, 0xff, 0x2f, 0xe1, /* 1d8: bx lr */ 0x0c, 0x00, 0x9d, 0xe5, /* 1dc: ldr r0, [sp, #12] */ 0x8b, 0xff, 0xff, 0xea, /* 1e0: b 14 */ }; uint32_t args[] = { buf, spi_ctl_reg, spi_ctl_xch_bitmask, spi_fifo_reg, spi_tx_reg, spi_rx_reg, spi_bc_reg, spi_tc_reg, spi_bcc_reg }; aw_fel_remotefunc_prepare(dev, 56, arm_code, sizeof(arm_code), 9, args); } sunxi-tools-1.4.2~git20181114.6d598a/fel-sdboot.S000066400000000000000000000042611337271345600206650ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/fel-sdboot.lds000066400000000000000000000016551337271345600212510ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/fel-spiflash.c000066400000000000000000000334201337271345600212230ustar00rootroot00000000000000/* * (C) Copyright 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 . */ #include #include #include #include "fel_lib.h" #include "progress.h" #include "fel-remotefunc-spi-data-transfer.h" /*****************************************************************************/ typedef struct { uint32_t id; uint8_t write_enable_cmd; uint8_t large_erase_cmd; uint32_t large_erase_size; uint8_t small_erase_cmd; uint32_t small_erase_size; uint8_t program_cmd; uint32_t program_size; char *text_description; } spi_flash_info_t; spi_flash_info_t spi_flash_info[] = { { .id = 0xEF40, .write_enable_cmd = 0x6, .large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024, .small_erase_cmd = 0x20, .small_erase_size = 4 * 1024, .program_cmd = 0x02, .program_size = 256, .text_description = "Winbond W25Qxx" }, { .id = 0xC220, .write_enable_cmd = 0x6, .large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024, .small_erase_cmd = 0x20, .small_erase_size = 4 * 1024, .program_cmd = 0x02, .program_size = 256, .text_description = "Macronix MX25Lxxxx" }, }; spi_flash_info_t default_spi_flash_info = { .id = 0x0000, .write_enable_cmd = 0x6, .large_erase_cmd = 0xD8, .large_erase_size = 64 * 1024, .small_erase_cmd = 0x20, .small_erase_size = 4 * 1024, .program_cmd = 0x02, .program_size = 256, .text_description = "Unknown", }; /*****************************************************************************/ uint32_t fel_readl(feldev_handle *dev, uint32_t addr); void fel_writel(feldev_handle *dev, uint32_t addr, uint32_t val); #define readl(addr) fel_readl(dev, (addr)) #define writel(val, addr) fel_writel(dev, (addr), (val)) #define PA (0) #define PB (1) #define PC (2) #define CCM_SPI0_CLK (0x01C20000 + 0xA0) #define CCM_AHB_GATING0 (0x01C20000 + 0x60) #define CCM_AHB_GATE_SPI0 (1 << 20) #define SUN6I_BUS_SOFT_RST_REG0 (0x01C20000 + 0x2C0) #define SUN6I_SPI0_RST (1 << 20) #define SUNXI_GPC_SPI0 (3) #define SUN50I_GPC_SPI0 (4) #define SUN4I_CTL_ENABLE (1 << 0) #define SUN4I_CTL_MASTER (1 << 1) #define SUN4I_CTL_TF_RST (1 << 8) #define SUN4I_CTL_RF_RST (1 << 9) #define SUN4I_CTL_XCH (1 << 10) #define SUN6I_TCR_XCH (1 << 31) #define SUN4I_SPI0_CCTL (0x01C05000 + 0x1C) #define SUN4I_SPI0_CTL (0x01C05000 + 0x08) #define SUN4I_SPI0_RX (0x01C05000 + 0x00) #define SUN4I_SPI0_TX (0x01C05000 + 0x04) #define SUN4I_SPI0_FIFO_STA (0x01C05000 + 0x28) #define SUN4I_SPI0_BC (0x01C05000 + 0x20) #define SUN4I_SPI0_TC (0x01C05000 + 0x24) #define SUN6I_SPI0_CCTL (0x01C68000 + 0x24) #define SUN6I_SPI0_GCR (0x01C68000 + 0x04) #define SUN6I_SPI0_TCR (0x01C68000 + 0x08) #define SUN6I_SPI0_FIFO_STA (0x01C68000 + 0x1C) #define SUN6I_SPI0_MBC (0x01C68000 + 0x30) #define SUN6I_SPI0_MTC (0x01C68000 + 0x34) #define SUN6I_SPI0_BCC (0x01C68000 + 0x38) #define SUN6I_SPI0_TXD (0x01C68000 + 0x200) #define SUN6I_SPI0_RXD (0x01C68000 + 0x300) #define CCM_SPI0_CLK_DIV_BY_2 (0x1000) #define CCM_SPI0_CLK_DIV_BY_4 (0x1001) #define CCM_SPI0_CLK_DIV_BY_6 (0x1002) /* * Configure pin function on a GPIO port */ static void gpio_set_cfgpin(feldev_handle *dev, int port_num, int pin_num, int val) { uint32_t port_base = 0x01C20800 + port_num * 0x24; uint32_t cfg_reg = port_base + 4 * (pin_num / 8); uint32_t pin_idx = pin_num % 8; uint32_t x = readl(cfg_reg); x &= ~(0x7 << (pin_idx * 4)); x |= val << (pin_idx * 4); writel(x, cfg_reg); } static bool spi_is_sun6i(feldev_handle *dev) { soc_info_t *soc_info = dev->soc_info; switch (soc_info->soc_id) { case 0x1623: /* A10 */ case 0x1625: /* A13 */ case 0x1651: /* A20 */ return false; default: return true; } } /* * Init the SPI0 controller and setup pins muxing. */ static bool spi0_init(feldev_handle *dev) { uint32_t reg_val; soc_info_t *soc_info = dev->soc_info; if (!soc_info) return false; /* Setup SPI0 pins muxing */ switch (soc_info->soc_id) { case 0x1625: /* Allwinner A13 */ case 0x1680: /* Allwinner H3 */ case 0x1718: /* Allwinner H5 */ gpio_set_cfgpin(dev, PC, 0, SUNXI_GPC_SPI0); gpio_set_cfgpin(dev, PC, 1, SUNXI_GPC_SPI0); gpio_set_cfgpin(dev, PC, 2, SUNXI_GPC_SPI0); gpio_set_cfgpin(dev, PC, 3, SUNXI_GPC_SPI0); break; case 0x1689: /* Allwinner A64 */ gpio_set_cfgpin(dev, PC, 0, SUN50I_GPC_SPI0); gpio_set_cfgpin(dev, PC, 1, SUN50I_GPC_SPI0); gpio_set_cfgpin(dev, PC, 2, SUN50I_GPC_SPI0); gpio_set_cfgpin(dev, PC, 3, SUN50I_GPC_SPI0); break; default: /* Unknown/Unsupported SoC */ return false; } reg_val = readl(CCM_AHB_GATING0); reg_val |= CCM_AHB_GATE_SPI0; writel(reg_val, CCM_AHB_GATING0); /* 24MHz from OSC24M */ writel((1 << 31), CCM_SPI0_CLK); /* divide by 4 */ writel(CCM_SPI0_CLK_DIV_BY_4, spi_is_sun6i(dev) ? SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL); if (spi_is_sun6i(dev)) { /* Deassert SPI0 reset */ reg_val = readl(SUN6I_BUS_SOFT_RST_REG0); reg_val |= SUN6I_SPI0_RST; writel(reg_val, SUN6I_BUS_SOFT_RST_REG0); /* Enable SPI in the master mode and do a soft reset */ reg_val = readl(SUN6I_SPI0_GCR); reg_val |= (1 << 31) | 3; writel(reg_val, SUN6I_SPI0_GCR); /* Wait for completion */ while (readl(SUN6I_SPI0_GCR) & (1 << 31)) {} } else { reg_val = readl(SUN4I_SPI0_CTL); reg_val |= SUN4I_CTL_MASTER; reg_val |= SUN4I_CTL_ENABLE | SUN4I_CTL_TF_RST | SUN4I_CTL_RF_RST; writel(reg_val, SUN4I_SPI0_CTL); } return true; } /* * Backup/restore the initial portion of the SRAM, which can be used as * a temporary data buffer. */ static void *backup_sram(feldev_handle *dev) { soc_info_t *soc_info = dev->soc_info; size_t bufsize = soc_info->scratch_addr - soc_info->spl_addr; void *buf = malloc(bufsize); aw_fel_read(dev, soc_info->spl_addr, buf, bufsize); return buf; } static void restore_sram(feldev_handle *dev, void *buf) { soc_info_t *soc_info = dev->soc_info; size_t bufsize = soc_info->scratch_addr - soc_info->spl_addr; aw_fel_write(dev, buf, soc_info->spl_addr, bufsize); free(buf); } static void prepare_spi_batch_data_transfer(feldev_handle *dev, uint32_t buf) { if (spi_is_sun6i(dev)) { aw_fel_remotefunc_prepare_spi_batch_data_transfer(dev, buf, SUN6I_SPI0_TCR, SUN6I_TCR_XCH, SUN6I_SPI0_FIFO_STA, SUN6I_SPI0_TXD, SUN6I_SPI0_RXD, SUN6I_SPI0_MBC, SUN6I_SPI0_MTC, SUN6I_SPI0_BCC); } else { aw_fel_remotefunc_prepare_spi_batch_data_transfer(dev, buf, SUN4I_SPI0_CTL, SUN4I_CTL_XCH, SUN4I_SPI0_FIFO_STA, SUN4I_SPI0_TX, SUN4I_SPI0_RX, SUN4I_SPI0_BC, SUN4I_SPI0_TC, 0); } } /* * Read data from the SPI flash. Use the first 4KiB of SRAM as the data buffer. */ void aw_fel_spiflash_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len, progress_cb_t progress) { soc_info_t *soc_info = dev->soc_info; void *backup = backup_sram(dev); uint8_t *buf8 = (uint8_t *)buf; size_t max_chunk_size = soc_info->scratch_addr - soc_info->spl_addr; if (max_chunk_size > 0x1000) max_chunk_size = 0x1000; uint8_t *cmdbuf = malloc(max_chunk_size); memset(cmdbuf, 0, max_chunk_size); aw_fel_write(dev, cmdbuf, soc_info->spl_addr, max_chunk_size); if (!spi0_init(dev)) return; prepare_spi_batch_data_transfer(dev, soc_info->spl_addr); progress_start(progress, len); while (len > 0) { size_t chunk_size = len; if (chunk_size > max_chunk_size - 8) chunk_size = max_chunk_size - 8; memset(cmdbuf, 0, max_chunk_size); cmdbuf[0] = (chunk_size + 4) >> 8; cmdbuf[1] = (chunk_size + 4); cmdbuf[2] = 3; cmdbuf[3] = offset >> 16; cmdbuf[4] = offset >> 8; cmdbuf[5] = offset; if (chunk_size == max_chunk_size - 8) aw_fel_write(dev, cmdbuf, soc_info->spl_addr, 6); else aw_fel_write(dev, cmdbuf, soc_info->spl_addr, chunk_size + 8); aw_fel_remotefunc_execute(dev, NULL); aw_fel_read(dev, soc_info->spl_addr + 6, buf8, chunk_size); len -= chunk_size; offset += chunk_size; buf8 += chunk_size; progress_update(chunk_size); } free(cmdbuf); restore_sram(dev, backup); } /* * Write data to the SPI flash. Use the first 4KiB of SRAM as the data buffer. */ #define CMD_WRITE_ENABLE 0x06 void aw_fel_spiflash_write_helper(feldev_handle *dev, uint32_t offset, void *buf, size_t len, size_t erase_size, uint8_t erase_cmd, size_t program_size, uint8_t program_cmd) { soc_info_t *soc_info = dev->soc_info; uint8_t *buf8 = (uint8_t *)buf; size_t max_chunk_size = soc_info->scratch_addr - soc_info->spl_addr; size_t cmd_idx; if (max_chunk_size > 0x1000) max_chunk_size = 0x1000; uint8_t *cmdbuf = malloc(max_chunk_size); cmd_idx = 0; prepare_spi_batch_data_transfer(dev, soc_info->spl_addr); while (len > 0) { while (len > 0 && max_chunk_size - cmd_idx > program_size + 64) { if (offset % erase_size == 0) { /* Emit write enable command */ cmdbuf[cmd_idx++] = 0; cmdbuf[cmd_idx++] = 1; cmdbuf[cmd_idx++] = CMD_WRITE_ENABLE; /* Emit erase command */ cmdbuf[cmd_idx++] = 0; cmdbuf[cmd_idx++] = 4; cmdbuf[cmd_idx++] = erase_cmd; cmdbuf[cmd_idx++] = offset >> 16; cmdbuf[cmd_idx++] = offset >> 8; cmdbuf[cmd_idx++] = offset; /* Emit wait for completion */ cmdbuf[cmd_idx++] = 0xFF; cmdbuf[cmd_idx++] = 0xFF; } /* Emit write enable command */ cmdbuf[cmd_idx++] = 0; cmdbuf[cmd_idx++] = 1; cmdbuf[cmd_idx++] = CMD_WRITE_ENABLE; /* Emit page program command */ size_t write_count = program_size; if (write_count > len) write_count = len; cmdbuf[cmd_idx++] = (4 + write_count) >> 8; cmdbuf[cmd_idx++] = 4 + write_count; cmdbuf[cmd_idx++] = program_cmd; cmdbuf[cmd_idx++] = offset >> 16; cmdbuf[cmd_idx++] = offset >> 8; cmdbuf[cmd_idx++] = offset; memcpy(cmdbuf + cmd_idx, buf8, write_count); cmd_idx += write_count; buf8 += write_count; len -= write_count; offset += write_count; /* Emit wait for completion */ cmdbuf[cmd_idx++] = 0xFF; cmdbuf[cmd_idx++] = 0xFF; } /* Emit the end marker */ cmdbuf[cmd_idx++] = 0; cmdbuf[cmd_idx++] = 0; /* Flush */ aw_fel_write(dev, cmdbuf, soc_info->spl_addr, cmd_idx); aw_fel_remotefunc_execute(dev, NULL); cmd_idx = 0; } free(cmdbuf); } void aw_fel_spiflash_write(feldev_handle *dev, uint32_t offset, void *buf, size_t len, progress_cb_t progress) { void *backup = backup_sram(dev); uint8_t *buf8 = (uint8_t *)buf; spi_flash_info_t *flash_info = &default_spi_flash_info; /* FIXME */ if ((offset % flash_info->small_erase_size) != 0) { fprintf(stderr, "aw_fel_spiflash_write: 'addr' must be %d bytes aligned\n", flash_info->small_erase_size); exit(1); } if (!spi0_init(dev)) return; progress_start(progress, len); while (len > 0) { size_t write_count; if ((offset % flash_info->large_erase_size) != 0 || len < flash_info->large_erase_size) { write_count = flash_info->small_erase_size; if (write_count > len) write_count = len; aw_fel_spiflash_write_helper(dev, offset, buf8, write_count, flash_info->small_erase_size, flash_info->small_erase_cmd, flash_info->program_size, flash_info->program_cmd); } else { write_count = flash_info->large_erase_size; if (write_count > len) write_count = len; aw_fel_spiflash_write_helper(dev, offset, buf8, write_count, flash_info->large_erase_size, flash_info->large_erase_cmd, flash_info->program_size, flash_info->program_cmd); } len -= write_count; offset += write_count; buf8 += write_count; progress_update(write_count); } restore_sram(dev, backup); } /* * Use the read JEDEC ID (9Fh) command. */ void aw_fel_spiflash_info(feldev_handle *dev) { soc_info_t *soc_info = dev->soc_info; const char *manufacturer; unsigned char buf[] = { 0, 4, 0x9F, 0, 0, 0, 0x0, 0x0 }; void *backup = backup_sram(dev); if (!spi0_init(dev)) return; aw_fel_write(dev, buf, soc_info->spl_addr, sizeof(buf)); prepare_spi_batch_data_transfer(dev, soc_info->spl_addr); aw_fel_remotefunc_execute(dev, NULL); aw_fel_read(dev, soc_info->spl_addr, buf, sizeof(buf)); restore_sram(dev, backup); /* Assume that the MISO pin is either pulled up or down */ if (buf[5] == 0x00 || buf[5] == 0xFF) { printf("No SPI flash detected.\n"); return; } switch (buf[3]) { case 0xEF: manufacturer = "Winbond"; break; case 0xC2: manufacturer = "Macronix"; break; default: manufacturer = "Unknown"; break; } printf("Manufacturer: %s (%02Xh), model: %02Xh, size: %d bytes.\n", manufacturer, buf[3], buf[4], (1 << buf[5])); } /* * Show a help message about the available "spiflash-*" commands. */ void aw_fel_spiflash_help(void) { printf(" spiflash-info Retrieves basic information\n" " spiflash-read addr length file Write SPI flash contents into file\n" " spiflash-write addr file Store file contents into SPI flash\n"); } sunxi-tools-1.4.2~git20181114.6d598a/fel-spiflash.h000066400000000000000000000023041337271345600212250ustar00rootroot00000000000000/* * (C) Copyright 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 . */ #ifndef _SUNXI_TOOLS_FEL_SPIFLASH_H #define _SUNXI_TOOLS_FEL_SPIFLASH_H #include "fel_lib.h" #include "progress.h" void aw_fel_spiflash_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len, progress_cb_t progress); void aw_fel_spiflash_write(feldev_handle *dev, uint32_t offset, void *buf, size_t len, progress_cb_t progress); void aw_fel_spiflash_info(feldev_handle *dev); void aw_fel_spiflash_help(void); void aw_fel_spi0_init(feldev_handle *dev); #endif sunxi-tools-1.4.2~git20181114.6d598a/fel.c000066400000000000000000001364531337271345600174260ustar00rootroot00000000000000/* * 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 "fel_lib.h" #include "fel-spiflash.h" #include #include #include #include #include #include #include #include #include #include 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 */ /* printf-style output, but only if "verbose" flag is active */ #define pr_info(...) \ do { if (verbose) printf(__VA_ARGS__); } while (0); /* 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 /* * Legacy format image U-Boot header, * all data in network byte order (aka natural aka bigendian). * Taken from ${U-BOOT}/include/image.h */ typedef struct image_header { uint32_t ih_magic; /* Image Header Magic Number */ uint32_t ih_hcrc; /* Image Header CRC Checksum */ uint32_t ih_time; /* Image Creation Timestamp */ uint32_t ih_size; /* Image Data Size */ uint32_t ih_load; /* Data Load Address */ uint32_t ih_ep; /* Entry Point Address */ uint32_t ih_dcrc; /* Image Data CRC Checksum */ uint8_t ih_os; /* Operating System */ uint8_t ih_arch; /* CPU architecture */ uint8_t ih_type; /* Image Type */ uint8_t ih_comp; /* Compression Type */ uint8_t ih_name[IH_NMLEN]; /* Image Name */ } image_header_t; #define HEADER_NAME_OFFSET offsetof(image_header_t, ih_name) #define HEADER_SIZE sizeof(image_header_t) /* * 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) { image_header_t *hdr = (image_header_t *)buf; if (len <= HEADER_SIZE) /* insufficient length/size */ return IH_TYPE_INVALID; if (be32toh(hdr->ih_magic) != IH_MAGIC) /* signature mismatch */ return IH_TYPE_INVALID; /* For sunxi, we always expect ARM architecture here */ if (hdr->ih_arch != IH_ARCH_ARM) return IH_TYPE_ARCH_MISMATCH; /* assume a valid header, and return ih_type */ return hdr->ih_type; } void aw_fel_print_version(feldev_handle *dev) { struct aw_fel_version buf = dev->soc_version; const char *soc_name = dev->soc_name; if (soc_name[0] == '0') /* hexadecimal ID -> unknown SoC */ soc_name = "unknown"; 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]); } /* * This wrapper for the FEL write functionality safeguards against overwriting * an already loaded U-Boot binary. * The return value represents elapsed time in seconds (needed for execution). */ double aw_write_buffer(feldev_handle *dev, 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) pr_fatal("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); double start = gettime(); aw_fel_write_buffer(dev, buf, offset, len, progress); 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) pr_fatal("stat() error on file \"%s\": %s\n", filename, strerror(errno)); if (!S_ISREG(st.st_mode)) pr_fatal("error: \"%s\" is not a regular file\n", filename); 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 offset = 0, bufsize = 8192; 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) { size_t len = bufsize - offset; size_t n = fread(buf+offset, 1, len, in); offset += n; if (n < len) break; bufsize *= 2; buf = realloc(buf, bufsize); if (!buf) { perror("Failed to resize load_file() buffer"); exit(1); } } if (size) *size = offset; if (in != stdin) fclose(in); return buf; } void aw_fel_hexdump(feldev_handle *dev, uint32_t offset, size_t size) { if (size > 0) { unsigned char buf[size]; aw_fel_read(dev, offset, buf, size); hexdump(buf, offset, size); } } void aw_fel_dump(feldev_handle *dev, uint32_t offset, size_t size) { if (size > 0) { unsigned char buf[size]; aw_fel_read(dev, offset, buf, size); fwrite(buf, size, 1, stdout); } } void aw_fel_fill(feldev_handle *dev, uint32_t offset, size_t size, unsigned char value) { if (size > 0) { unsigned char buf[size]; memset(buf, value, size); aw_write_buffer(dev, buf, offset, size, false); } } /* * Upload a function (implemented in native ARM code) to the device and * prepare for executing it. Use a subset of 32-bit ARM AAPCS calling * conventions: all arguments are integer 32-bit values, and an optional * return value is a 32-bit integer too. The function code needs to be * compiled in the ARM mode (Thumb2 is not supported), it also must be * a position independent leaf function (have no calls to anything else) * and have no references to any global variables. * * 'stack_size' - the required stack size for the function (can be * calculated using the '-fstack-usage' GCC option) * 'arm_code' - a pointer to the memory buffer with the function code * 'arm_code_size' - the size of the function code * 'num_args' - the number of 32-bit function arguments * 'args' - an array with the function argument values * * Note: once uploaded, the function can be executed multiple times with * exactly the same arguments. If some internal state needs to be * updated between function calls, then it's best to pass a pointer * to some state structure located elsewhere in SRAM as one of the * function arguments. */ bool aw_fel_remotefunc_prepare(feldev_handle *dev, size_t stack_size, void *arm_code, size_t arm_code_size, size_t num_args, uint32_t *args) { size_t idx, i; size_t tmp_buf_size; soc_info_t *soc_info = dev->soc_info; uint32_t *tmp_buf; uint32_t new_sp, num_args_on_stack = (num_args <= 4 ? 0 : num_args - 4); uint32_t entry_code[] = { htole32(0xe58fe040), /* 0: str lr, [pc, #64] */ htole32(0xe58fd040), /* 4: str sp, [pc, #64] */ htole32(0xe59fd040), /* 8: ldr sp, [pc, #64] */ htole32(0xe28fc040), /* c: add ip, pc, #64 */ htole32(0xe1a0200d), /* 10: mov r2, sp */ htole32(0xe49c0004), /* 14: ldr r0, [ip], #4 */ htole32(0xe3500000), /* 18: cmp r0, #0 */ htole32(0x0a000003), /* 1c: beq 30 */ htole32(0xe49c1004), /* 20: ldr r1, [ip], #4 */ htole32(0xe4821004), /* 24: str r1, [r2], #4 */ htole32(0xe2500001), /* 28: subs r0, r0, #1 */ htole32(0x1afffffb), /* 2c: bne 20 */ htole32(0xe8bc000f), /* 30: ldm ip!, {r0, r1, r2, r3} */ htole32(0xe12fff3c), /* 34: blx ip */ htole32(0xe59fe008), /* 38: ldr lr, [pc, #8] */ htole32(0xe59fd008), /* 3c: ldr sp, [pc, #8] */ htole32(0xe58f0000), /* 40: str r0, [pc] */ htole32(0xe12fff1e), /* 44: bx lr */ htole32(0x00000000), /* 48: .word 0x00000000 */ htole32(0x00000000), /* 4c: .word 0x00000000 */ }; if (!soc_info) return false; /* Calculate the stack location */ new_sp = soc_info->scratch_addr + sizeof(entry_code) + 2 * 4 + num_args_on_stack * 4 + 4 * 4 + arm_code_size + stack_size; new_sp = (new_sp + 7) & ~7; tmp_buf_size = new_sp - soc_info->scratch_addr; tmp_buf = calloc(tmp_buf_size, 1); memcpy(tmp_buf, entry_code, sizeof(entry_code)); idx = sizeof(entry_code) / 4; tmp_buf[idx++] = htole32(new_sp); tmp_buf[idx++] = htole32(num_args_on_stack); for (i = num_args - num_args_on_stack; i < num_args; i++) tmp_buf[idx++] = htole32(args[i]); for (i = 0; i < 4; i++) tmp_buf[idx++] = (i < num_args ? htole32(args[i]) : 0); memcpy(tmp_buf + idx, arm_code, arm_code_size); aw_fel_write(dev, tmp_buf, soc_info->scratch_addr, tmp_buf_size); free(tmp_buf); return true; } /* * Execute the previously uploaded function. The 'result' pointer allows to * retrieve the return value. */ bool aw_fel_remotefunc_execute(feldev_handle *dev, uint32_t *result) { soc_info_t *soc_info = dev->soc_info; if (!soc_info) return false; aw_fel_execute(dev, soc_info->scratch_addr); if (result) { aw_fel_read(dev, soc_info->scratch_addr + 0x48, result, sizeof(uint32_t)); *result = le32toh(*result); } return true; } static uint32_t fel_to_spl_thunk[] = { #include "thunks/fel-to-spl-thunk.h" }; #define DRAM_BASE 0x40000000 #define DRAM_SIZE 0x80000000 uint32_t aw_read_arm_cp_reg(feldev_handle *dev, soc_info_t *soc_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 & 0x7) << 21) | ((crn & 0xF) << 16) | ((coproc & 0xF) << 8) | ((opc2 & 0x7) << 5) | (crm & 0xF); 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(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_info->scratch_addr); aw_fel_read(dev, soc_info->scratch_addr + 12, &val, sizeof(val)); return le32toh(val); } void aw_write_arm_cp_reg(feldev_handle *dev, soc_info_t *soc_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 & 0x7) << 21) | ((crn & 0xF) << 16) | ((coproc & 0xF) << 8) | ((opc2 & 7) << 5) | (crm & 0xF); 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(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_info->scratch_addr); } /* "readl" of a single value */ uint32_t fel_readl(feldev_handle *dev, uint32_t addr) { uint32_t val; fel_readl_n(dev, addr, &val, 1); return val; } /* "writel" of a single value */ void fel_writel(feldev_handle *dev, uint32_t addr, uint32_t val) { fel_writel_n(dev, addr, &val, 1); } void aw_fel_print_sid(feldev_handle *dev, bool force_workaround) { uint32_t key[4]; soc_info_t *soc_info = dev->soc_info; if (!soc_info->sid_base) { printf("SID registers for your SoC (%s) are unknown or inaccessible.\n", dev->soc_name); return; } if (soc_info->sid_fix || force_workaround) { pr_info("Read SID key via registers, base = 0x%08X\n", soc_info->sid_base); } else { pr_info("SID key (e-fuses) at 0x%08X\n", soc_info->sid_base + soc_info->sid_offset); } fel_get_sid_root_key(dev, key, force_workaround); /* output SID in "xxxxxxxx:xxxxxxxx:xxxxxxxx:xxxxxxxx" format */ for (unsigned i = 0; i <= 3; i++) printf("%08x%c", key[i], i < 3 ? ':' : '\n'); } void aw_enable_l2_cache(feldev_handle *dev, soc_info_t *soc_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(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_info->scratch_addr); } void aw_get_stackinfo(feldev_handle *dev, soc_info_t *soc_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(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_info->scratch_addr); aw_fel_read(dev, soc_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(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_info->scratch_addr); aw_fel_read(dev, soc_info->scratch_addr + 0x24, results, 8); #endif *sp_irq = le32toh(results[0]); *sp = le32toh(results[1]); } uint32_t aw_get_ttbr0(feldev_handle *dev, soc_info_t *soc_info) { return aw_read_arm_cp_reg(dev, soc_info, 15, 0, 2, 0, 0); } uint32_t aw_get_ttbcr(feldev_handle *dev, soc_info_t *soc_info) { return aw_read_arm_cp_reg(dev, soc_info, 15, 0, 2, 0, 2); } uint32_t aw_get_dacr(feldev_handle *dev, soc_info_t *soc_info) { return aw_read_arm_cp_reg(dev, soc_info, 15, 0, 3, 0, 0); } uint32_t aw_get_sctlr(feldev_handle *dev, soc_info_t *soc_info) { return aw_read_arm_cp_reg(dev, soc_info, 15, 0, 1, 0, 0); } void aw_set_ttbr0(feldev_handle *dev, soc_info_t *soc_info, uint32_t ttbr0) { return aw_write_arm_cp_reg(dev, soc_info, 15, 0, 2, 0, 0, ttbr0); } void aw_set_ttbcr(feldev_handle *dev, soc_info_t *soc_info, uint32_t ttbcr) { return aw_write_arm_cp_reg(dev, soc_info, 15, 0, 2, 0, 2, ttbcr); } void aw_set_dacr(feldev_handle *dev, soc_info_t *soc_info, uint32_t dacr) { aw_write_arm_cp_reg(dev, soc_info, 15, 0, 3, 0, 0, dacr); } void aw_set_sctlr(feldev_handle *dev, soc_info_t *soc_info, uint32_t sctlr) { aw_write_arm_cp_reg(dev, soc_info, 15, 0, 1, 0, 0, sctlr); } /* * Issue a "smc #0" instruction. This brings a SoC booted in "secure boot" * state from the default non-secure FEL into secure FEL. * This crashes on devices using "non-secure boot", as the BROM does not * provide a handler address in MVBAR. So we have a runtime check. */ void aw_apply_smc_workaround(feldev_handle *dev) { soc_info_t *soc_info = dev->soc_info; uint32_t val; uint32_t arm_code[] = { htole32(0xe1600070), /* smc #0 */ htole32(0xe12fff1e), /* bx lr */ }; /* Return if the SoC does not need this workaround */ if (!soc_info->needs_smc_workaround_if_zero_word_at_addr) return; /* This has less overhead than fel_readl_n() and may be good enough */ aw_fel_read(dev, soc_info->needs_smc_workaround_if_zero_word_at_addr, &val, sizeof(val)); /* Return if the workaround is not needed or has been already applied */ if (val != 0) return; pr_info("Applying SMC workaround... "); aw_fel_write(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_info->scratch_addr); pr_info(" done.\n"); } /* * 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(feldev_handle *dev, soc_info_t *soc_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(0xe3c00b06), /* bic r0, r0, #0x1800 */ 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(dev, soc_info); if ((sctlr & ~((0x7 << 11) | (1 << 6) | 1)) != 0x00C50038) pr_fatal("Unexpected SCTLR (%08X)\n", sctlr); if (!(sctlr & 1)) { pr_info("MMU is not enabled by BROM\n"); return NULL; } dacr = aw_get_dacr(dev, soc_info); if (dacr != 0x55555555) pr_fatal("Unexpected DACR (%08X)\n", dacr); ttbcr = aw_get_ttbcr(dev, soc_info); if (ttbcr != 0x00000000) pr_fatal("Unexpected TTBCR (%08X)\n", ttbcr); ttbr0 = aw_get_ttbr0(dev, soc_info); if (ttbr0 & 0x3FFF) pr_fatal("Unexpected TTBR0 (%08X)\n", ttbr0); tt = malloc(16 * 1024); pr_info("Reading the MMU translation table from 0x%08X\n", ttbr0); aw_fel_read(dev, 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) pr_fatal("MMU: not a section descriptor\n"); if ((tt[i] >> 20) != i) pr_fatal("MMU: not a direct mapping\n"); } pr_info("Disabling I-cache, MMU and branch prediction..."); aw_fel_write(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_info->scratch_addr); pr_info(" done.\n"); return tt; } void aw_restore_and_enable_mmu(feldev_handle *dev, soc_info_t *soc_info, uint32_t *tt) { uint32_t i; uint32_t ttbr0 = aw_get_ttbr0(dev, soc_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(0xe3800b06), /* orr r0, r0, #0x1800 */ 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(dev, tt, ttbr0, 16 * 1024); pr_info("Enabling I-cache, MMU and branch prediction..."); aw_fel_write(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_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(feldev_handle *dev, uint8_t *buf, size_t len) { soc_info_t *soc_info = dev->soc_info; 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 = soc_info->spl_addr; uint32_t *tt = NULL; if (!soc_info || !soc_info->swap_buffers) pr_fatal("SPL: Unsupported SoC type\n"); if (len < 32 || memcmp(buf + 4, "eGON.BT0", 8) != 0) pr_fatal("SPL: eGON header is not found\n"); spl_checksum = 2 * le32toh(buf32[3]) - 0x5F0A6C39; spl_len = le32toh(buf32[4]); if (spl_len > len || (spl_len % 4) != 0) pr_fatal("SPL: bad length in the eGON header\n"); len = spl_len; for (i = 0; i < len / 4; i++) spl_checksum -= le32toh(buf32[i]); if (spl_checksum != 0) pr_fatal("SPL: checksum check failed\n"); if (soc_info->needs_l2en) { pr_info("Enabling the L2 cache\n"); aw_enable_l2_cache(dev, soc_info); } aw_get_stackinfo(dev, soc_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(dev, soc_info); if (!tt && soc_info->mmu_tt_addr) { if (soc_info->mmu_tt_addr & 0x3FFF) pr_fatal("SPL: 'mmu_tt_addr' must be 16K aligned\n"); pr_info("Generating the new MMU translation table at 0x%08X\n", soc_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(dev, soc_info, 0x55555555); aw_set_ttbcr(dev, soc_info, 0x00000000); aw_set_ttbr0(dev, soc_info, soc_info->mmu_tt_addr); tt = aw_generate_mmu_translation_table(); } swap_buffers = soc_info->swap_buffers; for (i = 0; swap_buffers[i].size; i++) { if ((swap_buffers[i].buf2 >= soc_info->spl_addr) && (swap_buffers[i].buf2 < soc_info->spl_addr + spl_len_limit)) spl_len_limit = swap_buffers[i].buf2 - soc_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(dev, 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(dev, 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 (soc_info->thunk_addr < spl_len_limit) spl_len_limit = soc_info->thunk_addr; if (spl_len > spl_len_limit) pr_fatal("SPL: too large (need %u, have %u)\n", spl_len, spl_len_limit); /* Write the remaining part of the SPL */ if (len > 0) aw_fel_write(dev, buf, cur_addr, len); thunk_size = sizeof(fel_to_spl_thunk) + sizeof(soc_info->spl_addr) + (i + 1) * sizeof(*swap_buffers); if (thunk_size > soc_info->thunk_size) pr_fatal("SPL: bad thunk size (need %d, have %d)\n", (int)sizeof(fel_to_spl_thunk), soc_info->thunk_size); 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), &soc_info->spl_addr, sizeof(soc_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(dev, thunk_buf, soc_info->thunk_addr, thunk_size); aw_fel_execute(dev, soc_info->thunk_addr); pr_info(" done.\n"); free(thunk_buf); /* TODO: Try to find and fix the bug, which needs this workaround */ struct timespec req = { .tv_nsec = 250000000 }; /* 250ms */ nanosleep(&req, NULL); /* Read back the result and check if everything was fine */ aw_fel_read(dev, soc_info->spl_addr + 4, header_signature, 8); if (strcmp(header_signature, "eGON.FEL") != 0) pr_fatal("SPL: failure code '%s'\n", header_signature); /* re-enable the MMU if it was enabled by BROM */ if (tt != NULL) aw_restore_and_enable_mmu(dev, soc_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(feldev_handle *dev, uint8_t *buf, size_t len) { if (len <= HEADER_SIZE) return; /* Insufficient size (no actual data), just bail out */ image_header_t hdr = *(image_header_t *)buf; uint32_t hcrc = be32toh(hdr.ih_hcrc); /* The CRC is calculated on the whole header but the CRC itself */ hdr.ih_hcrc = 0; uint32_t computed_hcrc = crc32(0, (const uint8_t *) &hdr, HEADER_SIZE); if (hcrc != computed_hcrc) pr_fatal("U-Boot header CRC mismatch: expected %x, got %x\n", hcrc, computed_hcrc); /* 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: pr_error("Invalid U-Boot image: bad size or signature\n"); break; case IH_TYPE_ARCH_MISMATCH: pr_error("Invalid U-Boot image: wrong architecture\n"); break; default: pr_error("Invalid U-Boot image: error code %d\n", image_type); } exit(1); } if (image_type != IH_TYPE_FIRMWARE) pr_fatal("U-Boot image type mismatch: " "expected IH_TYPE_FIRMWARE, got %02X\n", image_type); uint32_t data_size = be32toh(hdr.ih_size); /* Image Data Size */ uint32_t load_addr = be32toh(hdr.ih_load); /* Data Load Address */ if (data_size > len - HEADER_SIZE) pr_fatal("U-Boot image data trucated: " "expected %zu bytes, got %u\n", len - HEADER_SIZE, data_size); uint32_t dcrc = be32toh(hdr.ih_dcrc); uint32_t computed_dcrc = crc32(0, buf + HEADER_SIZE, data_size); if (dcrc != computed_dcrc) pr_fatal("U-Boot data CRC mismatch: expected %x, got %x\n", dcrc, computed_dcrc); /* 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(dev, 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(feldev_handle *dev, 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(dev, buf, size); /* check for optional main U-Boot binary (and transfer it, if applicable) */ if (size > SPL_LEN_LIMIT) aw_fel_write_uboot_image(dev, 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 2 /* maximum supported version */ bool have_sunxi_spl(feldev_handle *dev, uint32_t spl_addr) { uint8_t spl_signature[4]; aw_fel_read(dev, 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) { pr_error("sunxi SPL version mismatch: " "found 0x%02X < required minimum 0x%02X\n", spl_signature[3], SPL_MIN_VERSION); pr_error("You need to update your U-Boot (mksunxiboot) to a more recent version.\n"); return false; } if (spl_signature[3] > SPL_MAX_VERSION) { pr_error("sunxi SPL version mismatch: " "found 0x%02X > maximum supported 0x%02X\n", spl_signature[3], SPL_MAX_VERSION); pr_error("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(feldev_handle *dev, uint32_t script_address, uint32_t uEnv_length) { soc_info_t *soc_info = dev->soc_info; /* write something _only_ if we have a suitable SPL header */ if (have_sunxi_spl(dev, soc_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(dev, transfer, soc_info->spl_addr + 0x18, sizeof(transfer)); } } /* * 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(feldev_handle *dev, uint32_t entry_point, bool aarch64) { soc_info_t *soc_info = dev->soc_info; if (!soc_info->rvbar_reg) { pr_error("ERROR: Can't issue RMR request!\n" "RVBAR is not supported or unknown for your SoC (%s).\n", dev->soc_name); 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(dev, 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(dev, 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(feldev_handle *dev, size_t count, size_t argc, char **argv, progress_cb_t callback) { if (argc < count * 2) pr_fatal("error: too few arguments for uploading %zu files\n", count); /* 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(dev, 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(dev, offset, 0); if (is_uEnv(buf, size)) /* uEnv-style data */ pass_fel_information(dev, offset, size); } free(buf); } return i; /* return number of files that were processed */ } static void felusb_list_devices(void) { size_t devices; /* FEL device count */ feldev_list_entry *list, *entry; list = list_fel_devices(&devices); for (entry = list; entry->soc_version.soc_id; entry++) { printf("USB device %03d:%03d Allwinner %-8s", entry->busnum, entry->devnum, entry->soc_name); /* output SID only if non-zero */ if (entry->SID[0] | entry->SID[1] | entry->SID[2] | entry->SID[3]) printf("%08x:%08x:%08x:%08x", entry->SID[0], entry->SID[1], entry->SID[2], entry->SID[3]); putchar('\n'); } free(list); if (verbose && devices == 0) pr_error("No Allwinner devices in FEL mode detected.\n"); feldev_done(NULL); exit(devices > 0 ? EXIT_SUCCESS : EXIT_FAILURE); } static void select_by_sid(const char *sid_arg, int *busnum, int *devnum) { char sid[36]; feldev_list_entry *list, *entry; list = list_fel_devices(NULL); for (entry = list; entry->soc_version.soc_id; entry++) { snprintf(sid, sizeof(sid), "%08x:%08x:%08x:%08x", entry->SID[0], entry->SID[1], entry->SID[2], entry->SID[3]); if (strcmp(sid, sid_arg) == 0) { *busnum = entry->busnum; *devnum = entry->devnum; break; } } free(list); } void usage(const char *cmd) { puts("sunxi-fel " VERSION "\n"); printf("Usage: %s [options] command arguments... [command...]\n" " -h, --help Print this usage summary and exit\n" " -v, --verbose Verbose logging\n" " -p, --progress \"write\" transfers show a progress bar\n" " -l, --list Enumerate all (USB) FEL devices and exit\n" " -d, --dev bus:devnum Use specific USB bus and device number\n" " --sid SID Select device by SID key (exact match)\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" " memmove dest source size Copy bytes within device memory\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" , cmd); printf("\n"); aw_fel_spiflash_help(); exit(0); } 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 */ bool device_list = false; /* -l switch, prints device list and exits */ feldev_handle *handle; int busnum = -1, devnum = -1; char *sid_arg = NULL; if (argc <= 1) usage(argv[0]); /* process all "prefix"-type arguments first */ while (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) usage(argv[0]); else 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 (strcmp(argv[1], "--list") == 0 || strcmp(argv[1], "-l") == 0 || strcmp(argv[1], "list") == 0) device_list = 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) pr_fatal("ERROR: Expected 'bus:devnum', got '%s'.\n", dev_arg); pr_info("Selecting USB Bus %03d Device %03d\n", busnum, devnum); } else if (strcmp(argv[1], "--sid") == 0 && argc > 2) { sid_arg = argv[2]; argc -= 1; argv += 1; } else break; /* no valid (prefix) option detected, exit loop */ argc -= 1; argv += 1; } /* * If any option-style arguments remain (starting with '-') we know that * we won't recognize them later (at best yielding "Invalid command"). * However this would only happen _AFTER_ trying to open a FEL device, * which might fail with "Allwinner USB FEL device not found". To avoid * confusing the user, bail out here - with a more descriptive message. */ int i; for (i = 1; i < argc; i++) if (*argv[i] == '-') pr_fatal("Invalid option %s\n", argv[i]); /* Process options that don't require a FEL device handle */ if (device_list) felusb_list_devices(); /* and exit program afterwards */ if (sid_arg) { /* try to set busnum and devnum according to "--sid" option */ select_by_sid(sid_arg, &busnum, &devnum); if (busnum <= 0 || devnum <= 0) pr_fatal("No matching FEL device found for SID '%s'\n", sid_arg); pr_info("Selecting FEL device %03d:%03d by SID\n", busnum, devnum); } /* * Open FEL device - either specified by busnum:devnum, or * the first one matching the given USB vendor/procduct ID. */ handle = feldev_open(busnum, devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID); /* Some SoCs need the SMC workaround to enter the secure boot mode */ aw_apply_smc_workaround(handle); /* Handle command-style arguments, in order of appearance */ 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], "memmove") == 0 && argc > 4) { /* three parameters: destination addr, source addr, byte count */ fel_memmove(handle, strtoul(argv[2], NULL, 0), strtoul(argv[3], NULL, 0), strtoul(argv[4], NULL, 0)); skip = 4; } else if (strcmp(argv[1], "readl") == 0 && argc > 2) { printf("0x%08x\n", fel_readl(handle, strtoul(argv[2], NULL, 0))); skip = 2; } else if (strcmp(argv[1], "writel") == 0 && argc > 3) { 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, false); } else if (strcmp(argv[1], "sid-registers") == 0) { aw_fel_print_sid(handle, true); /* enforce register access */ } 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 if (strcmp(argv[1], "spiflash-info") == 0) { aw_fel_spiflash_info(handle); } else if (strcmp(argv[1], "spiflash-read") == 0 && argc > 4) { size_t size = strtoul(argv[3], NULL, 0); void *buf = malloc(size); aw_fel_spiflash_read(handle, strtoul(argv[2], NULL, 0), buf, size, pflag_active ? progress_bar : NULL); save_file(argv[4], buf, size); free(buf); skip=4; } else if (strcmp(argv[1], "spiflash-write") == 0 && argc > 3) { size_t size; void *buf = load_file(argv[3], &size); aw_fel_spiflash_write(handle, strtoul(argv[2], NULL, 0), buf, size, pflag_active ? progress_bar : NULL); free(buf); skip=3; } else { pr_fatal("Invalid command %s\n", argv[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); } feldev_done(handle); return 0; } sunxi-tools-1.4.2~git20181114.6d598a/fel_lib.c000066400000000000000000000677661337271345600202660ustar00rootroot00000000000000/* * Copyright (C) 2012 Henrik Nordstrom * Copyright (C) 2015 Siarhei Siamashka * Copyright (C) 2016 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 . */ /********************************************************************** * USB library and helper functions for the FEL utility **********************************************************************/ #include "portable_endian.h" #include "fel_lib.h" #include #include #include #include #include #include #define USB_TIMEOUT 10000 /* 10 seconds */ static bool fel_lib_initialized = false; /* This is out 'private' data type that will be part of a "FEL device" handle */ struct _felusb_handle { libusb_device_handle *handle; int endpoint_out, endpoint_in; bool iface_detached; }; /* 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); } /* * AW_USB_MAX_BULK_SEND and the timeout constant USB_TIMEOUT are related. * Both need to be selected in a way that transferring the maximum chunk size * with (SoC-specific) slow transfer speed won't time out. * * The 512 KiB here are chosen based on the assumption that we want a 10 seconds * timeout, and "slow" transfers take place at approx. 64 KiB/sec - so we can * expect the maximum chunk being transmitted within 8 seconds or less. */ static const int AW_USB_MAX_BULK_SEND = 512 * 1024; /* 512 KiB 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. * (Worst case of "slow" transfers -> one update every two seconds.) */ 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, USB_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, USB_TIMEOUT); if (rc != 0) usb_error(rc, "usb_bulk_recv()", 2); length -= recv; data += recv; } } 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)); #define AW_USB_READ 0x11 #define AW_USB_WRITE 0x12 struct aw_fel_request { uint32_t request; uint32_t address; uint32_t length; uint32_t pad; }; /* FEL request types */ #define AW_FEL_VERSION 0x001 #define AW_FEL_1_WRITE 0x101 #define AW_FEL_1_EXEC 0x102 #define AW_FEL_1_READ 0x103 static void aw_send_usb_request(feldev_handle *dev, 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(dev->usb->handle, dev->usb->endpoint_out, &req, sizeof(req), false); } static void aw_read_usb_response(feldev_handle *dev) { char buf[13]; usb_bulk_recv(dev->usb->handle, dev->usb->endpoint_in, buf, sizeof(buf)); assert(strcmp(buf, "AWUS") == 0); } static void aw_usb_write(feldev_handle *dev, const void *data, size_t len, bool progress) { aw_send_usb_request(dev, AW_USB_WRITE, len); usb_bulk_send(dev->usb->handle, dev->usb->endpoint_out, data, len, progress); aw_read_usb_response(dev); } static void aw_usb_read(feldev_handle *dev, const void *data, size_t len) { aw_send_usb_request(dev, AW_USB_READ, len); usb_bulk_send(dev->usb->handle, dev->usb->endpoint_in, data, len, false); aw_read_usb_response(dev); } void aw_send_fel_request(feldev_handle *dev, 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(dev, &req, sizeof(req), false); } void aw_read_fel_status(feldev_handle *dev) { char buf[8]; aw_usb_read(dev, buf, sizeof(buf)); } /* AW_FEL_VERSION request */ static void aw_fel_get_version(feldev_handle *dev, struct aw_fel_version *buf) { aw_send_fel_request(dev, AW_FEL_VERSION, 0, 0); aw_usb_read(dev, buf, sizeof(*buf)); aw_read_fel_status(dev); 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]); } /* AW_FEL_1_READ request */ void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len) { aw_send_fel_request(dev, AW_FEL_1_READ, offset, len); aw_usb_read(dev, buf, len); aw_read_fel_status(dev); } /* AW_FEL_1_WRITE request */ void aw_fel_write(feldev_handle *dev, void *buf, uint32_t offset, size_t len) { aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len); aw_usb_write(dev, buf, len, false); aw_read_fel_status(dev); } /* AW_FEL_1_EXEC request */ void aw_fel_execute(feldev_handle *dev, uint32_t offset) { aw_send_fel_request(dev, AW_FEL_1_EXEC, offset, 0); aw_read_fel_status(dev); } /* * 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 optionally allows progress callbacks. */ void aw_fel_write_buffer(feldev_handle *dev, void *buf, uint32_t offset, size_t len, bool progress) { aw_send_fel_request(dev, AW_FEL_1_WRITE, offset, len); aw_usb_write(dev, buf, len, progress); aw_read_fel_status(dev); } /* * We don't want the scratch code/buffer to exceed a maximum size of 0x400 bytes * (256 32-bit words) on readl_n/writel_n transfers. To guarantee this, we have * to account for the amount of space the ARM code uses. */ #define LCODE_ARM_WORDS 12 /* word count of the [read/write]l_n scratch code */ #define LCODE_ARM_SIZE (LCODE_ARM_WORDS << 2) /* code size in bytes */ #define LCODE_MAX_TOTAL 0x100 /* max. words in buffer */ #define LCODE_MAX_WORDS (LCODE_MAX_TOTAL - LCODE_ARM_WORDS) /* data words */ /* multiple "readl" from sequential addresses to a destination buffer */ static void aw_fel_readl_n(feldev_handle *dev, uint32_t addr, uint32_t *dst, size_t count) { if (count == 0) return; if (count > LCODE_MAX_WORDS) { fprintf(stderr, "ERROR: Max. word count exceeded, truncating aw_fel_readl_n() transfer\n"); count = LCODE_MAX_WORDS; } assert(LCODE_MAX_WORDS < 256); /* protect against corruption of ARM code */ uint32_t arm_code[] = { htole32(0xe59f0020), /* ldr r0, [pc, #32] ; ldr r0,[read_addr] */ htole32(0xe28f1024), /* add r1, pc, #36 ; adr r1, read_data */ htole32(0xe59f201c), /* ldr r2, [pc, #28] ; ldr r2,[read_count] */ htole32(0xe3520000 + LCODE_MAX_WORDS), /* cmp r2, #LCODE_MAX_WORDS */ htole32(0xc3a02000 + LCODE_MAX_WORDS), /* movgt r2, #LCODE_MAX_WORDS */ /* read_loop: */ htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */ htole32(0x412fff1e), /* bxmi lr ; return if (r2 < 0) */ htole32(0xe4903004), /* ldr r3, [r0], #4 ; load and post-inc */ htole32(0xe4813004), /* str r3, [r1], #4 ; store and post-inc */ htole32(0xeafffffa), /* b read_loop */ htole32(addr), /* read_addr */ htole32(count) /* read_count */ /* read_data (buffer) follows, i.e. values go here */ }; assert(sizeof(arm_code) == LCODE_ARM_SIZE); /* scratch buffer setup: transfers ARM code, including addr and count */ aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code)); /* execute code, read back the result */ aw_fel_execute(dev, dev->soc_info->scratch_addr); uint32_t buffer[count]; aw_fel_read(dev, dev->soc_info->scratch_addr + LCODE_ARM_SIZE, buffer, sizeof(buffer)); /* extract values to destination buffer */ uint32_t *val = buffer; while (count-- > 0) *dst++ = le32toh(*val++); } /* * aw_fel_readl_n() wrapper that can handle large transfers. If necessary, * those will be done in separate 'chunks' of no more than LCODE_MAX_WORDS. */ void fel_readl_n(feldev_handle *dev, uint32_t addr, uint32_t *dst, size_t count) { while (count > 0) { size_t n = count > LCODE_MAX_WORDS ? LCODE_MAX_WORDS : count; aw_fel_readl_n(dev, addr, dst, n); addr += n * sizeof(uint32_t); dst += n; count -= n; } } /* multiple "writel" from a source buffer to sequential addresses */ static void aw_fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count) { if (count == 0) return; if (count > LCODE_MAX_WORDS) { fprintf(stderr, "ERROR: Max. word count exceeded, truncating aw_fel_writel_n() transfer\n"); count = LCODE_MAX_WORDS; } assert(LCODE_MAX_WORDS < 256); /* protect against corruption of ARM code */ /* * We need a fixed array size to allow for (partial) initialization, * so we'll claim the maximum total number of words (0x100) here. */ uint32_t arm_code[LCODE_MAX_TOTAL] = { htole32(0xe59f0020), /* ldr r0, [pc, #32] ; ldr r0,[write_addr] */ htole32(0xe28f1024), /* add r1, pc, #36 ; adr r1, write_data */ htole32(0xe59f201c), /* ldr r2, [pc, #28] ; ldr r2,[write_count]*/ htole32(0xe3520000 + LCODE_MAX_WORDS), /* cmp r2, #LCODE_MAX_WORDS */ htole32(0xc3a02000 + LCODE_MAX_WORDS), /* movgt r2, #LCODE_MAX_WORDS */ /* write_loop: */ htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */ htole32(0x412fff1e), /* bxmi lr ; return if (r2 < 0) */ htole32(0xe4913004), /* ldr r3, [r1], #4 ; load and post-inc */ htole32(0xe4803004), /* str r3, [r0], #4 ; store and post-inc */ htole32(0xeafffffa), /* b write_loop */ htole32(addr), /* write_addr */ htole32(count) /* write_count */ /* write_data (buffer) follows, i.e. values taken from here */ }; /* copy values from source buffer */ size_t i; for (i = 0; i < count; i++) arm_code[LCODE_ARM_WORDS + i] = htole32(*src++); /* scratch buffer setup: transfers ARM code and data */ aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, (LCODE_ARM_WORDS + count) * sizeof(uint32_t)); /* execute, and we're done */ aw_fel_execute(dev, dev->soc_info->scratch_addr); } /* * aw_fel_writel_n() wrapper that can handle large transfers. If necessary, * those will be done in separate 'chunks' of no more than LCODE_MAX_WORDS. */ void fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count) { while (count > 0) { size_t n = count > LCODE_MAX_WORDS ? LCODE_MAX_WORDS : count; aw_fel_writel_n(dev, addr, src, n); addr += n * sizeof(uint32_t); src += n; count -= n; } } /* * move (arbitrary byte count) data between addresses within SoC memory * * These functions try to copy as many bytes as possible using 32-bit word * transfers, and handle any unaligned bytes ('head' and 'tail') separately. * * This is useful for the same reasons that "readl"/"writel" were introduced: * Byte-oriented transfers ("string" copy) might not give the expected results * when accessing hardware registers, like e.g. the (G)PIO config/state. * * We have two different low-level functions, where the copy operation moves * upwards or downwards respectively. This allows a non-destructive "memmove" * wrapper to select the suitable one in case of memory overlap. */ static void fel_memcpy_up(feldev_handle *dev, uint32_t dst_addr, uint32_t src_addr, size_t size) { if (size == 0) return; /* * copy "upwards", increasing destination and source addresses */ uint32_t arm_code[] = { htole32(0xe59f0054), /* ldr r0, [pc, #84] ; ldr r0, [dst_addr] */ htole32(0xe59f1054), /* ldr r1, [pc, #84] ; ldr r1, [src_addr] */ htole32(0xe59f2054), /* ldr r2, [pc, #84] ; ldr r2, [size] */ htole32(0xe0413000), /* sub r3, r1, r0 ; r3 = r1 - r0 */ htole32(0xe3130003), /* tst r3, #3 ; test lower bits */ htole32(0x1a00000b), /* bne copyup_tail ; unaligned copying */ /* copyup_head: */ htole32(0xe3110003), /* tst r1, #3 ; word-aligned? */ htole32(0x0a000004), /* beq copyup_loop */ htole32(0xe4d13001), /* ldrb r3, [r1], #1 ; load and post-inc */ htole32(0xe4c03001), /* strb r3, [r0], #1 ; store and post-inc */ htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */ htole32(0x5afffff9), /* bpl copyup_head ; while (r2 >= 0) */ htole32(0xe12fff1e), /* bx lr ; early return */ /* copyup_loop: */ htole32(0xe2522004), /* subs r2, r2, #4 ; r2 -= 4 */ htole32(0x54913004), /* ldrpl r3, [r1], #4 ; load and post-inc */ htole32(0x54803004), /* strpl r3, [r0], #4 ; store and post-inc */ htole32(0x5afffffb), /* bpl copyup_loop ; while (r2 >= 0) */ htole32(0xe2822004), /* add r2, r2, #4 ; remaining bytes */ /* copyup_tail: */ htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */ htole32(0x412fff1e), /* bxmi lr ; return if (r2 < 0) */ htole32(0xe4d13001), /* ldrb r3, [r1], #1 ; load and post-inc */ htole32(0xe4c03001), /* strb r3, [r0], #1 ; store and post-inc */ htole32(0xeafffffa), /* b copyup_tail */ htole32(dst_addr), /* destination address */ htole32(src_addr), /* source address */ htole32(size), /* size (= byte count) */ }; aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, dev->soc_info->scratch_addr); } static void fel_memcpy_down(feldev_handle *dev, uint32_t dst_addr, uint32_t src_addr, size_t size) { if (size == 0) return; /* * This ARM code makes use of decreasing values in r2 * for memory indexing relative to the base addresses in r0 and r1. */ uint32_t arm_code[] = { htole32(0xe59f0058), /* ldr r0, [pc, #88] ; ldr r0, [dst_addr] */ htole32(0xe59f1058), /* ldr r1, [pc, #88] ; ldr r1, [src_addr] */ htole32(0xe59f2058), /* ldr r2, [pc, #88] ; ldr r2, [size] */ htole32(0xe0403001), /* sub r3, r0, r1 ; r3 = r0 - r1 */ htole32(0xe3130003), /* tst r3, #3 ; test lower bits */ htole32(0x1a00000c), /* bne copydn_tail ; unaligned copying */ /* copydn_head: */ htole32(0xe0813002), /* add r3, r1, r2 ; r3 = r1 + r2 */ htole32(0xe3130003), /* tst r3, #3 ; word-aligned? */ htole32(0x0a000004), /* beq copydn_loop */ htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */ htole32(0x412fff1e), /* bxmi lr ; early return */ htole32(0xe7d13002), /* ldrb r3, [r1, r2] ; load byte */ htole32(0xe7c03002), /* strb r3, [r0, r2] ; store byte */ htole32(0xeafffff7), /* b copydn_head */ /* copydn_loop: */ htole32(0xe2522004), /* subs r2, r2, #4 ; r2 -= 4 */ htole32(0x57913002), /* ldrpl r3, [r1, r2] ; load word */ htole32(0x57803002), /* strpl r3, [r0, r2] ; store word */ htole32(0x5afffffb), /* bpl copydn_loop ; while (r2 >= 0) */ htole32(0xe2822004), /* add r2, r2, #4 ; remaining bytes */ /* copydn_tail: */ htole32(0xe2522001), /* subs r2, r2, #1 ; r2 -= 1 */ htole32(0x412fff1e), /* bxmi lr ; return if (r2 < 0) */ htole32(0xe7d13002), /* ldrb r3, [r1, r2] ; load byte */ htole32(0xe7c03002), /* strb r3, [r0, r2] ; store byte */ htole32(0xeafffffa), /* b copydn_tail */ htole32(dst_addr), /* destination address */ htole32(src_addr), /* source address */ htole32(size), /* size (= byte count) */ }; aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, dev->soc_info->scratch_addr); } void fel_memmove(feldev_handle *dev, uint32_t dst_addr, uint32_t src_addr, size_t size) { /* * To ensure non-destructive operation, we need to select "downwards" * copying if the destination overlaps the source region. */ if (dst_addr >= src_addr && dst_addr < (src_addr + size)) fel_memcpy_down(dev, dst_addr, src_addr, size); else fel_memcpy_up(dev, dst_addr, src_addr, size); } /* * Bitwise manipulation of a 32-bit word at given address, via bit masks that * specify which bits to clear and which to set. */ void fel_clrsetbits_le32(feldev_handle *dev, uint32_t addr, uint32_t clrbits, uint32_t setbits) { uint32_t arm_code[] = { htole32(0xe59f0018), /* 0: ldr r0, [addr] */ htole32(0xe5901000), /* 4: ldr r1, [r0] */ htole32(0xe59f2014), /* 8: ldr r2, [clrbits] */ htole32(0xe1c11002), /* c: bic r1, r1, r2 */ htole32(0xe59f2010), /* 10: ldr r2, [setbits] */ htole32(0xe1811002), /* 14: orr r1, r1, r2 */ htole32(0xe5801000), /* 18: str r1, [r0] */ htole32(0xe12fff1e), /* 1c: bx lr */ htole32(addr), /* address */ htole32(clrbits), /* bits to clear */ htole32(setbits), /* bits to set */ }; aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, dev->soc_info->scratch_addr); } /* * Memory access to the SID (root) keys proved to be unreliable for certain * SoCs. This function uses an alternative, register-based approach to retrieve * the values. */ static void fel_get_sid_registers(feldev_handle *dev, uint32_t *result) { uint32_t arm_code[] = { htole32(0xe59f0040), /* 0: ldr r0, [pc, #64] */ htole32(0xe3a01000), /* 4: mov r1, #0 */ htole32(0xe28f303c), /* 8: add r3, pc, #60 */ /* : */ htole32(0xe1a02801), /* c: lsl r2, r1, #16 */ htole32(0xe3822b2b), /* 10: orr r2, r2, #44032 */ htole32(0xe3822002), /* 14: orr r2, r2, #2 */ htole32(0xe5802040), /* 18: str r2, [r0, #64] */ /* : */ htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */ htole32(0xe3120002), /* 20: tst r2, #2 */ htole32(0x1afffffc), /* 24: bne 1c */ htole32(0xe5902060), /* 28: ldr r2, [r0, #96] */ htole32(0xe7832001), /* 2c: str r2, [r3, r1] */ htole32(0xe2811004), /* 30: add r1, r1, #4 */ htole32(0xe3510010), /* 34: cmp r1, #16 */ htole32(0x3afffff3), /* 38: bcc c */ htole32(0xe3a02000), /* 3c: mov r2, #0 */ htole32(0xe5802040), /* 40: str r2, [r0, #64] */ htole32(0xe12fff1e), /* 44: bx lr */ htole32(dev->soc_info->sid_base), /* SID base addr */ /* retrieved SID values go here */ }; /* write and execute code */ aw_fel_write(dev, arm_code, dev->soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, dev->soc_info->scratch_addr); /* read back the result */ aw_fel_read(dev, dev->soc_info->scratch_addr + sizeof(arm_code), result, 4 * sizeof(uint32_t)); for (unsigned i = 0; i < 4; i++) result[i] = le32toh(result[i]); } /* Read the SID "root" key (128 bits). You need to pass the device handle, * a pointer to a result array capable of receiving at least four 32-bit words, * and a flag specifying if the register-access workaround should be enforced. * Return value indicates whether the result is expected to be usable: * The function will return `false` (and zero the result) if it cannot access * the SID registers. */ bool fel_get_sid_root_key(feldev_handle *dev, uint32_t *result, bool force_workaround) { if (!dev->soc_info->sid_base) { /* SID unavailable */ for (unsigned i = 0; i < 4; i++) result[i] = 0; return false; } if (dev->soc_info->sid_fix || force_workaround) /* Work around SID issues by using ARM thunk code */ fel_get_sid_registers(dev, result); else /* Read SID directly from memory */ fel_readl_n(dev, dev->soc_info->sid_base + dev->soc_info->sid_offset, result, 4); return true; } /* general functions, "FEL device" management */ static int feldev_get_endpoint(feldev_handle *dev) { struct libusb_device *usb = libusb_get_device(dev->usb->handle); struct libusb_config_descriptor *config; int if_idx, set_idx, ep_idx, ret; const struct libusb_interface *iface; const struct libusb_interface_descriptor *setting; const struct libusb_endpoint_descriptor *ep; ret = libusb_get_active_config_descriptor(usb, &config); if (ret) return ret; for (if_idx = 0; if_idx < config->bNumInterfaces; if_idx++) { iface = config->interface + if_idx; for (set_idx = 0; set_idx < iface->num_altsetting; set_idx++) { setting = iface->altsetting + set_idx; for (ep_idx = 0; ep_idx < setting->bNumEndpoints; ep_idx++) { 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) dev->usb->endpoint_in = ep->bEndpointAddress; else dev->usb->endpoint_out = ep->bEndpointAddress; } } } libusb_free_config_descriptor(config); return LIBUSB_SUCCESS; } /* claim USB interface associated with the libusb handle for a FEL device */ void feldev_claim(feldev_handle *dev) { int rc = libusb_claim_interface(dev->usb->handle, 0); #if defined(__linux__) if (rc != LIBUSB_SUCCESS) { libusb_detach_kernel_driver(dev->usb->handle, 0); dev->usb->iface_detached = true; rc = libusb_claim_interface(dev->usb->handle, 0); } #endif if (rc) usb_error(rc, "libusb_claim_interface()", 1); rc = feldev_get_endpoint(dev); if (rc) usb_error(rc, "FAILED to get FEL mode endpoint addresses!", 1); } /* release USB interface associated with the libusb handle for a FEL device */ void feldev_release(feldev_handle *dev) { libusb_release_interface(dev->usb->handle, 0); #if defined(__linux__) if (dev->usb->iface_detached) libusb_attach_kernel_driver(dev->usb->handle, 0); #endif } /* open handle to desired FEL device */ feldev_handle *feldev_open(int busnum, int devnum, uint16_t vendor_id, uint16_t product_id) { if (!fel_lib_initialized) /* if not already done: auto-initialize */ feldev_init(); feldev_handle *result = calloc(1, sizeof(feldev_handle)); if (!result) { fprintf(stderr, "FAILED to allocate feldev_handle memory.\n"); exit(1); } result->usb = calloc(1, sizeof(felusb_handle)); if (!result->usb) { fprintf(stderr, "FAILED to allocate felusb_handle memory.\n"); free(result); exit(1); } 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->usb->handle = libusb_open_device_with_vid_pid(NULL, vendor_id, product_id); if (!result->usb->handle) { switch (errno) { case EACCES: fprintf(stderr, "ERROR: You don't have permission to access Allwinner USB FEL device\n"); break; default: fprintf(stderr, "ERROR: Allwinner USB FEL device not found!\n"); break; } exit(1); } } else { /* look for specific bus and device number */ 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->usb->handle); 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); } } feldev_claim(result); /* claim interface, detect USB endpoints */ /* retrieve BROM version and SoC information */ aw_fel_get_version(result, &result->soc_version); get_soc_name_from_id(result->soc_name, result->soc_version.soc_id); result->soc_info = get_soc_info_from_version(&result->soc_version); return result; } /* close FEL device (optional, dev may be NULL) */ void feldev_close(feldev_handle *dev) { if (dev) { if (dev->usb->handle) { feldev_release(dev); libusb_close(dev->usb->handle); } free(dev->usb); /* release memory allocated for felusb_handle */ } } void feldev_init(void) { int rc = libusb_init(NULL); if (rc != 0) usb_error(rc, "libusb_init()", 1); fel_lib_initialized = true; } void feldev_done(feldev_handle *dev) { feldev_close(dev); free(dev); if (fel_lib_initialized) libusb_exit(NULL); } /* * Enumerate (all) FEL devices. Allocates a list (array of feldev_list_entry) * and optionally returns the number of elements via "count". You may * alternatively detect the end of the list by checking the entry's soc_version * for a zero ID. * It's your responsibility to call free() on the result later. */ feldev_list_entry *list_fel_devices(size_t *count) { feldev_list_entry *list, *entry; ssize_t rc, i; libusb_context *ctx; libusb_device **usb; struct libusb_device_descriptor desc; feldev_handle *dev; size_t devices = 0; libusb_init(&ctx); rc = libusb_get_device_list(ctx, &usb); if (rc < 0) usb_error(rc, "libusb_get_device_list()", 1); /* * Size our array to hold entries for every USB device, * plus an empty one at the end (for list termination). */ list = calloc(rc + 1, sizeof(feldev_list_entry)); if (!list) { fprintf(stderr, "list_fel_devices() FAILED to allocate list memory.\n"); exit(1); } for (i = 0; i < rc; i++) { libusb_get_device_descriptor(usb[i], &desc); if (desc.idVendor != AW_USB_VENDOR_ID || desc.idProduct != AW_USB_PRODUCT_ID) continue; /* not an Allwinner FEL device */ entry = list + devices; /* pointer to current feldev_list_entry */ devices += 1; entry->busnum = libusb_get_bus_number(usb[i]); entry->devnum = libusb_get_device_address(usb[i]); dev = feldev_open(entry->busnum, entry->devnum, AW_USB_VENDOR_ID, AW_USB_PRODUCT_ID); /* copy relevant fields */ entry->soc_version = dev->soc_version; entry->soc_info = dev->soc_info; strncpy(entry->soc_name, dev->soc_name, sizeof(soc_name_t)); /* retrieve SID bits */ fel_get_sid_root_key(dev, entry->SID, false); feldev_close(dev); free(dev); } libusb_free_device_list(usb, true); libusb_exit(ctx); if (count) *count = devices; return list; } sunxi-tools-1.4.2~git20181114.6d598a/fel_lib.h000066400000000000000000000061121337271345600202450ustar00rootroot00000000000000/* * Copyright (C) 2016 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_FEL_LIB_H #define _SUNXI_TOOLS_FEL_LIB_H #include #include #include "progress.h" #include "soc_info.h" /* USB identifiers for Allwinner device in FEL mode */ #define AW_USB_VENDOR_ID 0x1F3A #define AW_USB_PRODUCT_ID 0xEFE8 typedef struct _felusb_handle felusb_handle; /* opaque data type */ /* More general FEL "device" handle, including version data and SoC info */ typedef struct { felusb_handle *usb; struct aw_fel_version soc_version; soc_name_t soc_name; soc_info_t *soc_info; } feldev_handle; /* list_fel_devices() will return an array of this type */ typedef struct { int busnum, devnum; struct aw_fel_version soc_version; soc_name_t soc_name; soc_info_t *soc_info; uint32_t SID[4]; } feldev_list_entry; /* FEL device management */ void feldev_init(void); void feldev_done(feldev_handle *dev); feldev_handle *feldev_open(int busnum, int devnum, uint16_t vendor_id, uint16_t product_id); void feldev_close(feldev_handle *dev); feldev_list_entry *list_fel_devices(size_t *count); /* FEL functions */ void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len); void aw_fel_write(feldev_handle *dev, void *buf, uint32_t offset, size_t len); void aw_fel_write_buffer(feldev_handle *dev, void *buf, uint32_t offset, size_t len, bool progress); void aw_fel_execute(feldev_handle *dev, uint32_t offset); void fel_readl_n(feldev_handle *dev, uint32_t addr, uint32_t *dst, size_t count); void fel_writel_n(feldev_handle *dev, uint32_t addr, uint32_t *src, size_t count); void fel_memmove(feldev_handle *dev, uint32_t dst_addr, uint32_t src_addr, size_t size); void fel_clrsetbits_le32(feldev_handle *dev, uint32_t addr, uint32_t clrbits, uint32_t setbits); #define fel_clrbits_le32(dev, addr, value) \ fel_clrsetbits_le32(dev, addr, value, 0) #define fel_setbits_le32(dev, addr, value) \ fel_clrsetbits_le32(dev, addr, 0, value) /* retrieve SID root key */ bool fel_get_sid_root_key(feldev_handle *dev, uint32_t *result, bool force_workaround); bool aw_fel_remotefunc_prepare(feldev_handle *dev, size_t stack_size, void *arm_code, size_t arm_code_size, size_t num_args, uint32_t *args); bool aw_fel_remotefunc_execute(feldev_handle *dev, uint32_t *result); #endif /* _SUNXI_TOOLS_FEL_LIB_H */ sunxi-tools-1.4.2~git20181114.6d598a/fexc.c000066400000000000000000000170061337271345600175750ustar00rootroot00000000000000/* * 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(...) pr_error("fexc: " __VA_ARGS__) #define pr_err(...) pr_error("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); pr_error("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: pr_error("%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]) { pr_error("%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 */ /* fall-through */ case 1: if (strcmp(argv[optind], "-") != 0) filename[0] = argv[optind]; /* in */ case 0: break; default: goto show_usage; } if (verbose>0) pr_error("%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.2~git20181114.6d598a/fexc.h000066400000000000000000000016431337271345600176020ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/find-arm-gcc.sh000077500000000000000000000006161337271345600212710ustar00rootroot00000000000000# # Try to locate suitable ARM cross compilers available via $PATH # If any are found, this function will output them as a TAB-delimited list # scan_path () { IFS=":" for path in $PATH; do find "$path" -maxdepth 1 -executable -name 'arm*-gcc' -printf '%f\t' 2>/dev/null done } # Use only the first field from result, and convert it to a toolchain prefix scan_path | cut -f 1 | sed -e 's/-gcc/-/' sunxi-tools-1.4.2~git20181114.6d598a/include/000077500000000000000000000000001337271345600201235ustar00rootroot00000000000000sunxi-tools-1.4.2~git20181114.6d598a/include/list.h000066400000000000000000000042241337271345600212510ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/include/portable_endian.h000066400000000000000000000061421337271345600234250ustar00rootroot00000000000000// "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(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) # include /* For functions still missing, try to substitute 'historic' OpenBSD names */ #ifndef be16toh # define be16toh(x) betoh16(x) #endif #ifndef le16toh # define le16toh(x) letoh16(x) #endif #ifndef be32toh # define be32toh(x) betoh32(x) #endif #ifndef le32toh # define le32toh(x) letoh32(x) #endif #ifndef be64toh # define be64toh(x) betoh64(x) #endif #ifndef le64toh # define le64toh(x) letoh64(x) #endif #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.2~git20181114.6d598a/include/types.h000066400000000000000000000023541337271345600214440ustar00rootroot00000000000000/* * (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.2~git20181114.6d598a/jtag-loop.S000066400000000000000000000024601337271345600205220ustar00rootroot00000000000000/* * (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.2~git20181114.6d598a/jtag-loop.c000066400000000000000000000024731337271345600205460ustar00rootroot00000000000000/* * (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.2~git20181114.6d598a/jtag-loop.lds000066400000000000000000000016551337271345600211070ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/meminfo.c000066400000000000000000000461751337271345600203130ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/nand-common.h000066400000000000000000000021671337271345600210650ustar00rootroot00000000000000/* * (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.2~git20181114.6d598a/nand-image-builder.c000066400000000000000000000703521337271345600222770ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/nand-part-a10.h000066400000000000000000000051341337271345600211170ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/nand-part-a20.h000066400000000000000000000064351337271345600211250ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/nand-part-main.c000066400000000000000000000056121337271345600214560ustar00rootroot00000000000000/* * (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.2~git20181114.6d598a/nand-part.c000066400000000000000000000177501337271345600205420ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/phoenix_info.c000066400000000000000000000106401337271345600213320ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/pio.c000066400000000000000000000226741337271345600174460ustar00rootroot00000000000000/* * (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 __attribute__((noreturn)) 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=output, 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); } } return 0; } sunxi-tools-1.4.2~git20181114.6d598a/progress.c000066400000000000000000000110671337271345600205150ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/progress.h000066400000000000000000000030671337271345600205230ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/script.c000066400000000000000000000134051337271345600201530ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/script.h000066400000000000000000000065011337271345600201570ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/script_bin.c000066400000000000000000000230471337271345600210060ustar00rootroot00000000000000/* * 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(...) pr_error("fexc-bin: " __VA_ARGS__) #define pr_err(...) pr_error("E: fexc-bin: " __VA_ARGS__) #ifdef DEBUG #define pr_debug(...) pr_error("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 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->filesize = bin_size; head->version[0] = 1; head->version[1] = 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] > SCRIPT_BIN_VERSION_LIMIT) || (head->version[1] > SCRIPT_BIN_VERSION_LIMIT)) { pr_err("Malformed data: version %u.%u.\n", head->version[0], head->version[1]); 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\n", filename, head->version[0], head->version[1]); pr_info("%s: size: %zu (%u sections), header value: %u\n", filename, bin_size, head->sections, head->filesize); /* 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.2~git20181114.6d598a/script_bin.h000066400000000000000000000033051337271345600210060ustar00rootroot00000000000000/* * 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 filesize; uint32_t version[2]; 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.2~git20181114.6d598a/script_extractor.c000066400000000000000000000022551337271345600222470ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/script_fex.c000066400000000000000000000217041337271345600210160ustar00rootroot00000000000000/* * 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(...) pr_error("fexc-fex: " __VA_ARGS__) #define pr_err(...) pr_error("E: fexc-fex: " __VA_ARGS__) #ifdef DEBUG #define pr_debug(...) pr_error("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 */ pr_error("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) { pr_error("E: %s:%zu: invalid character at %zu.\n", filename, line, p-buffer+1); } else { pr_error("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) { pr_error("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) { pr_error("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) { pr_error("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) { pr_error("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; */ pr_error("Warning: %s:%zu: unquoted value '%s', assuming string\n", filename, line, p); if (script_string_entry_new(last_section, key, pe-p, p)) { pr_debug("%s.%s = \"%s\"\n", last_section->name, key, p); continue; } perror("malloc"); } pr_error("E: %s:%zu: parse error at %zu.\n", filename, line, p-buffer+1); goto parse_error; invalid_char_at_p: pr_error("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.2~git20181114.6d598a/script_fex.h000066400000000000000000000016621337271345600210240ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/script_uboot.c000066400000000000000000000136041337271345600213640ustar00rootroot00000000000000/* * 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(...) pr_error("fexc-uboot: " __VA_ARGS__) #define pr_err(...) pr_error("E: fexc-uboot: " __VA_ARGS__) #ifdef DEBUG #define pr_debug(...) pr_error("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.2~git20181114.6d598a/script_uboot.h000066400000000000000000000015521337271345600213700ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/soc_info.c000066400000000000000000000243201337271345600204440ustar00rootroot00000000000000/* * Copyright (C) 2012 Henrik Nordstrom * Copyright (C) 2015 Siarhei Siamashka * Copyright (C) 2016 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 . */ /********************************************************************** * SoC information and retrieval of soc_sram_info **********************************************************************/ #include "soc_info.h" #include #include /* * 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 * important 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 = 0x1C00, .buf2 = 0xA400, .size = 0x0400 }, /* 0x5C00-0x6FFF (Stack) */ { .buf1 = 0x5C00, .buf2 = 0xA800, .size = 0x1400 }, /* 0x7C00-0x7FFF (Something important) */ { .buf1 = 0x7C00, .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 = 0x1800, .buf2 = 0x20000, .size = 0x800 }, { .buf1 = 0x5C00, .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 = 0x1800, .buf2 = 0x44000, .size = 0x800 }, { .buf1 = 0x5C00, .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 */ }; /* * H6 has 32KiB of SRAM A at 0x20000 and a large SRAM C at 0x28000. 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 0x20000. */ sram_swap_buffers h6_sram_swap_buffers[] = { /* 0x21C00-0x21FFF (IRQ stack) */ { .buf1 = 0x21C00, .buf2 = 0x2A400, .size = 0x0400 }, /* 0x25C00-0x26FFF (Stack) */ { .buf1 = 0x25C00, .buf2 = 0x2A800, .size = 0x1400 }, /* 0x27C00-0x27FFF (Something important) */ { .buf1 = 0x27C00, .buf2 = 0x2BC00, .size = 0x0400 }, { .size = 0 } /* End of the table */ }; soc_info_t soc_info_table[] = { { .soc_id = 0x1623, /* Allwinner A10 */ .name = "A10", .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .needs_l2en = true, .sid_base = 0x01C23800, },{ .soc_id = 0x1625, /* Allwinner A10s, A13, R8 */ .name = "A13", .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .needs_l2en = true, .sid_base = 0x01C23800, },{ .soc_id = 0x1651, /* Allwinner A20 */ .name = "A20", .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .sid_base = 0x01C23800, },{ .soc_id = 0x1650, /* Allwinner A23 */ .name = "A23", .scratch_addr = 0x1000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sid_base = 0x01C23800, },{ .soc_id = 0x1633, /* Allwinner A31 */ .name = "A31", .scratch_addr = 0x1000, .thunk_addr = 0x22E00, .thunk_size = 0x200, .swap_buffers = a31_sram_swap_buffers, },{ .soc_id = 0x1667, /* Allwinner A33, R16 */ .name = "A33", .scratch_addr = 0x1000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sid_base = 0x01C23800, },{ .soc_id = 0x1689, /* Allwinner A64 */ .name = "A64", .spl_addr = 0x10000, .scratch_addr = 0x11000, .thunk_addr = 0x1A200, .thunk_size = 0x200, .swap_buffers = a64_sram_swap_buffers, .sid_base = 0x01C14000, .sid_offset = 0x200, .rvbar_reg = 0x017000A0, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x40004, },{ .soc_id = 0x1639, /* Allwinner A80 */ .name = "A80", .spl_addr = 0x10000, .scratch_addr = 0x11000, .thunk_addr = 0x23400, .thunk_size = 0x200, .swap_buffers = a80_sram_swap_buffers, .sid_base = 0X01C0E000, .sid_offset = 0x200, },{ .soc_id = 0x1673, /* Allwinner A83T */ .name = "A83T", .scratch_addr = 0x1000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sid_base = 0x01C14000, .sid_offset = 0x200, },{ .soc_id = 0x1680, /* Allwinner H3, H2+ */ .name = "H3", .scratch_addr = 0x1000, .mmu_tt_addr = 0x8000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .sid_base = 0x01C14000, .sid_offset = 0x200, .sid_fix = true, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x40004, },{ .soc_id = 0x1681, /* Allwinner V3s */ .name = "V3s", .scratch_addr = 0x1000, .mmu_tt_addr = 0x8000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .sid_base = 0x01C23800, },{ .soc_id = 0x1718, /* Allwinner H5 */ .name = "H5", .spl_addr = 0x10000, .scratch_addr = 0x11000, .thunk_addr = 0x1A200, .thunk_size = 0x200, .swap_buffers = a64_sram_swap_buffers, .sid_base = 0x01C14000, .sid_offset = 0x200, .rvbar_reg = 0x017000A0, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x40004, },{ .soc_id = 0x1701, /* Allwinner R40 */ .name = "R40", .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .sid_base = 0x01C1B000, .sid_offset = 0x200, },{ .soc_id = 0x1728, /* Allwinner H6 */ .name = "H6", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x2A200, .thunk_size = 0x200, .swap_buffers = h6_sram_swap_buffers, .sid_base = 0x03006000, .sid_offset = 0x200, .rvbar_reg = 0x09010040, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x100004, },{ .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 = 0x1C00, .buf2 = 0x5800, .size = 0x400 }, { .size = 0 } /* End of the table */ }; soc_info_t generic_soc_info = { .scratch_addr = 0x1000, .thunk_addr = 0x5680, .thunk_size = 0x180, .swap_buffers = generic_sram_swap_buffers, }; /* functions to retrieve SoC information */ soc_info_t *get_soc_info_from_id(uint32_t soc_id) { soc_info_t *soc, *result = NULL; for (soc = soc_info_table; soc->swap_buffers; soc++) if (soc->soc_id == soc_id) { result = soc; break; } if (!result) { printf("Warning: no 'soc_sram_info' data for your SoC (id=%04X)\n", soc_id); result = &generic_soc_info; } return result; } soc_info_t *get_soc_info_from_version(struct aw_fel_version *buf) { return get_soc_info_from_id(buf->soc_id); } void get_soc_name_from_id(soc_name_t buffer, uint32_t soc_id) { soc_info_t *soc; for (soc = soc_info_table; soc->swap_buffers; soc++) if (soc->soc_id == soc_id && soc->name != NULL) { strncpy(buffer, soc->name, sizeof(soc_name_t) - 1); return; } /* unknown SoC (or name string missing), use the hexadecimal ID */ snprintf(buffer, sizeof(soc_name_t) - 1, "0x%04X", soc_id); } sunxi-tools-1.4.2~git20181114.6d598a/soc_info.h000066400000000000000000000124111337271345600204470ustar00rootroot00000000000000/* * Copyright (C) 2015 Siarhei Siamashka * Copyright (C) 2016 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_SOC_INFO_H #define _SUNXI_TOOLS_SOC_INFO_H #include #include /* SoC version information, as retrieved by the FEL protocol */ 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)); /* * Buffer for a SoC name string. We want at least 6 + 1 characters, to store * the hexadecimal ID "0xABCD" for unknown SoCs, plus the terminating NUL. */ typedef char soc_name_t[8]; /* * The 'sram_swap_buffers' structure is used to describe information about * pairwise memory regions 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 any more, 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. * * If an SoC has the "secure boot" fuse burned, it will enter FEL mode in * non-secure state, so with the SCR.NS bit set. Since in this mode the * secure/non-secure state restrictions are actually observed, we suffer * from several restrictions: * - No access to the SID information (both via memory mapped and "register"). * - No access to secure SRAM (SRAM A2 on H3/A64/H5). * - No access to the secure side of the GIC, so it can't be configured to * be accessible from non-secure world. * - No RMR trigger on ARMv8 cores to bring the core into AArch64. * However it has been found out that a simple "smc" call will immediately * return from monitor mode, but with the NS bit cleared, so access to all * secure peripherals is suddenly possible. * The 'needs_smc_workaround_if_zero_word_at_addr' field can be used to * have a check for this condition (reading from restricted addresses * typically returns zero) and then activate the SMC workaround if needed. */ typedef struct { uint32_t soc_id; /* ID of the SoC */ const char *name; /* human-readable SoC name string */ 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_base; /* base address for SID registers */ uint32_t sid_offset; /* offset for SID_KEY[0-3], "root key" */ uint32_t rvbar_reg; /* MMIO address of RVBARADDR0_L register */ bool sid_fix; /* Use SID workaround (read via register) */ /* Use SMC workaround (enter secure mode) if can't read from this address */ uint32_t needs_smc_workaround_if_zero_word_at_addr; sram_swap_buffers *swap_buffers; } soc_info_t; void get_soc_name_from_id(soc_name_t buffer, uint32_t soc_id); soc_info_t *get_soc_info_from_id(uint32_t soc_id); soc_info_t *get_soc_info_from_version(struct aw_fel_version *buf); #endif /* _SUNXI_TOOLS_SOC_INFO_H */ sunxi-tools-1.4.2~git20181114.6d598a/tests/000077500000000000000000000000001337271345600176425ustar00rootroot00000000000000sunxi-tools-1.4.2~git20181114.6d598a/tests/Makefile000066400000000000000000000026331337271345600213060ustar00rootroot00000000000000# # tests/Makefile # BOARDS_URL := https://github.com/linux-sunxi/sunxi-boards/archive/master.zip BOARDS_DIR := sunxi-boards check: check_all_fex coverage # Conversion cycle (.fex -> .bin -> .fex) test for all sunxi-boards check_all_fex: $(BOARDS_DIR)/README unify-fex ./test_all_fex.sh $(BOARDS_DIR) coverage: # Usage help / invocation with no args ../sunxi-fexc -? 2> /dev/null ; exit 0 # Improve code coverage for corner cases (e.g. erroneous parameters) ./test_fex2bin_corner_cases.sh ./test_bin2fex_corner_cases.sh # Retrieve and extract sunxi-boards archive (containing all .fex) $(BOARDS_DIR).zip: curl -fLsS -o $@ $(BOARDS_URL) $(BOARDS_DIR)/README: $(BOARDS_DIR).zip @echo Extracting $< ... unzip -q $< mv sunxi-boards-master $(BOARDS_DIR) touch -r $(BOARDS_DIR) $< cat patches/*.patch | patch -p1 unify-fex: unify-fex.c $(CC) -Wall -Werror -o $@ $< clean: rm -rf $(BOARDS_DIR).zip $(BOARDS_DIR) unify-fex # # Dedicated rule for Travis CI test of sunxi-boards. This assumes that the # sunxi-tools source (archive) was extracted into a subdir below the working # directory, meaning that BOARDS_DIR should be "../.." # sunxi-boards_CI: unify-fex # compile sunxi-fexc, link bin2fex and fex2bin make -C .. bin2fex fex2bin # apply patches to BOARDS_DIR, ignore mismatches cat patches/*.patch | patch --forward -r- -p2 -d $(BOARDS_DIR) || true # and finally run the tests ./test_all_fex.sh $(BOARDS_DIR) sunxi-tools-1.4.2~git20181114.6d598a/tests/fextest.sh000077500000000000000000000006441337271345600216670ustar00rootroot00000000000000#!/bin/bash echo $0 $* FEX2BIN=../fex2bin BIN2FEX=../bin2fex FEX=$1 BIN=${FEX/%.fex/.bin} REVERSE=${FEX/%.fex/.new} ${FEX2BIN} ${FEX} ${BIN} ${BIN2FEX} ${BIN} ${REVERSE} # preprocess .fex, compare it to the bin2fex output if ./unify-fex ${FEX} | diff -uwB - ${REVERSE}; then # if successful, clean up the output files rm -f ${BIN} ${REVERSE} else echo '***' echo "*** ERROR processing ${FEX}" echo '***' exit 1 fi sunxi-tools-1.4.2~git20181114.6d598a/tests/patches/000077500000000000000000000000001337271345600212715ustar00rootroot00000000000000sunxi-tools-1.4.2~git20181114.6d598a/tests/patches/a-star_kv49l.patch000066400000000000000000000025321337271345600245340ustar00rootroot00000000000000See https://github.com/linux-sunxi/sunxi-boards/issues/51 --- orig/sunxi-boards/sys_config/a33/a-star_kv49l.fex +++ new/sunxi-boards/sys_config/a33/a-star_kv49l.fex @@ -830,25 +830,25 @@ [Vdevice] Vdevice_used = 0 -Vdevice_0 = port:P@00<0><0><0><0> -Vdevice_1 = port:P@00<0><0><0><0> +;Vdevice_0 = port:P@00<0><0><0><0> +;Vdevice_1 = port:P@00<0><0><0><0> [s_uart0] s_uart_used = 0 -s_uart_tx = port:P@00<0><0><0><0> -s_uart_rx = port:P@00<0><0><0><0> +;s_uart_tx = port:P@00<0><0><0><0> +;s_uart_rx = port:P@00<0><0><0><0> [s_rsb0] s_rsb_used = 0 -s_rsb_sck = port:P@00<0><0><0><0> -s_rsb_sda = port:P@00<0><0><0><0> +;s_rsb_sck = port:P@00<0><0><0><0> +;s_rsb_sda = port:P@00<0><0><0><0> [s_jtag0] s_jtag_used = 0 -s_jtag_tms = port:P@00<0><0><0><0> -s_jtag_tck = port:P@00<0><0><0><0> -s_jtag_tdo = port:P@00<0><0><0><0> -s_jtag_tdi = port:P@00<0><0><0><0> +;s_jtag_tms = port:P@00<0><0><0><0> +;s_jtag_tck = port:P@00<0><0><0><0> +;s_jtag_tdo = port:P@00<0><0><0><0> +;s_jtag_tdi = port:P@00<0><0><0><0> [s_powchk] s_powchk_used = 0 @@ -875,9 +875,9 @@ [leds_para] leds_used = 0 -;red_led = port:P@00<0><0><0><0> -;red_led_active_low = 0 -;green_led_active_low = 0 -;blue_led = -;blue_led_active_low = 0 +;red_led = port:P@00<0><0><0><0> +;red_led_active_low = 0 +;green_led_active_low = 0 +;blue_led = +;blue_led_active_low = 0 sunxi-tools-1.4.2~git20181114.6d598a/tests/test_all_fex.sh000077500000000000000000000002301337271345600226450ustar00rootroot00000000000000#!/bin/sh FEXFILES=fexfiles.lst find $1 -name '*.fex' > ${FEXFILES} while read fex; do ./fextest.sh ${fex} || exit done <${FEXFILES} rm -f ${FEXFILES} sunxi-tools-1.4.2~git20181114.6d598a/tests/test_bin2fex_corner_cases.sh000077500000000000000000000007611337271345600253270ustar00rootroot00000000000000#!/bin/bash # # === Test errors / corner cases of "bin2fex", improving on code coverage === # BIN2FEX=../bin2fex TESTFILE=sunxi-boards/sys_config/a10/a10-olinuxino-lime # use sunxi-fexc in "fex2bin" mode, testing explicit parameters at the same time FEX2BIN="../sunxi-fexc -v -q -I fex -O bin" ${FEX2BIN} ${TESTFILE}.fex ${TESTFILE}.bin # have bin2fex explicitly read /dev/stdin, to force use of fexc.c's "read_all()" cat ${TESTFILE}.bin | ${BIN2FEX} /dev/stdin > /dev/null rm -f ${TESTFILE}.bin sunxi-tools-1.4.2~git20181114.6d598a/tests/test_fex2bin_corner_cases.sh000077500000000000000000000025251337271345600253270ustar00rootroot00000000000000#!/bin/bash # # === Test errors / corner cases of "fex2bin", improving on code coverage === # FEX2BIN=../fex2bin function expect () { OUT=`${FEX2BIN} 2>&1` if (! echo ${OUT} | grep -q "$1"); then echo ERROR: Expected substring \"$1\" not found in output: echo ${OUT} exit 1 fi #echo ${OUT} } # missing section, CRLF line ending echo -e "foobar\r\n" | expect "data must follow a section" # malformed sections expect "incomplete section declaration" <<-EOF [foobar EOF expect "invalid character at 5" <<-EOF [foo#bar] EOF # invalid entry expect "invalid character at 4" <<-EOF [foo] bar EOF # bad port specifiers expect "parse error at 12" <<-EOF [foo] bar = port:P@0 EOF expect "invalid character at 14" <<-EOF [foo] bar = port:PA* EOF expect "port out of range at 14" <<-EOF [foo] bar = port:PA666 EOF expect "value out of range at 17" <<-EOF [foo] bar = port:PA00<-1> EOF expect "invalid character at 18" <<-EOF [foo] bar = port:PA00<0 > EOF # bad = pairs expect "invalid character at 8" <<-EOF [foo] bar = 0* EOF expect "value out of range" <<-EOF [foo] bar = 4294967296 EOF expect "unquoted value 'bad', assuming string" <<-EOF [foo] bar = bad EOF # test truncation of very long identifiers ${FEX2BIN} > /dev/null <<-EOF [an_overly_long_section_name_to_truncate] an_overly_long_entry_name_to_truncate = 0 EOF sunxi-tools-1.4.2~git20181114.6d598a/tests/unify-fex.c000066400000000000000000000075641337271345600217340ustar00rootroot00000000000000/* * Copyright (C) 2016 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 . */ /* * unify-fex.c * * A utility program to do some standard transformations on .fex files, * to allow simpler (diff) comparison with the output of bin2fex. */ #include #include #include #include #include /* string macro to determine if str starts with a given literal */ #define starts(str, literal) \ (strncmp(str, "" literal, sizeof(literal) - 1) == 0) int main(int argc, char **argv) { FILE *input = stdin; char line[1024]; char *c, *p; int64_t num; if (argc >= 2) { input = fopen(argv[1], "r"); if (!input) { perror("failed to open input file"); exit(EXIT_FAILURE); } } /* loop over all input lines, output goes to stdout */ while (fgets(line, sizeof(line), input)) { /* strip all whitespace (even CR/LF) from the input line */ for (c = p = line; *p; p++) { if (!isspace(*p)) *c++ = *p; } *c = '\0'; if (*line == ';' || *line == '#') /* This is a comment line, simply ignore it */ continue; if (*line == ':') continue; /* suspect malformed comment, ignore */ if ((p = strchr(line, '='))) { /* This is a = line, reformat it */ c = strdup(p + 1); sprintf(p, " = %s", c); free(c); p += 3; /* have p point to the beginning of */ if (starts(p, "port:")) { if (p[5] == 'P') { /* port:P... */ /* get pin number (including bank) */ num = ((p[6] - 'A') << 5) + strtoll(p + 7, &c, 10); c = strdup(c); sprintf(p, "port:P%c%02" PRId64 "%s", 'A' + (int)(num >> 5), num & 0x1F, c); free(c); /* check angle brackets to determine options count */ num = 0; for (c = p + 9; *c; c++) { if (*c == '<') num++; } /* append "" for missing options */ c = strrchr(p, '\0'); while (num < 4) { c += sprintf(c, ""); num++; } } } else { /* * fix formatting of numeric values, depending on the keyword * these are a bit nasty, since they vary wildly between hex * and decimal - see decompile_single_mode() in script_fex.c */ num = strtoll(p, NULL, 0); if (num || *p == '0') { int hex = starts(line, "csi_twi_addr"); hex |= starts(line, "ctp_twi_addr"); hex |= starts(line, "dram_baseaddr"); hex |= starts(line, "dram_emr"); hex |= starts(line, "dram_tpr"); hex |= starts(line, "dram_zq"); hex |= starts(line, "g2d_size"); hex |= starts(line, "gsensor_twi_addr"); hex |= starts(line, "lcd_gamma_tbl_"); hex |= starts(line, "rtp_press_threshold "); hex |= starts(line, "rtp_sensitive_level"); hex |= starts(line, "tkey_twi_addr"); /* large decimals will be decompiled as negative */ if (!hex && num >= 2147483648LL) num -= 4294967296LL; sprintf(p, hex ? "0x%" PRIx64 : "%" PRId64, num); } else { /* * We expect all other (= non-numeric) values * to be strings, always quote them. */ if (*p && (*p != '"')) { c = strdup(p); sprintf(p, "\"%s\"", c); free(c); } } } } puts(line); } if (ferror(input)) { perror("file read error"); exit(EXIT_FAILURE); } fclose(input); return EXIT_SUCCESS; } sunxi-tools-1.4.2~git20181114.6d598a/thunks/000077500000000000000000000000001337271345600200145ustar00rootroot00000000000000sunxi-tools-1.4.2~git20181114.6d598a/thunks/Makefile000066400000000000000000000017661337271345600214660ustar00rootroot00000000000000# # build "preprocessed" .h files for inclusion of ARM scratch code # SPL_THUNK := fel-to-spl-thunk.h THUNKS := clrsetbits.h THUNKS += memcpy.h THUNKS += readl_writel.h THUNKS += rmr-thunk.h THUNKS += sid_read_root.h all: $(SPL_THUNK) $(THUNKS) # clean up object files afterwards rm -f *.o # This empty prerequisite enforces a rebuild of all the headers on every run FORCE: # If not specified explicitly: try to guess a suitable ARM toolchain prefix CROSS_COMPILE ?= $(shell ../find-arm-gcc.sh) AS := $(CROSS_COMPILE)as OBJDUMP := $(CROSS_COMPILE)objdump AWK_O_TO_H := LC_ALL=C awk -f objdump_to_h.awk # The SPL thunk requires a different output format. The "style" variable for # awk controls this, and causes the htole32() conversion to be omitted. fel-to-spl-thunk.h: fel-to-spl-thunk.S FORCE $(AS) -o $(subst .S,.o,$<) $< $(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) -v style=old > $@ $(THUNKS): %.h: %.S FORCE $(AS) -o $(subst .S,.o,$<) $< $(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) > $@ sunxi-tools-1.4.2~git20181114.6d598a/thunks/README.md000066400000000000000000000015511337271345600212750ustar00rootroot00000000000000 # thunks/README.md This directory contains assembly sources for ARM [thunk] code, and a corresponding _Makefile_. The idea is that the resulting binary routines can be transferred to a suitable target device and then executed 'remotely', usually via `sunxi-fel`. Normally you don't need to change or (re)build anything within this folder. Currently our main build process (via the parent directory's _Makefile_) only includes `fel-to-spl-thunk.h` directly. Other _.h_ files are provided **just for reference**. The main purpose of this folder is simply keeping track of _.S_ sources, to help with possible future maintenance of the various code snippets. Please note that any files lacking explicit license information are intended to be covered by the project's [overall license](../LICENSE.md) (GPLv2). [thunk]: https://en.wikipedia.org/wiki/Thunk#Interoperability sunxi-tools-1.4.2~git20181114.6d598a/thunks/clrsetbits.S000066400000000000000000000007121337271345600223160ustar00rootroot00000000000000/* * Thunk code to assist with bitwise operations (set/clear) via FEL */ fel_clrsetbits_le32: ldr r0, 1f /* address */ ldr r1, [r0] /* load value */ ldr r2, 2f /* clrbits mask */ bic r1, r2 /* clear bits, post-increment r1 */ ldr r2, 3f /* setbits mask */ orr r1, r2 /* set bits (logical "or") */ str r1, [r0] /* store result */ bx lr 1: .word 0 /* addr */ 2: .word 0 /* clrbits (= bits to clear) */ 3: .word 0 /* setbits (= bits to set) */ sunxi-tools-1.4.2~git20181114.6d598a/thunks/clrsetbits.h000066400000000000000000000010571337271345600223460ustar00rootroot00000000000000 /* : */ htole32(0xe59f0018), /* 0: ldr r0, [pc, #24] */ htole32(0xe5901000), /* 4: ldr r1, [r0] */ htole32(0xe59f2014), /* 8: ldr r2, [pc, #20] */ htole32(0xe1c11002), /* c: bic r1, r1, r2 */ htole32(0xe59f2010), /* 10: ldr r2, [pc, #16] */ htole32(0xe1811002), /* 14: orr r1, r1, r2 */ htole32(0xe5801000), /* 18: str r1, [r0] */ htole32(0xe12fff1e), /* 1c: bx lr */ sunxi-tools-1.4.2~git20181114.6d598a/thunks/fel-to-spl-thunk.S000066400000000000000000000116231337271345600232540ustar00rootroot00000000000000/* * 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.2~git20181114.6d598a/thunks/fel-to-spl-thunk.h000066400000000000000000000117311337271345600233010ustar00rootroot00000000000000 /* : */ 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.2~git20181114.6d598a/thunks/memcpy.S000066400000000000000000000037301337271345600214350ustar00rootroot00000000000000/* * copy "upwards", increasing destination and source addresses */ fel_memcpy_up: ldr r0, 1f /* dst_addr */ ldr r1, 2f /* src_addr */ ldr r2, 3f /* bytes */ sub r3, r1, r0 tst r3, #3 /* test LSB for word alignment */ bne copyup_tail /* unaligned access, copy byte-wise */ copyup_head: tst r1, #3 /* word boundary? */ beq copyup_loop ldrb r3, [r1], #1 /* load and post-inc */ strb r3, [r0], #1 /* store and post-inc */ subs r2, #1 /* r2 -= 1 */ bpl copyup_head bx lr /* early return on small byte count (r2 < 0) */ copyup_loop: subs r2, #4 /* r2 -= 4 */ ldrpl r3, [r1], #4 /* load and post-inc */ strpl r3, [r0], #4 /* store and post-inc */ bpl copyup_loop /* while (r2 >= 0) */ add r2, #4 /* r2 = remaining byte count */ copyup_tail: subs r2, #1 /* r2 -= 1 */ bxmi lr /* return on (r2 < 0) */ ldrb r3, [r1], #1 /* load and post-inc */ strb r3, [r0], #1 /* store and post-inc */ b copyup_tail 1: .word 0 /* dst_addr */ 2: .word 0 /* src_addr */ 3: .word 0 /* bytes */ /* * copy "downwards", using base-relative indexing */ fel_memcpy_down: ldr r0, 1f /* dst_addr */ ldr r1, 2f /* src_addr */ ldr r2, 3f /* bytes */ sub r3, r0, r1 tst r3, #3 /* test LSB for word alignment */ bne copydn_tail /* unaligned access, copy byte-wise */ copydn_head: add r3, r1, r2 /* r3 = r1 + r2, for alignment check */ tst r3, #3 /* word boundary? */ beq copydn_loop subs r2, #1 /* r2 -= 1 */ bxmi lr /* early return on small byte count (r2 < 0) */ ldrb r3, [r1, r2] /* load byte */ strb r3, [r0, r2] /* store byte */ b copydn_head copydn_loop: subs r2, #4 /* r2 -= 4 */ ldrpl r3, [r1, r2] /* load word */ strpl r3, [r0, r2] /* store word */ bpl copydn_loop /* while (r2 >= 0) */ add r2, #4 /* r2 = remaining byte count */ copydn_tail: subs r2, #1 /* r2 -= 1 */ bxmi lr /* return on (r2 < 0) */ ldrb r3, [r1, r2] /* load byte */ strb r3, [r0, r2] /* store byte */ b copydn_tail 1: .word 0 /* dst_addr */ 2: .word 0 /* src_addr */ 3: .word 0 /* bytes */ sunxi-tools-1.4.2~git20181114.6d598a/thunks/memcpy.h000066400000000000000000000063341337271345600214650ustar00rootroot00000000000000 /* : */ htole32(0xe59f0054), /* 0: ldr r0, [pc, #84] */ htole32(0xe59f1054), /* 4: ldr r1, [pc, #84] */ htole32(0xe59f2054), /* 8: ldr r2, [pc, #84] */ htole32(0xe0413000), /* c: sub r3, r1, r0 */ htole32(0xe3130003), /* 10: tst r3, #3 */ htole32(0x1a00000b), /* 14: bne 48 */ /* : */ htole32(0xe3110003), /* 18: tst r1, #3 */ htole32(0x0a000004), /* 1c: beq 34 */ htole32(0xe4d13001), /* 20: ldrb r3, [r1], #1 */ htole32(0xe4c03001), /* 24: strb r3, [r0], #1 */ htole32(0xe2522001), /* 28: subs r2, r2, #1 */ htole32(0x5afffff9), /* 2c: bpl 18 */ htole32(0xe12fff1e), /* 30: bx lr */ /* : */ htole32(0xe2522004), /* 34: subs r2, r2, #4 */ htole32(0x54913004), /* 38: ldrpl r3, [r1], #4 */ htole32(0x54803004), /* 3c: strpl r3, [r0], #4 */ htole32(0x5afffffb), /* 40: bpl 34 */ htole32(0xe2822004), /* 44: add r2, r2, #4 */ /* : */ htole32(0xe2522001), /* 48: subs r2, r2, #1 */ htole32(0x412fff1e), /* 4c: bxmi lr */ htole32(0xe4d13001), /* 50: ldrb r3, [r1], #1 */ htole32(0xe4c03001), /* 54: strb r3, [r0], #1 */ htole32(0xeafffffa), /* 58: b 48 */ /* : */ htole32(0xe59f0058), /* 68: ldr r0, [pc, #88] */ htole32(0xe59f1058), /* 6c: ldr r1, [pc, #88] */ htole32(0xe59f2058), /* 70: ldr r2, [pc, #88] */ htole32(0xe0403001), /* 74: sub r3, r0, r1 */ htole32(0xe3130003), /* 78: tst r3, #3 */ htole32(0x1a00000c), /* 7c: bne b4 */ /* : */ htole32(0xe0813002), /* 80: add r3, r1, r2 */ htole32(0xe3130003), /* 84: tst r3, #3 */ htole32(0x0a000004), /* 88: beq a0 */ htole32(0xe2522001), /* 8c: subs r2, r2, #1 */ htole32(0x412fff1e), /* 90: bxmi lr */ htole32(0xe7d13002), /* 94: ldrb r3, [r1, r2] */ htole32(0xe7c03002), /* 98: strb r3, [r0, r2] */ htole32(0xeafffff7), /* 9c: b 80 */ /* : */ htole32(0xe2522004), /* a0: subs r2, r2, #4 */ htole32(0x57913002), /* a4: ldrpl r3, [r1, r2] */ htole32(0x57803002), /* a8: strpl r3, [r0, r2] */ htole32(0x5afffffb), /* ac: bpl a0 */ htole32(0xe2822004), /* b0: add r2, r2, #4 */ /* : */ htole32(0xe2522001), /* b4: subs r2, r2, #1 */ htole32(0x412fff1e), /* b8: bxmi lr */ htole32(0xe7d13002), /* bc: ldrb r3, [r1, r2] */ htole32(0xe7c03002), /* c0: strb r3, [r0, r2] */ htole32(0xeafffffa), /* c4: b b4 */ sunxi-tools-1.4.2~git20181114.6d598a/thunks/objdump_to_h.awk000066400000000000000000000012721337271345600231730ustar00rootroot00000000000000# labels /[[:xdigit:]]+ <\w+>:/ { # (Note: using $0 instead of $2 would also include the address) if (style=="old") printf "\t/* %s */\n", $2 else printf "\t\t/* %s */\n", $2 } # disassembly lines /[[:xdigit:]]+:/ { if (style=="old") printf "\t0x%s, /* %9s %-10s", $2, $1, $3 else printf "\t\thtole32(0x%s), /* %5s %-5s", $2, $1, $3 for (i = 4; i <= NF; i++) if ($i == ";") { # strip comment (anything after and including ';') NF = i - 1 break } # clear $1 to $3, which re-calculates $0 (= remainder of line) $3 = "" $2 = "" $1 = "" gsub("^\\s+", "") # strip leading whitespace if (style=="old") printf " %-28s */\n", $0 else printf " %-23s */\n", $0 } sunxi-tools-1.4.2~git20181114.6d598a/thunks/readl_writel.S000066400000000000000000000014601337271345600226160ustar00rootroot00000000000000/* * Thunk code for buffered 'long' (i.e. 32-bit) read and write operations */ .equ MAX_WORDS, 0x100 - 12 fel_readl_n: ldr r0, 1f /* read_addr */ adr r1, 3f /* read_data */ ldr r2, 2f /* read_count */ /* limit word count to a maximum value */ cmp r2, #MAX_WORDS movgt r2, #MAX_WORDS read_loop: subs r2, #1 bxmi lr ldr r3, [r0], #4 str r3, [r1], #4 b read_loop 1: .word 0 /* read_addr */ 2: .word 0 /* read_count */ 3: .word 0 /* read_data */ fel_writel_n: ldr r0, 1f /* write_addr */ adr r1, 3f /* write_data */ ldr r2, 2f /* write_count */ /* limit word count to a maximum value */ cmp r2, #MAX_WORDS movgt r2, #MAX_WORDS write_loop: subs r2, #1 bxmi lr ldr r3, [r1], #4 str r3, [r0], #4 b write_loop 1: .word 0 /* write_addr */ 2: .word 0 /* write_count */ 3: .word 0 /* write_data */ sunxi-tools-1.4.2~git20181114.6d598a/thunks/readl_writel.h000066400000000000000000000026021337271345600226420ustar00rootroot00000000000000 /* : */ htole32(0xe59f0020), /* 0: ldr r0, [pc, #32] */ htole32(0xe28f1024), /* 4: add r1, pc, #36 */ htole32(0xe59f201c), /* 8: ldr r2, [pc, #28] */ htole32(0xe35200f4), /* c: cmp r2, #244 */ htole32(0xc3a020f4), /* 10: movgt r2, #244 */ /* : */ htole32(0xe2522001), /* 14: subs r2, r2, #1 */ htole32(0x412fff1e), /* 18: bxmi lr */ htole32(0xe4903004), /* 1c: ldr r3, [r0], #4 */ htole32(0xe4813004), /* 20: str r3, [r1], #4 */ htole32(0xeafffffa), /* 24: b 14 */ /* : */ htole32(0xe59f0020), /* 34: ldr r0, [pc, #32] */ htole32(0xe28f1024), /* 38: add r1, pc, #36 */ htole32(0xe59f201c), /* 3c: ldr r2, [pc, #28] */ htole32(0xe35200f4), /* 40: cmp r2, #244 */ htole32(0xc3a020f4), /* 44: movgt r2, #244 */ /* : */ htole32(0xe2522001), /* 48: subs r2, r2, #1 */ htole32(0x412fff1e), /* 4c: bxmi lr */ htole32(0xe4913004), /* 50: ldr r3, [r1], #4 */ htole32(0xe4803004), /* 54: str r3, [r0], #4 */ htole32(0xeafffffa), /* 58: b 48 */ sunxi-tools-1.4.2~git20181114.6d598a/thunks/rmr-thunk.S000066400000000000000000000011541337271345600220700ustar00rootroot00000000000000/* * Request AArch32/AArch64 warm reset, using RVBAR and Reset Management Register */ rmr_request: ldr r0, 1f /* RVBAR register address */ ldr r1, 2f /* desired entry point (reset vector) */ str r1, [r0] dsb isb /* make sure we write the address */ ldr r1, 3f /* RMR mode: bit 1 = RR, bit 0 = AA64 */ mrc p15, 0, r0, c12, c0, 2 /* read RMR */ orr r0, r0, r1 /* request warm reset (according to rmr_mode) */ mcr p15, 0, r0, c12, c0, 2 /* write RMR, trigger reset */ isb 0: wfi b 0b /* loop */ 1: .word 0 /* rvbar_reg */ 2: .word 0 /* entry_point */ 3: .word 0 /* rmr_mode (2 = AArch32, 3 = AArch64) */ sunxi-tools-1.4.2~git20181114.6d598a/thunks/rmr-thunk.h000066400000000000000000000014631337271345600221200ustar00rootroot00000000000000 /* : */ htole32(0xe59f0028), /* 0: ldr r0, [pc, #40] */ htole32(0xe59f1028), /* 4: ldr r1, [pc, #40] */ htole32(0xe5801000), /* 8: str r1, [r0] */ htole32(0xf57ff04f), /* c: dsb sy */ htole32(0xf57ff06f), /* 10: isb sy */ htole32(0xe59f101c), /* 14: ldr r1, [pc, #28] */ htole32(0xee1c0f50), /* 18: mrc 15, 0, r0, cr12, cr0, {2} */ htole32(0xe1800001), /* 1c: orr r0, r0, r1 */ htole32(0xee0c0f50), /* 20: mcr 15, 0, r0, cr12, cr0, {2} */ htole32(0xf57ff06f), /* 24: isb sy */ htole32(0xe320f003), /* 28: wfi */ htole32(0xeafffffd), /* 2c: b 28 */ sunxi-tools-1.4.2~git20181114.6d598a/thunks/sid_read_root.S000066400000000000000000000054071337271345600227630ustar00rootroot00000000000000/* * Copyright (C) 2016 Bernhard Nortmann * * 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. */ /* * ARM thunk code to read the SID root key using register-based access. * * This is necessary for certain SoCs (e.g. H3), as the values read via * memory mapping might not be consistent. For background information see * https://groups.google.com/forum/#!topic/linux-sunxi/ynyIP8c61Qs */ SID_BASE .req r0 sid_key_index .req r1 .set SID_PRCTL, 0x40 /* SID program/read control register */ .set SID_PRKEY, 0x50 /* SID program key value register */ .set SID_RDKEY, 0x60 /* SID read key value register */ .set SID_OP_LOCK, 0xAC /* Efuse operation lock value */ .set SID_READ_START, (1 << 1) /* bit 1 of SID_PRCTL, Software Read Start */ .set SID_PG_START, (1 << 0) /* bit 0 of SID_PRCTL, Software Program Start */ sid_read_root_key: ldr SID_BASE, sid_base mov sid_key_index, #0 adr r3, sid_result /* result pointer */ sid_read_loop: mov r2, sid_key_index, lsl #16 /* PG_INDEX value */ orr r2, #SID_OP_LOCK << 8 /* OP_LOCK to enable SID_READ_START */ orr r2, #SID_READ_START str r2, [SID_BASE, #SID_PRCTL] /* write SID_PRCTL */ sid_read_wait: ldr r2, [SID_BASE, #SID_PRCTL] /* read SID_PRCTL */ tst r2, #SID_READ_START /* check if read operation completed */ bne sid_read_wait /* loop while bit 1 still set */ ldr r2, [SID_BASE, #SID_RDKEY] /* read SID key value */ str r2, [r3, sid_key_index] /* store SID value */ add sid_key_index, #4 cmp sid_key_index, #16 blo sid_read_loop /* loop while (sid_key_index < 0x10) */ mov r2, #0 str r2, [SID_BASE, #SID_PRCTL] /* clear SID_PRCTL */ bx lr sid_base: .word 0 sid_result: .word 0 /* receives the four "root key" 32-bit words */ .word 0 .word 0 .word 0 sunxi-tools-1.4.2~git20181114.6d598a/thunks/sid_read_root.h000066400000000000000000000025371337271345600230110ustar00rootroot00000000000000 /* : */ htole32(0xe59f0040), /* 0: ldr r0, [pc, #64] */ htole32(0xe3a01000), /* 4: mov r1, #0 */ htole32(0xe28f303c), /* 8: add r3, pc, #60 */ /* : */ htole32(0xe1a02801), /* c: lsl r2, r1, #16 */ htole32(0xe3822b2b), /* 10: orr r2, r2, #44032 */ htole32(0xe3822002), /* 14: orr r2, r2, #2 */ htole32(0xe5802040), /* 18: str r2, [r0, #64] */ /* : */ htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */ htole32(0xe3120002), /* 20: tst r2, #2 */ htole32(0x1afffffc), /* 24: bne 1c */ htole32(0xe5902060), /* 28: ldr r2, [r0, #96] */ htole32(0xe7832001), /* 2c: str r2, [r3, r1] */ htole32(0xe2811004), /* 30: add r1, r1, #4 */ htole32(0xe3510010), /* 34: cmp r1, #16 */ htole32(0x3afffff3), /* 38: bcc c */ htole32(0xe3a02000), /* 3c: mov r2, #0 */ htole32(0xe5802040), /* 40: str r2, [r0, #64] */ htole32(0xe12fff1e), /* 44: bx lr */ /* : */ htole32(0x00000000), /* 48: .word 0x00000000 */ /* : */ sunxi-tools-1.4.2~git20181114.6d598a/uart0-helloworld-sdboot.c000066400000000000000000000446311337271345600233500ustar00rootroot00000000000000/* * 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 #define H6_UART0_BASE 0x05000000 #define H6_PIO_BASE 0x0300B000 #define H6_CCM_BASE 0x03001000 #define H6_SRAMCTRL_BASE 0x03000000 /***************************************************************************** * 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 SUN8I_V3S_GPB_UART0 (3) #define SUN50I_H5_GPA_UART0 (2) #define SUN50I_H6_GPH_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) static u32 pio_base; 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 *)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 *)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 *)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 *)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. * * * * Allwinner H6 has its memory map totally reworked, but the SRAM controller * * remains similar; the base of it is moved to 0x03000000. * *****************************************************************************/ #define VER_REG (AW_SRAMCTRL_BASE + 0x24) #define H6_VER_REG (H6_SRAMCTRL_BASE + 0x24) #define SUN4I_SID_BASE 0x01C23800 #define SUN8I_SID_BASE 0x01C14000 #define SID_PRCTL 0x40 /* SID program/read control register */ #define SID_RDKEY 0x60 /* SID read key value register */ #define SID_OP_LOCK 0xAC /* Efuse operation lock value */ #define SID_READ_START (1 << 1) /* bit 1 of SID_PRCTL, Software Read Start */ u32 sid_read_key(u32 sid_base, u32 offset) { u32 reg_val; reg_val = (offset & 0x1FF) << 16; /* PG_INDEX value */ reg_val |= (SID_OP_LOCK << 8) | SID_READ_START; /* request read access */ writel(reg_val, sid_base + SID_PRCTL); while (readl(sid_base + SID_PRCTL) & SID_READ_START) ; /* wait while busy */ reg_val = readl(sid_base + SID_RDKEY); /* read SID key value */ writel(0, sid_base + SID_PRCTL); /* clear SID_PRCTL (removing SID_OP_LOCK) */ return reg_val; } 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 { u32 reg; /* * This register is GICD_IIDR on H6, but unmapped according to * other known SoCs' user manuals. */ reg = readl(0x03021008); if ((reg & 0xfff) == 0x43b) /* Found GICv2 here, so it's a H6 */ reg = H6_VER_REG; else reg = VER_REG; set_wbit(reg, 1 << 15); soc_id = readl(reg) >> 16; } } /* Most SoCs can reliably be distinguished by simply checking their ID value */ #define soc_is_a10() (soc_id == 0x1623) #define soc_is_a20() (soc_id == 0x1651) #define soc_is_a31() (soc_id == 0x1633) #define soc_is_a80() (soc_id == 0x1639) #define soc_is_a64() (soc_id == 0x1689) #define soc_is_h5() (soc_id == 0x1718) #define soc_is_h6() (soc_id == 0x1728) #define soc_is_r40() (soc_id == 0x1701) #define soc_is_v3s() (soc_id == 0x1681) /* A10s and A13 share the same ID, so we need a little more effort on those */ int soc_is_a10s(void) { return soc_id == 0x1625 && (readl(SUN4I_SID_BASE + 8) & 0xf000) == 0x7000; } int soc_is_a13(void) { return soc_id == 0x1625 && (readl(SUN4I_SID_BASE + 8) & 0xf000) != 0x7000; } /* H2+ and H3 share the same ID, we can differentiate them by SID_RKEY0 */ int soc_is_h2_plus(void) { if (soc_id != 0x1680) return 0; u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0); return (sid0 & 0xff) == 0x42 || (sid0 & 0xff) == 0x83; } int soc_is_h3(void) { if (soc_id != 0x1680) return 0; u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0); /* * Note: according to Allwinner sources, H3 is expected * to show up as 0x00, 0x81 or ("H3D") 0x58 here. */ return (sid0 & 0xff) != 0x42 && (sid0 & 0xff) != 0x83; } /***************************************************************************** * 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) #define H6_UART_GATE_RESET (H6_CCM_BASE + 0x90C) #define H6_UART_GATE_SHIFT (0) #define H6_UART_RESET_SHIFT (16) void clock_init_uart_legacy(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)); } void clock_init_uart_h6(void) { /* Open the clock gate for UART0 */ set_wbit(H6_UART_GATE_RESET, 1 << (H6_UART_GATE_SHIFT + CONFIG_CONS_INDEX - 1)); /* Deassert UART0 reset */ set_wbit(H6_UART_GATE_RESET, 1 << (H6_UART_RESET_SHIFT + CONFIG_CONS_INDEX - 1)); } void clock_init_uart(void) { if (soc_is_h6()) clock_init_uart_h6(); else clock_init_uart_legacy(); } /***************************************************************************** * 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() || soc_is_r40()) { 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() || soc_is_h2_plus()) { 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 if (soc_is_h5()) { sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN50I_H5_GPA_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN50I_H5_GPA_UART0); sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP); } else if (soc_is_h6()) { sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H6_GPH_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H6_GPH_UART0); sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP); } else if (soc_is_v3s()) { sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN8I_V3S_GPB_UART0); sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_V3S_GPB_UART0); sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP); } else { /* Unknown SoC */ while (1) {} } } /*****************************************************************************/ static u32 uart0_base; #define UART0_RBR (uart0_base + 0x0) /* receive buffer register */ #define UART0_THR (uart0_base + 0x0) /* transmit holding register */ #define UART0_DLL (uart0_base + 0x0) /* divisor latch low register */ #define UART0_DLH (uart0_base + 0x4) /* divisor latch high register */ #define UART0_IER (uart0_base + 0x4) /* interrupt enable reigster */ #define UART0_IIR (uart0_base + 0x8) /* interrupt identity register */ #define UART0_FCR (uart0_base + 0x8) /* fifo control register */ #define UART0_LCR (uart0_base + 0xc) /* line control register */ #define UART0_LSR (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() || soc_is_h5()) spl_signature = (void *)0x10004; if (soc_is_h6()) spl_signature = (void *)0x20004; /* 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; } void bases_init(void) { if (soc_is_h6()) { pio_base = H6_PIO_BASE; uart0_base = H6_UART0_BASE; } else { pio_base = SUNXI_PIO_BASE; uart0_base = SUNXI_UART0_BASE; } } int main(void) { soc_detection_init(); bases_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_h2_plus()) uart0_puts("Allwinner H2+!\n"); else if (soc_is_h3()) uart0_puts("Allwinner H3!\n"); else if (soc_is_h5()) uart0_puts("Allwinner H5!\n"); else if (soc_is_h6()) uart0_puts("Allwinner H6!\n"); else if (soc_is_r40()) uart0_puts("Allwinner R40!\n"); else if (soc_is_v3s()) uart0_puts("Allwinner V3s!\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.2~git20181114.6d598a/uart0-helloworld-sdboot.lds000066400000000000000000000017161337271345600237050ustar00rootroot00000000000000/* * 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*) } }