pax_global_header00006660000000000000000000000064147626334570014533gustar00rootroot0000000000000052 comment=4390ca668f3b2e62f885edb6952b189c4489d83d sunxi-tools-1.4.2+git20240825.4390ca/000077500000000000000000000000001476263345700163535ustar00rootroot00000000000000sunxi-tools-1.4.2+git20240825.4390ca/.github/000077500000000000000000000000001476263345700177135ustar00rootroot00000000000000sunxi-tools-1.4.2+git20240825.4390ca/.github/workflows/000077500000000000000000000000001476263345700217505ustar00rootroot00000000000000sunxi-tools-1.4.2+git20240825.4390ca/.github/workflows/build_check.yml000066400000000000000000000006031476263345700247260ustar00rootroot00000000000000name: Build host tools on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install prerequisites run: sudo apt-get install -y libusb-1.0-0-dev zlib1g-dev libfdt-dev - name: make run: make tools misc - name: make check run: make check sunxi-tools-1.4.2+git20240825.4390ca/.gitignore000066400000000000000000000001421476263345700203400ustar00rootroot00000000000000bin2fex fex2bin sunxi-bootinfo sunxi-fel sunxi-fexc sunxi-nand-part sunxi-pio version.h *.o *.swp sunxi-tools-1.4.2+git20240825.4390ca/.travis.yml000066400000000000000000000021211476263345700204600ustar00rootroot00000000000000# 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+git20240825.4390ca/LICENSE.md000066400000000000000000000427451476263345700177730ustar00rootroot00000000000000### 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+git20240825.4390ca/Makefile000066400000000000000000000153721476263345700200230ustar00rootroot00000000000000# 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/ PKG_CONFIG ?= pkg-config # 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 MANDIR ?= $(PREFIX)/share/man/man1 .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 -D -m0644 -t $(DESTDIR)$(MANDIR) sunxi-fel.1 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 fit_image.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) -lfdt 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) -march=armv5te -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) -march=armv5te -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+git20240825.4390ca/README.md000066400000000000000000000135771476263345700176470ustar00rootroot00000000000000# 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. Development versions of zlib and libfdt are also required. To install the dependencies on Ubuntu 20.04 using package manager: ```bash sudo apt install libusb-1.0-0-dev libz-dev libfdt-dev ``` 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+git20240825.4390ca/adb-devprobe.sh000077500000000000000000000033731476263345700212520ustar00rootroot00000000000000#!/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+git20240825.4390ca/autoversion.sh000077500000000000000000000010021476263345700212610ustar00rootroot00000000000000# # 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+git20240825.4390ca/bin/000077500000000000000000000000001476263345700171235ustar00rootroot00000000000000sunxi-tools-1.4.2+git20240825.4390ca/bin/fel-sdboot.sunxi000066400000000000000000000200001476263345700222410ustar00rootroot00000000000000eGON.BT0n' SPL   / sunxi-tools-1.4.2+git20240825.4390ca/bin/jtag-loop.sunxi000066400000000000000000000010001476263345700220760ustar00rootroot00000000000000 eGON.BT0dreDDDsunxi-tools-1.4.2+git20240825.4390ca/bin/ramboot.scr000066400000000000000000000003661476263345700213040ustar00rootroot00000000000000'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+git20240825.4390ca/bin/ramboot.uboot-sh000066400000000000000000000002561476263345700222530ustar00rootroot00000000000000# 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+git20240825.4390ca/bin/uart0-helloworld-sdboot.sunxi000066400000000000000000000200001476263345700246770ustar00rootroot00000000000000eGON.BT0S SPL9-1804000"2"1<<1@-0PP@L0@@00"" 1<<1,4 -H000  80 4000 0Q000<000",,000003/t`T@-@@p@-@Pp@ (0+@@00`@0/d d0!QX0X 0 /L00H DR@00 ) 0 0 #80/ 9;$$\ 00 ,0R$0 :@s//$ %0,0 (0R 0  P// %0<000Z S @-$PBP/ @<000Z S @-$BPP/T @0l (l "("/0 ) ) )( )/0 ) ) )( )/X0X T0RR00 R0BA0S  0RP (Qp@-434S 0CRR 0 3030$303RR00R0S 677p@$202020JP 344OP 34 b`0"S!S(z)w)GP1P kh0!S  S)[*X*H!SOL!S . S$@%=% S(4 S,)lS S_h S) Y #Qt h \ 3(#!@- $000   000 @ /`@-@@0S S  0 0RR00R0S00RR RcRRxQ0ppR0h Q X Q$0S S///9QeGON.BT000 0SS   S CA R 0 0 $00 / S0 0 |00%&pp SS$`00 X00P0P 0 H0+0(Qt|Y@H$( Hello from Allwinner A10! Allwinner A10s! Allwinner A13! Allwinner A20! Allwinner A31/A31s! Allwinner A64! Allwinner H2+! Allwinner H3! Allwinner H5! Allwinner A63! Allwinner H6! Allwinner H616! Allwinner R329! Allwinner R40! Allwinner V3s! Allwinner V831! Allwinner V853! Allwinner R528/T113! Allwinner V5! 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. @-AB-@ 1RPg Pi PkJPVP0!S !S |!Sx QPh_PX0L!SH @!S< 4!S0 (!S$ !S !S  !S S S S S ea]#Q~3z{wsje(a#\QYVRNKYH!JMqsunxi-tools-1.4.2+git20240825.4390ca/boot_head.S000066400000000000000000000026071476263345700204300ustar00rootroot00000000000000/* * 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+git20240825.4390ca/boot_head.lds000066400000000000000000000016611476263345700210070ustar00rootroot00000000000000/* * 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+git20240825.4390ca/bootinfo.c000066400000000000000000000306351476263345700203450ustar00rootroot00000000000000/* * (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+git20240825.4390ca/common.h000066400000000000000000000030351476263345700200150ustar00rootroot00000000000000/* * 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+git20240825.4390ca/fel-gpio000077500000000000000000000033441476263345700200070ustar00rootroot00000000000000#!/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+git20240825.4390ca/fel-remotefunc-compiler.rb000077500000000000000000000122431476263345700234300ustar00rootroot00000000000000#!/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.exist?(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 -Os -marm -march=armv5te -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+git20240825.4390ca/fel-remotefunc-spi-data-transfer.c000066400000000000000000000125031476263345700247550ustar00rootroot00000000000000/* * 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+git20240825.4390ca/fel-remotefunc-spi-data-transfer.h000066400000000000000000000256751476263345700250000ustar00rootroot00000000000000/* 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, 0x4f, 0x2d, 0xe9, /* 0: push {r4, r5, r6, r7, r8, r9, sl, fp, lr} */ 0xc8, 0x91, 0x9f, 0xe5, /* 4: ldr r9, [pc, #456] */ 0x14, 0xd0, 0x4d, 0xe2, /* 8: sub sp, sp, #20 */ 0x00, 0x20, 0x8d, 0xe5, /* c: str r2, [sp] */ 0x0c, 0x20, 0x8d, 0xe2, /* 10: add r2, sp, #12 */ 0x04, 0x20, 0x8d, 0xe5, /* 14: str r2, [sp, #4] */ 0x01, 0x60, 0xd0, 0xe5, /* 18: ldrb r6, [r0, #1] */ 0x00, 0x20, 0xd0, 0xe5, /* 1c: ldrb r2, [r0] */ 0x06, 0x24, 0x82, 0xe1, /* 20: orr r2, r2, r6, lsl #8 */ 0x22, 0x64, 0xa0, 0xe1, /* 24: lsr r6, r2, #8 */ 0x02, 0x64, 0x86, 0xe1, /* 28: orr r6, r6, r2, lsl #8 */ 0x06, 0x68, 0xa0, 0xe1, /* 2c: lsl r6, r6, #16 */ 0x26, 0x68, 0xa0, 0xe1, /* 30: lsr r6, r6, #16 */ 0x00, 0x00, 0x56, 0xe3, /* 34: cmp r6, #0 */ 0x63, 0x00, 0x00, 0x0a, /* 38: beq 1cc */ 0x09, 0x00, 0x56, 0xe1, /* 3c: cmp r6, r9 */ 0x05, 0x20, 0xa0, 0x03, /* 40: moveq r2, #5 */ 0x0c, 0x20, 0xcd, 0x05, /* 44: strbeq r2, [sp, #12] */ 0x40, 0x20, 0x9d, 0xe5, /* 48: ldr r2, [sp, #64] */ 0x06, 0x50, 0xa0, 0x11, /* 4c: movne r5, r6 */ 0x02, 0x50, 0xa0, 0x03, /* 50: moveq r5, #2 */ 0x00, 0x50, 0x82, 0xe5, /* 54: str r5, [r2] */ 0x44, 0x20, 0x9d, 0xe5, /* 58: ldr r2, [sp, #68] */ 0x00, 0x70, 0xa0, 0x01, /* 5c: moveq r7, r0 */ 0x00, 0x50, 0x82, 0xe5, /* 60: str r5, [r2] */ 0x48, 0x20, 0x9d, 0xe5, /* 64: ldr r2, [sp, #72] */ 0x04, 0x00, 0x9d, 0x05, /* 68: ldreq r0, [sp, #4] */ 0x02, 0x00, 0x80, 0x12, /* 6c: addne r0, r0, #2 */ 0x00, 0x00, 0x52, 0xe3, /* 70: cmp r2, #0 */ 0x00, 0x50, 0x82, 0x15, /* 74: strne r5, [r2] */ 0x00, 0x20, 0x60, 0xe2, /* 78: rsb r2, r0, #0 */ 0x03, 0x20, 0x02, 0xe2, /* 7c: and r2, r2, #3 */ 0x3c, 0x40, 0x82, 0xe2, /* 80: add r4, r2, #60 */ 0x04, 0x00, 0x55, 0xe1, /* 84: cmp r5, r4 */ 0x05, 0x40, 0xa0, 0x31, /* 88: movcc r4, r5 */ 0x04, 0xe0, 0x80, 0xe0, /* 8c: add lr, r0, r4 */ 0x00, 0xc0, 0xa0, 0xe1, /* 90: mov ip, r0 */ 0x0e, 0x00, 0x5c, 0xe1, /* 94: cmp ip, lr */ 0x1b, 0x00, 0x00, 0x1a, /* 98: bne 10c */ 0x04, 0x40, 0x45, 0xe0, /* 9c: sub r4, r5, r4 */ 0x00, 0xa0, 0x0f, 0xe1, /* a0: mrs sl, CPSR */ 0xc0, 0xc0, 0x8a, 0xe3, /* a4: orr ip, sl, #192 */ 0x0c, 0xf0, 0x21, 0xe1, /* a8: msr CPSR_c, ip */ 0x00, 0xc0, 0x91, 0xe5, /* ac: ldr ip, [r1] */ 0x00, 0x80, 0x9d, 0xe5, /* b0: ldr r8, [sp] */ 0x02, 0x00, 0x55, 0xe1, /* b4: cmp r5, r2 */ 0x0c, 0xc0, 0x88, 0xe1, /* b8: orr ip, r8, ip */ 0x05, 0x20, 0xa0, 0x31, /* bc: movcc r2, r5 */ 0x00, 0xc0, 0x81, 0xe5, /* c0: str ip, [r1] */ 0x02, 0x80, 0x80, 0xe0, /* c4: add r8, r0, r2 */ 0x00, 0xc0, 0xa0, 0xe1, /* c8: mov ip, r0 */ 0x0c, 0x00, 0x58, 0xe1, /* cc: cmp r8, ip */ 0x11, 0x00, 0x00, 0x1a, /* d0: bne 11c */ 0x02, 0x20, 0x45, 0xe0, /* d4: sub r2, r5, r2 */ 0x03, 0x00, 0x52, 0xe3, /* d8: cmp r2, #3 */ 0x14, 0x00, 0x00, 0x8a, /* dc: bhi 134 */ 0x00, 0x00, 0x52, 0xe3, /* e0: cmp r2, #0 */ 0x25, 0x00, 0x00, 0x1a, /* e4: bne 180 */ 0x0a, 0xf0, 0x21, 0xe1, /* e8: msr CPSR_c, sl */ 0x09, 0x00, 0x56, 0xe1, /* ec: cmp r6, r9 */ 0x05, 0x00, 0x80, 0x10, /* f0: addne r0, r0, r5 */ 0xc7, 0xff, 0xff, 0x1a, /* f4: bne 18 */ 0x0d, 0x20, 0xdd, 0xe5, /* f8: ldrb r2, [sp, #13] */ 0x01, 0x00, 0x12, 0xe3, /* fc: tst r2, #1 */ 0x02, 0x00, 0x87, 0x02, /* 100: addeq r0, r7, #2 */ 0x07, 0x00, 0xa0, 0x11, /* 104: movne r0, r7 */ 0xc2, 0xff, 0xff, 0xea, /* 108: b 18 */ 0x38, 0xa0, 0x9d, 0xe5, /* 10c: ldr sl, [sp, #56] */ 0x01, 0x80, 0xdc, 0xe4, /* 110: ldrb r8, [ip], #1 */ 0x00, 0x80, 0xca, 0xe5, /* 114: strb r8, [sl] */ 0xdd, 0xff, 0xff, 0xea, /* 118: b 94 */ 0x00, 0xb0, 0x93, 0xe5, /* 11c: ldr fp, [r3] */ 0x7f, 0x00, 0x1b, 0xe3, /* 120: tst fp, #127 */ 0x3c, 0xb0, 0x9d, 0x15, /* 124: ldrne fp, [sp, #60] */ 0x00, 0xb0, 0xdb, 0x15, /* 128: ldrbne fp, [fp] */ 0x01, 0xb0, 0xcc, 0x14, /* 12c: strbne fp, [ip], #1 */ 0xe5, 0xff, 0xff, 0xea, /* 130: b cc */ 0x00, 0xb0, 0x93, 0xe5, /* 134: ldr fp, [r3] */ 0x7c, 0x00, 0x1b, 0xe3, /* 138: tst fp, #124 */ 0x2b, 0x88, 0xa0, 0xe1, /* 13c: lsr r8, fp, #16 */ 0x3c, 0xb0, 0x9d, 0x15, /* 140: ldrne fp, [sp, #60] */ 0x7f, 0x80, 0x08, 0xe2, /* 144: and r8, r8, #127 */ 0x00, 0xb0, 0x9b, 0x15, /* 148: ldrne fp, [fp] */ 0x04, 0xb0, 0x8c, 0x14, /* 14c: strne fp, [ip], #4 */ 0x04, 0x20, 0x42, 0x12, /* 150: subne r2, r2, #4 */ 0x3b, 0x00, 0x58, 0xe3, /* 154: cmp r8, #59 */ 0x00, 0x80, 0xa0, 0xc3, /* 158: movgt r8, #0 */ 0x01, 0x80, 0xa0, 0xd3, /* 15c: movle r8, #1 */ 0x03, 0x00, 0x54, 0xe3, /* 160: cmp r4, #3 */ 0x00, 0x80, 0xa0, 0x93, /* 164: movls r8, #0 */ 0x00, 0x00, 0x58, 0xe3, /* 168: cmp r8, #0 */ 0x38, 0xb0, 0x9d, 0x15, /* 16c: ldrne fp, [sp, #56] */ 0x04, 0x80, 0x9e, 0x14, /* 170: ldrne r8, [lr], #4 */ 0x04, 0x40, 0x44, 0x12, /* 174: subne r4, r4, #4 */ 0x00, 0x80, 0x8b, 0x15, /* 178: strne r8, [fp] */ 0xd5, 0xff, 0xff, 0xea, /* 17c: b d8 */ 0x00, 0xb0, 0x93, 0xe5, /* 180: ldr fp, [r3] */ 0x7f, 0x00, 0x1b, 0xe3, /* 184: tst fp, #127 */ 0x2b, 0x88, 0xa0, 0xe1, /* 188: lsr r8, fp, #16 */ 0x3c, 0xb0, 0x9d, 0x15, /* 18c: ldrne fp, [sp, #60] */ 0x7f, 0x80, 0x08, 0xe2, /* 190: and r8, r8, #127 */ 0x00, 0xb0, 0xdb, 0x15, /* 194: ldrbne fp, [fp] */ 0x01, 0xb0, 0xcc, 0x14, /* 198: strbne fp, [ip], #1 */ 0x01, 0x20, 0x42, 0x12, /* 19c: subne r2, r2, #1 */ 0x3b, 0x00, 0x58, 0xe3, /* 1a0: cmp r8, #59 */ 0x00, 0x80, 0xa0, 0xc3, /* 1a4: movgt r8, #0 */ 0x01, 0x80, 0xa0, 0xd3, /* 1a8: movle r8, #1 */ 0x00, 0x00, 0x54, 0xe3, /* 1ac: cmp r4, #0 */ 0x00, 0x80, 0xa0, 0x03, /* 1b0: moveq r8, #0 */ 0x00, 0x00, 0x58, 0xe3, /* 1b4: cmp r8, #0 */ 0x38, 0xb0, 0x9d, 0x15, /* 1b8: ldrne fp, [sp, #56] */ 0x01, 0x80, 0xde, 0x14, /* 1bc: ldrbne r8, [lr], #1 */ 0x01, 0x40, 0x44, 0x12, /* 1c0: subne r4, r4, #1 */ 0x00, 0x80, 0xcb, 0x15, /* 1c4: strbne r8, [fp] */ 0xc4, 0xff, 0xff, 0xea, /* 1c8: b e0 */ 0x14, 0xd0, 0x8d, 0xe2, /* 1cc: add sp, sp, #20 */ 0xf0, 0x8f, 0xbd, 0xe8, /* 1d0: pop {r4, r5, r6, r7, r8, r9, sl, fp, pc} */ 0xff, 0xff, 0x00, 0x00, /* 1d4: .word 0x0000ffff */ }; 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+git20240825.4390ca/fel-sdboot.S000066400000000000000000000042611476263345700205400ustar00rootroot00000000000000/* * 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+git20240825.4390ca/fel-sdboot.lds000066400000000000000000000016551476263345700211240ustar00rootroot00000000000000/* * 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+git20240825.4390ca/fel-spiflash.c000066400000000000000000000432421476263345700211010ustar00rootroot00000000000000/* * (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" }, { .id = 0x1C70, .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 = "Eon EN25QHxx" }, }; 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 SUNIV_PLL6_CTL (0x01c20000 + 0x28) #define SUNIV_AHB_APB_CFG (0x01c20000 + 0x54) #define H6_CCM_SPI0_CLK (0x03001000 + 0x940) #define H6_CCM_SPI_BGR (0x03001000 + 0x96C) #define H6_CCM_SPI0_GATE_RESET (1 << 0 | 1 << 16) #define SUNIV_GPC_SPI0 (2) #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 (1U << 31) #define SUN4I_SPI0_CCTL (spi_base(dev) + 0x1C) #define SUN4I_SPI0_CTL (spi_base(dev) + 0x08) #define SUN4I_SPI0_RX (spi_base(dev) + 0x00) #define SUN4I_SPI0_TX (spi_base(dev) + 0x04) #define SUN4I_SPI0_FIFO_STA (spi_base(dev) + 0x28) #define SUN4I_SPI0_BC (spi_base(dev) + 0x20) #define SUN4I_SPI0_TC (spi_base(dev) + 0x24) #define SUN6I_SPI0_CCTL (spi_base(dev) + 0x24) #define SUN6I_SPI0_GCR (spi_base(dev) + 0x04) #define SUN6I_SPI0_TCR (spi_base(dev) + 0x08) #define SUN6I_SPI0_FIFO_STA (spi_base(dev) + 0x1C) #define SUN6I_SPI0_MBC (spi_base(dev) + 0x30) #define SUN6I_SPI0_MTC (spi_base(dev) + 0x34) #define SUN6I_SPI0_BCC (spi_base(dev) + 0x38) #define SUN6I_SPI0_TXD (spi_base(dev) + 0x200) #define SUN6I_SPI0_RXD (spi_base(dev) + 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) #define CCM_SPI0_CLK_DIV_BY_32 (0x100f) static uint32_t gpio_base(feldev_handle *dev) { soc_info_t *soc_info = dev->soc_info; switch (soc_info->soc_id) { case 0x1816: /* V536 */ case 0x1817: /* V831 */ case 0x1728: /* H6 */ case 0x1823: /* H616 */ return 0x0300B000; default: return 0x01C20800; } } static uint32_t spi_base(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 */ case 0x1663: /* F1C100s */ case 0x1701: /* R40 */ return 0x01C05000; case 0x1816: /* V536 */ case 0x1817: /* V831 */ case 0x1728: /* H6 */ case 0x1823: /* H616 */ return 0x05010000; default: return 0x01C68000; } } /* * 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 = gpio_base(dev) + 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; } } static bool soc_is_h6_style(feldev_handle *dev) { soc_info_t *soc_info = dev->soc_info; switch (soc_info->soc_id) { case 0x1816: /* V536 */ case 0x1817: /* V831 */ case 0x1728: /* H6 */ case 0x1823: /* H616 */ return true; default: return false; } } /* * 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) { printf("Unable to fetch device information. " "Possibly unknown device.\n"); return false; } /* Setup SPI0 pins muxing */ switch (soc_info->soc_id) { case 0x1663: /* Allwinner F1C100s/F1C600/R6/F1C100A/F1C500 */ gpio_set_cfgpin(dev, PC, 0, SUNIV_GPC_SPI0); gpio_set_cfgpin(dev, PC, 1, SUNIV_GPC_SPI0); gpio_set_cfgpin(dev, PC, 2, SUNIV_GPC_SPI0); gpio_set_cfgpin(dev, PC, 3, SUNIV_GPC_SPI0); break; case 0x1625: /* Allwinner A13 */ case 0x1680: /* Allwinner H3 */ case 0x1681: /* Allwinner V3s */ 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 0x1623: /* Allwinner A10 */ case 0x1651: /* Allwinner A20 */ case 0x1701: /* Allwinner R40 */ 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, 23, 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; case 0x1816: /* Allwinner V536 */ case 0x1817: /* Allwinner V831 */ gpio_set_cfgpin(dev, PC, 1, SUN50I_GPC_SPI0); /* SPI0-CS */ /* fall-through */ case 0x1728: /* Allwinner H6 */ gpio_set_cfgpin(dev, PC, 0, SUN50I_GPC_SPI0); gpio_set_cfgpin(dev, PC, 2, SUN50I_GPC_SPI0); gpio_set_cfgpin(dev, PC, 3, SUN50I_GPC_SPI0); /* PC5 is SPI0-CS on the H6, and SPI0-HOLD on the V831 */ gpio_set_cfgpin(dev, PC, 5, SUN50I_GPC_SPI0); break; case 0x1823: /* Allwinner H616 */ gpio_set_cfgpin(dev, PC, 0, SUN50I_GPC_SPI0); /* SPI0_CLK */ gpio_set_cfgpin(dev, PC, 2, SUN50I_GPC_SPI0); /* SPI0_MOSI */ gpio_set_cfgpin(dev, PC, 3, SUN50I_GPC_SPI0); /* SPI0_CS0 */ gpio_set_cfgpin(dev, PC, 4, SUN50I_GPC_SPI0); /* SPI0_MISO */ break; default: /* Unknown/Unsupported SoC */ printf("SPI support not implemented yet for %x (%s)!\n", soc_info->soc_id, soc_info->name); return false; } if (soc_is_h6_style(dev)) { reg_val = readl(H6_CCM_SPI_BGR); reg_val |= H6_CCM_SPI0_GATE_RESET; writel(reg_val, H6_CCM_SPI_BGR); } else { 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); } reg_val = readl(CCM_AHB_GATING0); reg_val |= CCM_AHB_GATE_SPI0; writel(reg_val, CCM_AHB_GATING0); } if (soc_info->soc_id == 0x1663) { /* suniv F1C100s */ /* * suniv doesn't have a module clock for SPI0 and the clock * source is always the AHB clock. Setup AHB to 200 MHz by * setting PLL6 to 600 MHz with a divider of 3, then program * the internal SPI dividier to 32. */ /* Set PLL6 to 600MHz */ writel(0x80041801, SUNIV_PLL6_CTL); /* PLL6:AHB:APB = 6:2:1 */ writel(0x00003180, SUNIV_AHB_APB_CFG); /* divide by 32 */ writel(CCM_SPI0_CLK_DIV_BY_32, SUN6I_SPI0_CCTL); } else { /* divide 24MHz OSC by 4 */ writel(CCM_SPI0_CLK_DIV_BY_4, spi_is_sun6i(dev) ? SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL); /* Choose 24MHz from OSC24M and enable clock */ writel(1U << 31, soc_is_h6_style(dev) ? H6_CCM_SPI0_CLK : CCM_SPI0_CLK); } if (spi_is_sun6i(dev)) { /* Enable SPI in the master mode and do a soft reset */ reg_val = readl(SUN6I_SPI0_GCR); reg_val |= (1U << 31) | 3; writel(reg_val, SUN6I_SPI0_GCR); /* Wait for completion */ while (readl(SUN6I_SPI0_GCR) & (1U << 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; case 0x1C: manufacturer = "Eon"; break; default: manufacturer = "Unknown"; break; } printf("Manufacturer: %s (%02Xh), model: %02Xh, size: %d bytes.\n", manufacturer, buf[3], buf[4], (1U << 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+git20240825.4390ca/fel-spiflash.h000066400000000000000000000023041476263345700211000ustar00rootroot00000000000000/* * (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+git20240825.4390ca/fel.c000066400000000000000000001455701476263345700173010ustar00rootroot00000000000000/* * 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 "fit_image.h" #include #include #include #include #include #include #include #include #include #include bool verbose = false; /* If set, makes the 'fel' tool more talkative */ static uint32_t uboot_entry = 0; /* entry point (address) of U-Boot */ static uint32_t uboot_size = 0; /* size of U-Boot binary */ static bool enter_in_aarch64 = false; /* 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_TYPE_FLATDT 8 /* DTB or FIT image */ #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) == 0xd00dfeed) return IH_TYPE_FLATDT; 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_read_sid(dev, key, 0, sizeof(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_fel_dump_sid(feldev_handle *dev) { uint32_t buffer[2048 / sizeof(uint32_t)]; /* total SID size is 2K */ soc_info_t *soc_info = dev->soc_info; if (!soc_info->sid_base || !soc_info->sid_sections) { printf("SID memory maps for your SoC (%s) are unknown.\n", dev->soc_name); return; } for (const sid_section *s = soc_info->sid_sections; s->name; s++) { uint32_t count = s->size_bits / 32; if (fel_read_sid(dev, buffer, s->offset, count * 4, false)) { fprintf(stderr, "Read sid:%s failed\n", s->name); return; } printf("%-15s", s->name); for (uint32_t i = 0; i < count; i++) { if (i > 0 && ((i % 8) == 0)) printf("\n%-15s", ""); printf(" %08x", buffer[i]); } putchar('\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. * Bits [23:22] are Read-As-One on ARMv7, but Should-Be-Zero * on ARMv5, so ignore them. * We need the RES1 bits[18,16,4,3] and CP15BEN[5]. */ sctlr = aw_get_sctlr(dev, soc_info); if ((sctlr & ~((0x3 << 22) | (0x7 << 11) | (1 << 6) | 1)) != 0x00050038) 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); } /* Minimum offset of the main U-Boot image within u-boot-sunxi-with-spl.bin. */ #define SPL_MIN_OFFSET 0x8000 uint32_t 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; 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(); } spl_len_limit = soc_info->sram_size; 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 - soc_info->spl_addr < spl_len_limit) spl_len_limit = soc_info->thunk_addr - soc_info->spl_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); return spl_len; } /* * 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. */ static void aw_fel_write_uboot_image(feldev_handle *dev, uint8_t *buf, size_t len, const char *dt_name) { if (len <= HEADER_SIZE) return; /* Insufficient size (no actual data), just bail out */ image_header_t hdr = *(image_header_t *)buf; /* Check for a valid mkimage header */ int image_type = get_image_type(buf, len); if (image_type <= IH_TYPE_INVALID) { switch (image_type) { case IH_TYPE_INVALID: 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_FLATDT) { /* FIT image */ uboot_entry = load_fit_images(dev, buf, dt_name, &enter_in_aarch64); uboot_size = 4; /* dummy value to pass check below */ return; } if (image_type != IH_TYPE_FIRMWARE) pr_fatal("U-Boot image type mismatch: " "expected IH_TYPE_FIRMWARE, got %02X\n", image_type); /* The CRC is calculated on the whole header but the CRC itself */ uint32_t hcrc = be32toh(hdr.ih_hcrc); 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); 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; } static const char *spl_get_dtb_name(uint8_t *spl_buf) { uint32_t dt_offset; if (memcmp(spl_buf + 4, "eGON.BT0", 8)) return NULL; if (memcmp(spl_buf + 0x14, "SPL", 3)) return NULL; if (spl_buf[0x17] < 0x2) /* only since v0.2 */ return NULL; memcpy(&dt_offset, spl_buf + 0x20, 4); dt_offset = le32toh(dt_offset); if (verbose) printf("found DT name in SPL header: %s\n", spl_buf + dt_offset); return (char *)spl_buf + dt_offset; } /* * 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) { size_t size; uint32_t offset; /* load file into memory buffer */ uint8_t *buf = load_file(filename, &size); const char *dt_name = spl_get_dtb_name(buf); /* write and execute the SPL from the buffer */ offset = aw_fel_write_and_execute_spl(dev, buf, size); /* check for optional main U-Boot binary (and transfer it, if applicable) */ if (size > offset) { /* U-Boot pads to at least 32KB */ if (offset < SPL_MIN_OFFSET) offset = SPL_MIN_OFFSET; aw_fel_write_uboot_image(dev, buf + offset, size - offset, dt_name); } 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_MAJOR_BITS 3 #define SPL_MINOR_BITS 5 #define SPL_VERSION(maj, min) \ ((((maj) & ((1U << SPL_MAJOR_BITS) - 1)) << SPL_MINOR_BITS) | \ ((min) & ((1U << SPL_MINOR_BITS) - 1))) #define SPL_MIN_VERSION SPL_VERSION(0, 1) #define SPL_MAX_VERSION SPL_VERSION(0, 31) 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; } /* The H616 has two die variants with different RVBAR locations. */ uint32_t rvbar_reg = soc_info->rvbar_reg; if (soc_info->rvbar_reg_alt) { uint32_t ver_reg = fel_readl(dev, soc_info->ver_reg); if (ver_reg & 0xff) rvbar_reg = soc_info->rvbar_reg_alt; } 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(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, rvbar_reg, rmr_mode); aw_fel_execute(dev, soc_info->scratch_addr); pr_info(" done.\n"); } /* Use the watchdog to simply reboot. Useful to get out of fel without * power cycling or plugging. */ void aw_wd_reset(feldev_handle *dev) { const watchdog_info *wd = dev->soc_info->watchdog; if (!wd) { pr_error("No watchdog information available (yet) for soc: %s\n", dev->soc_info->name); return; } fel_writel(dev, wd->reg_mode, wd->reg_mode_value); pr_info("Requested watchdog reset\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" " --list-socs Print a list of all supported SoCs\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" " wdreset Reboot via watchdog\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" " sid-registers Retrieve and output 128-bit SID key,\n" " using the MMIO register read method\n" " sid-dump Dump the content of all the SID eFuses\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 */ bool socs_list = false; /* list all supported SoCs and exit */ 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 (strcmp(argv[1], "--list-socs") == 0 || strcmp(argv[1], "list-socs") == 0) socs_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 (socs_list) { const soc_info_t *soc_info = NULL; printf("SoCID name\n"); while ((soc_info = get_next_soc(soc_info)) != NULL) printf("%04x: %s\n", soc_info->soc_id, soc_info->name); return 0; } 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 (strcmp(argv[1], "wdreset") == 0) { aw_wd_reset(handle); } 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], "sid-dump") == 0) { aw_fel_dump_sid(handle); } else if (strcmp(argv[1], "write") == 0 && argc > 3) { skip += 2 * file_upload(handle, 1, argc - 2, argv + 2, pflag_active ? progress_bar : NULL); } else if (strcmp(argv[1], "write-with-progress") == 0 && argc > 3) { skip += 2 * file_upload(handle, 1, argc - 2, argv + 2, progress_bar); } else if (strcmp(argv[1], "write-with-gauge") == 0 && argc > 3) { skip += 2 * file_upload(handle, 1, argc - 2, argv + 2, progress_gauge); } else if (strcmp(argv[1], "write-with-xgauge") == 0 && argc > 3) { skip += 2 * file_upload(handle, 1, argc - 2, argv + 2, progress_gauge_xxx); } else if ((strcmp(argv[1], "multiwrite") == 0 || strcmp(argv[1], "multi") == 0) && argc > 4) { size_t count = strtoul(argv[2], NULL, 0); /* file count */ skip = 2 + 2 * file_upload(handle, count, argc - 3, argv + 3, progress_bar); } else if ((strcmp(argv[1], "multiwrite-with-gauge") == 0 || strcmp(argv[1], "multi-with-gauge") == 0) && argc > 4) { size_t count = strtoul(argv[2], NULL, 0); /* file count */ skip = 2 + 2 * file_upload(handle, count, argc - 3, argv + 3, progress_gauge); } else if ((strcmp(argv[1], "multiwrite-with-xgauge") == 0 || strcmp(argv[1], "multi-with-xgauge") == 0) && argc > 4) { size_t count = strtoul(argv[2], NULL, 0); /* file count */ skip = 2 + 2 * file_upload(handle, count, argc - 3, argv + 3, progress_gauge_xxx); } else if ((strcmp(argv[1], "echo-gauge") == 0) && argc > 2) { skip = 2; printf("XXX\n0\n%s\nXXX\n", argv[2]); fflush(stdout); } else if (strcmp(argv[1], "read") == 0 && argc > 4) { size_t size = strtoul(argv[3], NULL, 0); void *buf = malloc(size); aw_fel_read(handle, strtoul(argv[2], NULL, 0), buf, size); save_file(argv[4], buf, size); free(buf); skip=4; } else if (strcmp(argv[1], "clear") == 0 && argc > 2) { aw_fel_fill(handle, strtoul(argv[2], NULL, 0), strtoul(argv[3], NULL, 0), 0); skip=3; } else if (strcmp(argv[1], "fill") == 0 && argc > 3) { aw_fel_fill(handle, strtoul(argv[2], NULL, 0), strtoul(argv[3], NULL, 0), (unsigned char)strtoul(argv[4], NULL, 0)); skip=4; } else if (strcmp(argv[1], "spl") == 0 && argc > 2) { aw_fel_process_spl_and_uboot(handle, argv[2]); skip=2; } else if (strcmp(argv[1], "uboot") == 0 && argc > 2) { aw_fel_process_spl_and_uboot(handle, argv[2]); uboot_autostart = (uboot_entry > 0 && uboot_size > 0); if (!uboot_autostart) printf("Warning: \"uboot\" command failed to detect image! Can't execute U-Boot.\n"); skip=2; } else 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); if (enter_in_aarch64) aw_rmr_request(handle, uboot_entry, true); else aw_fel_execute(handle, uboot_entry); } feldev_done(handle); return 0; } sunxi-tools-1.4.2+git20240825.4390ca/fel_lib.c000066400000000000000000000732001476263345700201150ustar00rootroot00000000000000/* * 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; bool icache_hacked; }; /* a helper function to report libusb errors */ static 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 */ static 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 */ } } static 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, void *data, size_t len) { aw_send_usb_request(dev, AW_USB_READ, len); usb_bulk_recv(dev->usb->handle, dev->usb->endpoint_in, data, len); aw_read_usb_response(dev); } static 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); } static 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 = le32toh(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 */ static void aw_fel_write_raw(feldev_handle *dev, const void *buf, uint32_t offset, size_t len) { if (len == 0) return; 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); } static void aw_disable_icache(feldev_handle *dev) { soc_info_t *soc_info = dev->soc_info; uint32_t arm_code[] = { /* Clear SCTLR.I */ htole32(0xee110f10), /* mrc 15, 0, r0, cr1, cr0, {0} ;SCTLR */ htole32(0xe3c00a01), /* bic r0, r0, #0x1000 */ htole32(0xee010f10), /* mcr 15, 0, r0, cr1, cr0, {0} ;SCTLR */ /* Invalidate I-Cache */ htole32(0xee070f15), /* mcr 15, 0, r0, cr7, cr5, {0} ;ICIALLU */ /* Barrier to force instruction refetching */ htole32(0xf57ff06f), /* isb sy */ htole32(0xe12fff1e), /* bx lr */ }; aw_fel_write_raw(dev, arm_code, soc_info->scratch_addr, sizeof(arm_code)); aw_fel_execute(dev, soc_info->scratch_addr); } void aw_fel_write(feldev_handle *dev, const void *buf, uint32_t offset, size_t len) { if (dev->soc_info->icache_fix && !dev->usb->icache_hacked) { aw_disable_icache(dev); dev->usb->icache_hacked = true; } aw_fel_write_raw(dev, buf, offset, len); } /* * 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, const void *buf, uint32_t offset, size_t len, bool progress) { if (len == 0) return; 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 offset, uint32_t length) { uint32_t arm_code[] = { /* : */ htole32(0xe59f0044), /* 0: ldr r0, [pc, #68] */ htole32(0xe59f1044), /* 4: ldr r1, [pc, #68] */ htole32(0xe28f3048), /* 8: add r3, pc, #72 */ /* : */ 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(0xe4832004), /* 2c: str r2, [r3], #4 */ htole32(0xe2811004), /* 30: add r1, r1, #4 */ htole32(0xe59f2018), /* 34: ldr r2, [pc, #24] */ htole32(0xe1510002), /* 38: cmp r1, r2 */ htole32(0x3afffff2), /* 3c: bcc c */ htole32(0xe3a02000), /* 40: mov r2, #0 */ htole32(0xe5802040), /* 44: str r2, [r0, #64] */ htole32(0xe12fff1e), /* 48: bx lr */ /* : */ htole32(dev->soc_info->sid_base), /* SID base addr */ /* : */ htole32(offset), /* first word to read */ /* : */ htole32(offset + length), /* where to stop to read */ /* 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, length); for (unsigned i = 0; i < length / 4; i++) result[i] = le32toh(result[i]); } /** * fel_read_sid() - Read the content of the SID eFuses. * @dev: device handle for the FEL device * @result: pointer of a buffer receiving the content of the eFuses * @offset: beginning of the eFuses area to read, in bytes * @length: length of the eFuses area to read, in bytes * @force_workaround: whether to use the MMIO register based read method * * Read the contents of the non-volatile eFuses stored in the SoC. The size * and supposed usage layout differs between SoCs, but the "root" key * (containing some unique serial number) is always in the first 128 bits. * * Return: 0 if the operation was successful, a negative error code otherwise. */ int fel_read_sid(feldev_handle *dev, uint32_t *result, unsigned int offset, unsigned int length, bool force_workaround) { const soc_info_t *soc = dev->soc_info; if (!soc->sid_base) /* SID unavailable */ return -2; if ((offset & 3) || (length & 3)) /* needs to be 32-bit aligned */ return -3; if (soc->sid_fix || force_workaround) /* Work around SID issues by using ARM thunk code */ fel_get_sid_registers(dev, result, offset, length); else /* Read SID directly from memory */ fel_readl_n(dev, soc->sid_base + soc->sid_offset + offset, result, length / 4); return 0; } /* 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 */ static 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 */ static 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_read_sid(dev, entry->SID, 0, 16, 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+git20240825.4390ca/fel_lib.h000066400000000000000000000061341476263345700201240ustar00rootroot00000000000000/* * 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, const void *buf, uint32_t offset, size_t len); void aw_fel_write_buffer(feldev_handle *dev, const 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) int fel_read_sid(feldev_handle *dev, uint32_t *result, unsigned int offset, unsigned int length, 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+git20240825.4390ca/fexc.c000066400000000000000000000170061476263345700174500ustar00rootroot00000000000000/* * 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+git20240825.4390ca/fexc.h000066400000000000000000000016431476263345700174550ustar00rootroot00000000000000/* * 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+git20240825.4390ca/find-arm-gcc.sh000077500000000000000000000006161476263345700211440ustar00rootroot00000000000000# # 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+git20240825.4390ca/fit_image.c000066400000000000000000000166071476263345700204550ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Andre Przywara * * 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; under version 2 of the License. * * 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 "common.h" #include "fel_lib.h" #include "fit_image.h" /* defined in fel.c */ extern bool verbose; #define IH_ARCH_INVALID 0 #define IH_ARCH_ARM 2 #define IH_ARCH_ARM64 22 #define IH_OS_INVALID 0 #define IH_OS_LINUX 5 #define IH_OS_U_BOOT 17 #define IH_OS_ARM_TRUSTED_FIRMWARE 25 #define IH_OS_EFI 28 struct fit_image_info { const char *description; const char *data; uint32_t data_size; uint32_t load_addr; uint32_t entry_point; uint8_t os; uint8_t arch; }; static int fit_parse_os(const char *value) { if (!value || !*value) return IH_OS_INVALID; if (!strcmp(value, "u-boot")) return IH_OS_U_BOOT; if (!strcmp(value, "linux")) return IH_OS_LINUX; if (!strcmp(value, "arm-trusted-firmware")) return IH_OS_ARM_TRUSTED_FIRMWARE; if (!strcmp(value, "efi")) return IH_OS_EFI; return IH_OS_INVALID; } static int fit_parse_arch(const char *value) { if (!value || !*value) return IH_ARCH_INVALID; if (!strcmp(value, "arm")) return IH_ARCH_ARM; if (!strcmp(value, "arm64")) return IH_ARCH_ARM64; return IH_ARCH_INVALID; } static uint32_t fdt_getprop_u32(const void *fdt, int node, const char *name) { const fdt32_t *val; val = fdt_getprop(fdt, node, name, NULL); if (!val) return ~0U; return fdt32_to_cpu(*val); } static const char *fdt_getprop_str(const void *fdt, int node, const char *name) { const struct fdt_property *prop; prop = fdt_get_property(fdt, node, name, NULL); if (!prop) return NULL; return prop->data; } /* * Find the image with the given name under the /images node, and parse * its information into the fit_image_info struct. * Returns 0 on success, and a negative error value otherwise. */ static int fit_get_image_info(const void *fit, const char *name, struct fit_image_info *info) { int node; const char *str; uint32_t data_offset; node = fdt_path_offset(fit, "/images"); if (node < 0) return -1; node = fdt_subnode_offset(fit, node, name); if (node < 0) return -1; info->load_addr = fdt_getprop_u32(fit, node, "load"); info->entry_point = fdt_getprop_u32(fit, node, "entry"); info->description = fdt_getprop_str(fit, node, "description"); /* properties used for FIT images with external data */ info->data_size = fdt_getprop_u32(fit, node, "data-size"); data_offset = fdt_getprop_u32(fit, node, "data-offset"); /* check for embedded data (when invalid external data properties) */ if (info->data_size == ~0U || data_offset == ~0U) { const struct fdt_property *prop; int len; prop = fdt_get_property(fit, node, "data", &len); info->data_size = len; info->data = prop->data; } else { /* external data is appended at the end of the FIT DTB blob */ info->data = (const char *)fit + ((fdt_totalsize(fit) + 3) & ~3U); info->data += data_offset; } info->os = fit_parse_os(fdt_getprop_str(fit, node, "os")); info->arch = fit_parse_arch(fdt_getprop_str(fit, node, "arch")); str = fdt_getprop_str(fit, node, "compression"); /* The current SPL does not support compression either. */ if (str && strcmp(str, "none")) { printf("compression \"%s\" not supported for image \"%s\"\n", str, info->description); return -2; } return 0; } static int entry_arch; static uint32_t dtb_addr; /* * Upload the image described by its fit_image_info struct to the board. * Detect if an image contains an entry point and return that. * Set entry_arch to arm or arm64 on the way. Also detect the image * containing U-Boot and record its end address, so that the DTB can be * appended later on. * Returns the entry point if any is specified, or 0 otherwise. */ static uint32_t fit_load_image(feldev_handle *dev, struct fit_image_info *img) { uint32_t ret = 0; if (verbose) printf("loading image \"%s\" (%d bytes) to 0x%x\n", img->description, img->data_size, img->load_addr); aw_fel_write_buffer(dev, img->data, img->load_addr, img->data_size, true); if (img->entry_point != ~0U) { ret = img->entry_point; entry_arch = img->arch; } /* either explicitly marked as U-Boot, or the first invalid one */ if (img->os == IH_OS_U_BOOT || (!dtb_addr && img->os == IH_OS_INVALID)) dtb_addr = img->load_addr + img->data_size; return ret; } uint32_t load_fit_images(feldev_handle *dev, const void *fit, const char *dt_name, bool *use_aarch64) { const struct fdt_property *prop; struct fit_image_info img; const char *str; int node, len; uint32_t entry_point = 0; node = fdt_path_offset(fit, "/configurations"); if (node < 0) { pr_error("invalid FIT image, no /configurations node\n"); return 0; } /* * Find the right configuration node, either by using the provided * DT name as an identifier, falling back to the node titled "default", * or by using just the first node. */ if (dt_name) { for (node = fdt_first_subnode(fit, node); node >= 0; node = fdt_next_subnode(fit, node)) { prop = fdt_get_property(fit, node, "description", NULL); if (prop && !strcmp(prop->data, dt_name)) break; } if (node < 0) { pr_error("no matching FIT configuration node for \"%s\"\n", dt_name); return 0; } } else { prop = fdt_get_property(fit, node, "default", NULL); if (!prop) node = fdt_first_subnode(fit, node); else node = fdt_subnode_offset(fit, node, prop->data); if (node < 0) { pr_error("no default FIT configuration node\n"); return 0; } } entry_arch = IH_ARCH_INVALID; dtb_addr = 0; /* Load the image described as "firmware". */ str = fdt_getprop_str(fit, node, "firmware"); if (str && !fit_get_image_info(fit, str, &img)) { uint32_t addr = fit_load_image(dev, &img); if (addr != 0) entry_point = addr; } else { printf("WARNING: no valid \"firmware\" image entry in FIT\n"); } /* load all loadables, at their respective load addresses */ prop = fdt_get_property(fit, node, "loadables", &len); for (str = prop ? prop->data : NULL; prop && (str - prop->data) < len && *str; str += strlen(str) + 1) { uint32_t addr; if (fit_get_image_info(fit, str, &img)) { printf("Can't load loadable \"%s\", skipping.\n", str); continue; } addr = fit_load_image(dev, &img); if (addr != 0) entry_point = addr; } if (use_aarch64) *use_aarch64 = (entry_arch == IH_ARCH_ARM64); if (!dtb_addr) { printf("Warning: no U-Boot image found, not loading DTB\n"); return entry_point; } /* load .dtb right after the U-Boot image (appended DTB) */ str = fdt_getprop_str(fit, node, "fdt"); if (!str || fit_get_image_info(fit, str, &img)) { printf("Warning: no FDT found in FIT image\n"); return entry_point; } if (verbose) printf("loading DTB \"%s\" (%d bytes)\n", img.description, img.data_size); aw_fel_write_buffer(dev, img.data, dtb_addr, img.data_size, false); return entry_point; } sunxi-tools-1.4.2+git20240825.4390ca/fit_image.h000066400000000000000000000021651476263345700204540ustar00rootroot00000000000000/* * Copyright (C) 2018-2020 Andre Przywara * * 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; under version 2 of the License. * * 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 __FIT_IMAGE_H__ #define __FIT_IMAGE_H__ #include #include "fel_lib.h" /* * Load all images referenced in the given U-Boot FIT image. @dt_name will * be used to select one of the configurations. @use_aarch64 contains the * target architecture of the entry point. * Returns the entry point address of the image to be started. */ uint32_t load_fit_images(feldev_handle *dev, const void *fit, const char *dt_name, bool *use_aarch64); #endif sunxi-tools-1.4.2+git20240825.4390ca/include/000077500000000000000000000000001476263345700177765ustar00rootroot00000000000000sunxi-tools-1.4.2+git20240825.4390ca/include/list.h000066400000000000000000000042241476263345700211240ustar00rootroot00000000000000/* * 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+git20240825.4390ca/include/portable_endian.h000066400000000000000000000061421476263345700233000ustar00rootroot00000000000000// "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+git20240825.4390ca/include/types.h000066400000000000000000000023541476263345700213170ustar00rootroot00000000000000/* * (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+git20240825.4390ca/jtag-loop.S000066400000000000000000000024601476263345700203750ustar00rootroot00000000000000/* * (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+git20240825.4390ca/jtag-loop.c000066400000000000000000000024731476263345700204210ustar00rootroot00000000000000/* * (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+git20240825.4390ca/jtag-loop.lds000066400000000000000000000016551476263345700207620ustar00rootroot00000000000000/* * 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+git20240825.4390ca/meminfo.c000066400000000000000000000461411476263345700201570ustar00rootroot00000000000000/* * 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 "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 *(volatile unsigned int*) (base + offset); } void sunxi_io_write(void *base, int offset, unsigned int value) { *(volatile unsigned int*) (base + offset) = value; } void sunxi_io_mask(void *base, int offset, unsigned int value, unsigned int mask) { unsigned int tmp = sunxi_io_read(base, offset); tmp &= ~mask; tmp |= value & mask; sunxi_io_write(base, offset, tmp); } /* * 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+git20240825.4390ca/nand-common.h000066400000000000000000000021671476263345700207400ustar00rootroot00000000000000/* * (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+git20240825.4390ca/nand-image-builder.c000066400000000000000000000703521476263345700221520ustar00rootroot00000000000000/* * 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+git20240825.4390ca/nand-part-a10.h000066400000000000000000000051341476263345700207720ustar00rootroot00000000000000/* * 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+git20240825.4390ca/nand-part-a20.h000066400000000000000000000064351476263345700210000ustar00rootroot00000000000000/* * 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+git20240825.4390ca/nand-part-main.c000066400000000000000000000056121476263345700213310ustar00rootroot00000000000000/* * (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+git20240825.4390ca/nand-part.c000066400000000000000000000177371476263345700204220ustar00rootroot00000000000000/* * 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) { memcpy(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+git20240825.4390ca/phoenix_info.c000066400000000000000000000106401476263345700212050ustar00rootroot00000000000000/* * 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+git20240825.4390ca/pio.c000066400000000000000000000226741476263345700173210ustar00rootroot00000000000000/* * (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+git20240825.4390ca/progress.c000066400000000000000000000110671476263345700203700ustar00rootroot00000000000000/* * 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+git20240825.4390ca/progress.h000066400000000000000000000030671476263345700203760ustar00rootroot00000000000000/* * 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+git20240825.4390ca/script.c000066400000000000000000000134051476263345700200260ustar00rootroot00000000000000/* * 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+git20240825.4390ca/script.h000066400000000000000000000065011476263345700200320ustar00rootroot00000000000000/* * 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+git20240825.4390ca/script_bin.c000066400000000000000000000230641476263345700206600ustar00rootroot00000000000000/* * 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 == '_' || *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+git20240825.4390ca/script_bin.h000066400000000000000000000033051476263345700206610ustar00rootroot00000000000000/* * 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+git20240825.4390ca/script_extractor.c000066400000000000000000000022551476263345700221220ustar00rootroot00000000000000/* * 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+git20240825.4390ca/script_fex.c000066400000000000000000000221141476263345700206650ustar00rootroot00000000000000/* * 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); /* Some lines end in a trailing semicolon. */ if (pe > s && pe[-1] == ';') *--pe = '\0'; 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 == '-' || *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 == '-') 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+git20240825.4390ca/script_fex.h000066400000000000000000000016621476263345700206770ustar00rootroot00000000000000/* * 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+git20240825.4390ca/script_uboot.c000066400000000000000000000136041476263345700212370ustar00rootroot00000000000000/* * 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+git20240825.4390ca/script_uboot.h000066400000000000000000000015521476263345700212430ustar00rootroot00000000000000/* * 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+git20240825.4390ca/soc_info.c000066400000000000000000000560631476263345700203300ustar00rootroot00000000000000/* * 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. The BROM FEL code memory areas * are the same as on A10/A13/A20, but just shifted by 0x10000. * We put the backup buffers towards the end of SRAM C, in a location that * is also available on the H5. */ sram_swap_buffers a64_sram_swap_buffers[] = { /* 0x11C00-0x11FFF (IRQ stack) */ { .buf1 = 0x11C00, .buf2 = 0x31400, .size = 0x0400 }, /* 0x15C00-0x16FFF (Stack) */ { .buf1 = 0x15C00, .buf2 = 0x31800, .size = 0x1400 }, /* 0x17C00-0x17FFF (Something important) */ { .buf1 = 0x17C00, .buf2 = 0x32c00, .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 = 0x42400, .size = 0x0400 }, /* 0x25C00-0x26FFF (Stack) */ { .buf1 = 0x25C00, .buf2 = 0x42800, .size = 0x1400 }, /* 0x27C00-0x27FFF (Something important) */ { .buf1 = 0x27C00, .buf2 = 0x43c00, .size = 0x0400 }, { .size = 0 } /* End of the table */ }; /* * T7 SRAM layout: * * SRAM A1: 0x0002_0000 - 0x0002_7fff, 32K contains stacks * SRAM C: 0x0002_8000 - 0x0004_ffff, 160K full access * SRAM A2: 0x0010_0000 - 0x0010_3fff, 16K OpenRISC * 0x0010_4000 - 0x0011_ffff, 112K full access */ sram_swap_buffers t7_sram_swap_buffers[] = { /* 0x21C00-0x21FFF (IRQ stack) */ { .buf1 = 0x21C00, .buf2 = 0x4e400, .size = 0x0400 }, /* 0x25C00-0x26FFF (Stack) */ { .buf1 = 0x25C00, .buf2 = 0x4e800, .size = 0x1400 }, /* 0x27C00-0x27FFF (Something important) */ { .buf1 = 0x27C00, .buf2 = 0x4fc00, .size = 0x0400 }, { .size = 0 } /* End of the table */ }; /* * V831 has 96KiB SRAM A1 at 0x20000 where the SPL has to be loaded to. * SRAM C is continuous with SRAM A1, and both SRAMs are tried to be used * by BROM. Memory space is allocated both from the start of SRAM A1 and * the end of SRAM C. * The start of SRAM C is in between these areas, and can serve as backup * of IRQ stack, which is inside the first 32KiB of SRAM A1. Other areas * that are critical on older SoCs seem to be already in SRAM C, which * we do not need to preserve. */ sram_swap_buffers v831_sram_swap_buffers[] = { { .buf1 = 0x21000, .buf2 = 0x38000, .size = 0x1000 }, { .size = 0 } /* End of the table */ }; /* H616 situation is the same as V831 one, except it has 32 KiB of SRAM A1. */ sram_swap_buffers h616_sram_swap_buffers[] = { { .buf1 = 0x21000, .buf2 = 0x52a00, .size = 0x1000 }, { .size = 0 } /* End of the table */ }; /* * R329 has no SRAM A1, but a huge SRAM A2 at 0x100000. SPL and BROM uses * this SRAM A2's first part like how other SoCs use SRAM A1. The sp and * sp_irq values checked with thunk are 0x13c2c8 and 0x101400, which looks * similar to the situation of V831, in which the stack is quite high. */ sram_swap_buffers r329_sram_swap_buffers[] = { /* 0x101000-0x101400 (IRQ stack) */ { .buf1 = 0x101000, .buf2 = 0x13bc00, .size = 0x0400 }, { .size = 0 } /* End of the table */ }; /* * The FEL code from BROM in F1C100s also uses SRAM A in a similar way * with A10/A13/A20. * Unfortunately the SRAM layout of F1C100s is not documented at all, so * we can only try by r/w under FEL mode. * The result is that there's a contingous SRAM zone from 0x8800 to 0xb5ff. */ sram_swap_buffers f1c100s_sram_swap_buffers[] = { /* 0x1C00-0x1FFF (IRQ stack) */ { .buf1 = 0x1C00, .buf2 = 0x9000, .size = 0x0400 }, /* 0x5C00-0x6FFF (Stack) */ { .buf1 = 0x5C00, .buf2 = 0x9400, .size = 0x1400 }, /* 0x7C00-0x7FFF (Something important) */ { .buf1 = 0x7C00, .buf2 = 0xa800, .size = 0x0400 }, { .size = 0 } /* End of the table */ }; sram_swap_buffers a523_sram_swap_buffers[] = { { .buf1 = 0x45000, .buf2 = 0x40200, .size = 0x0400 }, { .size = 0 } /* End of the table */ }; /* * Some SoCs put both stacks, BSS and data segments at the end of a comparably * large SRAM, so we don't need to move anything around. */ sram_swap_buffers no_sram_swap_buffers[] = { { .size = 0 } /* End of the table */ }; const watchdog_info wd_a10_compat = { .reg_mode = 0x01C20C94, .reg_mode_value = 3, }; const watchdog_info wd_h3_compat = { .reg_mode = 0x01C20Cb8, .reg_mode_value = 1, }; const watchdog_info wd_a80 = { .reg_mode = 0x06000CB8, .reg_mode_value = 1, }; const watchdog_info wd_h6_compat = { .reg_mode = 0x030090b8, .reg_mode_value = 1, }; const watchdog_info wd_v853_compat = { .reg_mode = 0x020500b8, .reg_mode_value = 0x16aa0001, }; const watchdog_info wd_a523_compat = { .reg_mode = 0x02050008, .reg_mode_value = 0x16aa0001, }; static const sid_section r40_sid_maps[] = { SID_SECTION("chipid", 0x00, 128), SID_SECTION("in", 0x10, 256), SID_SECTION("ssk", 0x30, 128), SID_SECTION("thermal", 0x40, 32), SID_SECTION("ft_zone", 0x44, 64), SID_SECTION("tvout", 0x4c, 128), SID_SECTION("rssk", 0x5c, 256), SID_SECTION("hdcp_hash",0x7c, 128), SID_SECTION("reserved", 0x90, 896), SID_SECTION(NULL, 0, 0), }; static const sid_section h3_sid_maps[] = { SID_SECTION("chipid", 0x00, 128), SID_SECTION("oem_program", 0x10, 32), SID_SECTION("nv1", 0x14, 32), SID_SECTION("nv2", 0x18, 64), SID_SECTION("rsakey_hash", 0x20, 160), SID_SECTION("thermal", 0x34, 64), SID_SECTION("renewability", 0x3c, 64), SID_SECTION("huk", 0x44, 256), SID_SECTION("rotpk_hash", 0x64, 256), SID_SECTION("ssk", 0x84, 128), SID_SECTION("rssk", 0x94, 256), SID_SECTION("hdcp_hash", 0xb4, 128), SID_SECTION("ek_hash", 0xc4, 128), SID_SECTION("sn", 0xd4, 192), SID_SECTION("nv2_backup", 0xec, 64), SID_SECTION("lcjs", 0xf4, 32), SID_SECTION("debug", 0xf8, 32), SID_SECTION("chip_config", 0xfc, 32), SID_SECTION(NULL, 0, 0), }; static const sid_section h6_sid_maps[] = { SID_SECTION("chipid", 0x00, 128), SID_SECTION("brom_config", 0x10, 32), SID_SECTION("thermal", 0x14, 64), SID_SECTION("tf_zone", 0x1c, 128), SID_SECTION("oem_program", 0x2c, 96), SID_SECTION("mac-addr", 0x38, 64), SID_SECTION("write_protect", 0x40, 32), SID_SECTION("read-protect", 0x44, 32), SID_SECTION("lcjs", 0x48, 32), SID_SECTION("attr", 0x4c, 32), SID_SECTION("huk", 0x50, 96), SID_SECTION("vendor_id", 0x5c, 32), SID_SECTION("huk2", 0x60, 128), SID_SECTION("rotpk_hash", 0x70, 256), SID_SECTION("ssk", 0x90, 128), SID_SECTION("rssk", 0xa0, 256), SID_SECTION("hdcp_hash", 0xc0, 128), SID_SECTION("ek_hash", 0xd0, 128), SID_SECTION("sn", 0xe0, 192), SID_SECTION("nv1", 0xf8, 32), SID_SECTION("nv2", 0xfc, 224), SID_SECTION("hdcp_pkf", 0x118, 128), SID_SECTION("hdcp_duk", 0x128, 128), SID_SECTION("backup_key", 0x138, 576), SID_SECTION("sck0", 0x180, 256), SID_SECTION("sck0_mask", 0x1a0, 256), SID_SECTION("sck1", 0x1c0, 256), SID_SECTION("sck1_mask", 0x1e0, 256), SID_SECTION(NULL, 0, 0) }; static const sid_section t7_sid_maps[] = { SID_SECTION("chipid", 0x00, 128), SID_SECTION("brom_config", 0x10, 32), SID_SECTION("thermal", 0x14, 96), SID_SECTION("tf_zone", 0x20, 128), SID_SECTION("oem", 0x30, 128), SID_SECTION("jtag-security", 0x48, 32), SID_SECTION("jtag-attr", 0x4c, 32), SID_SECTION("in", 0x50, 192), SID_SECTION("operator-id", 0x68, 32), SID_SECTION("id", 0x6c, 32), SID_SECTION("rotpk", 0x70, 256), SID_SECTION("ssk", 0x90, 128), SID_SECTION("rssk", 0xa0, 256), SID_SECTION("reserved", 0xc0, 128), SID_SECTION("ek_hash", 0xd0, 128), SID_SECTION("sn", 0xe0, 192), SID_SECTION("nv1", 0xf8, 32), SID_SECTION("nv2", 0xfc, 224), SID_SECTION("reserved2", 0x118, 320), SID_SECTION(NULL, 0, 0) }; /* Placeholder for SoCs without a known SID map */ static const sid_section generic_2k_sid_maps[] = { SID_SECTION("chipid", 0x00, 128), SID_SECTION("unknown", 0x10, 1920), SID_SECTION(NULL, 0, 0), }; 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, .sram_size = 48 * 1024, .needs_l2en = true, .sid_base = 0x01C23800, .watchdog = &wd_a10_compat, },{ .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, .sram_size = 48 * 1024, .needs_l2en = true, .sid_base = 0x01C23800, .watchdog = &wd_a10_compat, },{ .soc_id = 0x1651, /* Allwinner A20 */ .name = "A20", .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .sram_size = 48 * 1024, .sid_base = 0x01C23800, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_a10_compat, },{ .soc_id = 0x1650, /* Allwinner A23 */ .name = "A23", .scratch_addr = 0x1000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sram_size = 64 * 1024, .sid_base = 0x01C23800, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_h3_compat, },{ .soc_id = 0x1633, /* Allwinner A31 */ .name = "A31", .scratch_addr = 0x1000, .thunk_addr = 0x22E00, .thunk_size = 0x200, .swap_buffers = a31_sram_swap_buffers, .sram_size = 32 * 1024, .watchdog = &wd_h3_compat, },{ .soc_id = 0x1667, /* Allwinner A33, R16 */ .name = "A33", .scratch_addr = 0x1000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sram_size = 32 * 1024, .sid_base = 0x01C23800, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_h3_compat, },{ .soc_id = 0x1689, /* Allwinner A64 */ .name = "A64", .spl_addr = 0x10000, .scratch_addr = 0x11000, .thunk_addr = 0x31200, .thunk_size = 0x200, .swap_buffers = a64_sram_swap_buffers, .sram_size = 140 * 1024, .sid_base = 0x01C14000, .sid_offset = 0x200, .sid_sections = h3_sid_maps, .rvbar_reg = 0x017000A0, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x40004, .watchdog = &wd_h3_compat, },{ .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, .sram_size = 40 * 1024, .sid_base = 0X01C0E000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_a80, },{ .soc_id = 0x1663, /* Allwinner F1C100s (all new sun3i?) */ .name = "F1C100s", .scratch_addr = 0x1000, .thunk_addr = 0xb400, .thunk_size = 0x200, .swap_buffers = f1c100s_sram_swap_buffers, .sram_size = 32 * 1024, /* No SID */ .watchdog = &wd_h3_compat, },{ .soc_id = 0x1673, /* Allwinner A83T */ .name = "A83T", .scratch_addr = 0x1000, .mmu_tt_addr = 0x44000, .thunk_addr = 0x46E00, .thunk_size = 0x200, .swap_buffers = ar100_abusing_sram_swap_buffers, .sram_size = 32 * 1024, .sid_base = 0x01C14000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_h3_compat, },{ .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, .sram_size = 108 * 1024, .sid_base = 0x01C14000, .sid_offset = 0x200, .sid_fix = true, .sid_sections = h3_sid_maps, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x40004, .watchdog = &wd_h3_compat, },{ .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, .sram_size = 60 * 1024, .sid_base = 0x01C23800, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_h3_compat, },{ .soc_id = 0x1708, /* Allwinner T7 */ .name = "T7", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x4e200, .thunk_size = 0x200, .swap_buffers = t7_sram_swap_buffers, .sram_size = 184 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = t7_sid_maps, .watchdog = &wd_h6_compat, },{ .soc_id = 0x1718, /* Allwinner H5 */ .name = "H5", .spl_addr = 0x10000, .scratch_addr = 0x11000, .thunk_addr = 0x31200, .thunk_size = 0x200, .swap_buffers = a64_sram_swap_buffers, .sram_size = 140 * 1024, .sid_base = 0x01C14000, .sid_offset = 0x200, .sid_sections = h3_sid_maps, .rvbar_reg = 0x017000A0, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x40004, .watchdog = &wd_h3_compat, },{ .soc_id = 0x1701, /* Allwinner R40 */ .name = "R40", .scratch_addr = 0x1000, .thunk_addr = 0xA200, .thunk_size = 0x200, .swap_buffers = a10_a13_a20_sram_swap_buffers, .sram_size = 48 * 1024, .sid_base = 0x01C1B000, .sid_offset = 0x200, .sid_sections = r40_sid_maps, .watchdog = &wd_a10_compat, },{ .soc_id = 0x1719, /* Allwinner A63 */ .name = "A63", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x42200, .thunk_size = 0x200, .swap_buffers = h6_sram_swap_buffers, .sram_size = 144 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .rvbar_reg = 0x09010040, .watchdog = &wd_h6_compat, },{ .soc_id = 0x1728, /* Allwinner H6 */ .name = "H6", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x42200, .thunk_size = 0x200, .swap_buffers = h6_sram_swap_buffers, .sram_size = 144 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = h6_sid_maps, .rvbar_reg = 0x09010040, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x100004, .watchdog = &wd_h6_compat, },{ .soc_id = 0x1816, /* Allwinner V536 */ .name = "V536", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x2A200, .thunk_size = 0x200, .swap_buffers = v831_sram_swap_buffers, .sram_size = 228 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_h6_compat, },{ .soc_id = 0x1817, /* Allwinner V831 */ .name = "V831", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x2A200, .thunk_size = 0x200, .swap_buffers = v831_sram_swap_buffers, .sram_size = 228 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_h6_compat, },{ .soc_id = 0x1823, /* Allwinner H616 */ .name = "H616", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x53a00, .thunk_size = 0x200, .swap_buffers = h616_sram_swap_buffers, .sram_size = 207 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .rvbar_reg = 0x09010040, .rvbar_reg_alt= 0x08100040, .ver_reg = 0x03000024, .watchdog = &wd_h6_compat, },{ .soc_id = 0x1851, /* Allwinner R329 */ .name = "R329", .spl_addr = 0x100000, .scratch_addr = 0x101000, .mmu_tt_addr = 0x130000, .thunk_addr = 0x13ba00, .thunk_size = 0x200, .swap_buffers = r329_sram_swap_buffers, .sram_size = 1856 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .rvbar_reg = 0x08100040, .watchdog = &wd_h6_compat, },{ .soc_id = 0x1886, /* Allwinner V853 */ .name = "V853", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x3A200, .thunk_size = 0x200, .swap_buffers = v831_sram_swap_buffers, .sram_size = 132 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .icache_fix = true, .watchdog = &wd_v853_compat, },{ .soc_id = 0x1859, /* Allwinner D1/D1s/R528/T113-S3 */ .name = "R528", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x3a200, .thunk_size = 0x200, .swap_buffers = v831_sram_swap_buffers, .sram_size = 160 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .icache_fix = true, .watchdog = &wd_v853_compat, },{ .soc_id = 0x1721, /* Allwinner V5 */ .name = "V5", .spl_addr = 0x20000, .scratch_addr = 0x21000, .thunk_addr = 0x42200, .thunk_size = 0x200, .swap_buffers = h6_sram_swap_buffers, .sram_size = 136 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .watchdog = &wd_h6_compat, },{ .soc_id = 0x1890, /* Allwinner A523 */ .name = "A523", .spl_addr = 0x44000, .scratch_addr = 0x45000, .thunk_addr = 0x40000, .thunk_size = 0x200, .swap_buffers = a523_sram_swap_buffers, .sram_size = 96 * 1024, .sid_base = 0x03006000, .sid_offset = 0x200, .sid_sections = generic_2k_sid_maps, .rvbar_reg = 0x08000040, .icache_fix = true, .watchdog = &wd_a523_compat, },{ .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); } /* * Iterate through all supported SoCs. The first call will take NULL as * an argument, subsequent calls pass in the pointer returned by the * previous call. When we reach the end of the list, the function * returns NULL. */ const soc_info_t *get_next_soc(const soc_info_t *prev) { const soc_info_t *soc; if (prev == NULL) return &soc_info_table[0]; for (soc = soc_info_table; soc->swap_buffers; soc++) { if (soc != prev) continue; soc++; if (!soc->swap_buffers) /* end of list? */ return NULL; return soc; } return NULL; /* prev entry not found */ } 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+git20240825.4390ca/soc_info.h000066400000000000000000000143711476263345700203310ustar00rootroot00000000000000/* * 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; /* * Contains information on the watchdog peripheral, to enable reset */ typedef struct { /* Register that needs to be written to */ uint32_t reg_mode; /* Value to write to trigger a reset */ uint32_t reg_mode_value; } watchdog_info; /* * sunxi sid sections */ typedef struct { const char *name; uint32_t offset; uint32_t size_bits; } sid_section; #define SID_SECTION(_name, _offset, _size_bits) { \ .name = _name, \ .offset = _offset, \ .size_bits = _size_bits, \ } /* * 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" */ const sid_section *sid_sections; /* sid memory maps */ uint32_t rvbar_reg; /* MMIO address of RVBARADDR0_L register */ uint32_t rvbar_reg_alt;/* alternative MMIO address of RVBARADDR0_L register */ uint32_t ver_reg; /* MMIO address of "Version Register" */ const watchdog_info *watchdog; /* Used for reset */ bool sid_fix; /* Use SID workaround (read via register) */ /* Use I$ workaround (disable I$ before first write to prevent stale thunk */ bool icache_fix; /* Use SMC workaround (enter secure mode) if can't read from this address */ uint32_t needs_smc_workaround_if_zero_word_at_addr; uint32_t sram_size; /* Usable contiguous SRAM at spl_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); const soc_info_t *get_next_soc(const soc_info_t *prev); #endif /* _SUNXI_TOOLS_SOC_INFO_H */ sunxi-tools-1.4.2+git20240825.4390ca/sunxi-fel.1000066400000000000000000000201611476263345700203470ustar00rootroot00000000000000.\" Manpage for sunxi-fel .\" Copyright (C) 2018 by Andre Przywara .TH sunxi-fel 1 "14 Jan 2022" "1.5" "sunxi-fel man page" .SH NAME sunxi-fel \- controlling USB BootROM protocol for Allwinner CPUs .SH SYNOPSIS sunxi-fel [OPTIONS] COMMAND [ARGS] .SH DESCRIPTION sunxi-fel is a script interface for USB communication with the BootROM of Allwinner CPUs. On explicit request (typically by pressing a button often labeled "uboot" or "recovery"), or when all other booting methods fail, the CPU's early ROM code enters the so called FEL mode, where it will wait for USB commands sent by some host to the Allwinner CPU's USB OTG interface. sunxi-fel implements this FEL protocol and communicates with the ROM code, to download or upload data and to start code execution. Besides simply allowing to write to or to read from device memory, sunxi-fel also helps with more complex things, which involve uploading code, executing it and then returning to FEL mode, to allow further data transfer or inspection. In particular it supports loading and executing U-Boot, including the primary SPL stage. .SH "OPTIONS" Those options affect general execution and should be put first, before any actual commands. .sp .B \-h, \-\-help .RS 4 Print a help message and exit. .RE .sp .B \-v, \-\-verbose .RS 4 Enable verbose logging. .RE .sp .B \-p, \-\-progress .RS 4 "write" transfers show a progress bar. .RE .sp .B \-l, \-\-list .RS 4 Enumerate all (USB) FEL devices and exit. .RE .sp .B \-\-list-socs .RS 4 Print a list of all supported SoCs and exit. .RE .sp .B \-d, \-\-dev bus:devnum .RS 4 Use specific USB bus and device number .RE .sp .B \-\-sid SID .RS 4 Select a device by its SID key (exact match). The SID key of a particular device can be queried using the "sid" command. .RE .SH "SUNXI-FEL COMMANDS" sunxi-fel can take several commands, each followed by their parameters, and will execute them in order. The only exception is the "uboot" command, which will delay launching U-Boot until all other commands have been executed. .sp Please note that accessing any part of the DRAM will not work until the DRAM controller has been initialized. This can be achieved by uploading and executing a suitable U-Boot SPL image, using the "spl" command. Trying to access DRAM before that will most likely hang. .sp Any numbers given as parameters to those commands can be prefixed with "0x" to denote hexadecimal notation or "0" to start an octal number. They are interpretated as decimal numbers otherwise. .PP .B spl .RS 4 Load and execute U-Boot SPL. .sp Upload the given binary file to the appropriate SRAM location, carefully moving the FEL stack out of the way in the process. The binary is then executed. When it returns, the FEL stack is restored and execution is transferred back to the BootROM's FEL routine. .sp If the file additionally contains a main U-Boot binary (u-boot-sunxi-with-spl.bin), this command also transfers that to memory, using the load address stored in the image file, but won't execute it. .RE .PP .B uboot .RS 4 like "spl", but actually starts U-Boot. U-Boot execution will take place when the fel utility exits. This allows combining "uboot" with further "write" commands, to transfer other files possibly needed for the boot. .RE .PP .B hex[dump]
.RS 4 Hexadecimal memory dump. Dumps bytes of the memory region starting at
. The context will be displayed as a hexdump, suitable for human inspection. .RE .PP .B dump
.RS 4 Binary memory dump. Dumps bytes of the memory region starting at
. The context will be dumped "as is" to the standard output, so it can be redirected to a file and processed as binary data. .RE .PP .B exe[cute]
.RS 4 Start executing code at
in memory on the device. .RE .PP .B reset64
.RS 4 Request a warm reset of the core, starting execution in the 64-bit AArch64 execution state, at
. .RE .PP .B wdreset .RS 4 Reset the device by triggering the watchdog with the shortest possible period. This will reset the whole SoC, including all on-SoC peripherals, but might not affect on-board devices like PMICs or PHYs. .RE .PP .B memmove .RS 4 Copy bytes within device memory, from to . .RE .PP .B readl
.RS 4 Read a 32-bit value from device memory at
. The value will be output as a hexadecimal number, prefixed with "0x". The address needs to be 4-byte aligned. .RE .PP .B writel
.RS 4 Write the given 32-bit to device memory at
. .RE .PP .B read
.RS 4 Write memory contents into file. Reads bytes from memory at
and writes the content into . .RE .PP .B write
.RS 4 Store file contents into memory. Writes the entire content of into memory at
. .RE .PP .B write-with-progress .RS 4 Display a textual progress bar while writing to the device. Same as "write" with the "-p" parameter. .RE .PP .B write-with-gauge .RS 4 Same as write, but write the progress in percentages to the standard output. This can be piped to any "dialog" compatible program, when using the --gauge widget. .RE .PP .B write-with-xgauge .RS 4 Same as write-with-gauge, but with extended gauge output. This can be piped to any "dialog" compatible program, using the --gauge widget. Aside from updating the current progress in percents, it also updates the number of bytes written and gives an estimated time to completion. .RE .PP .B multi[write] # ... .RS 4 Like "write-with-progress", but with multiple load adddresses and files, all sharing the same progress bar. .RE .PP .B multi[write]-with-gauge ... .RS 4 Like "write-with-gauge", but with multiple load adddresses and files, all sharing the same progress bar. .RE .PP .B multi[write]-with-xgauge ... .RS 4 Like "write-with-xgauge", but with multiple load adddresses and files, all sharing the same progress bar. .RE .PP .B echo-gauge "some text" .RS 4 Update prompt/caption for gauge output. This outputs a command to be interpreted by "dialog" to change the caption text. .RE .PP .B ver[sion] .RS 4 Show the BROM version. This prints some static data, among other things containing the detected SoC. Can be used to verify a FEL connection is working. .RE .PP .B sid .RS 4 Retrieve and output the 128-bit SID key. This key contains some form of serial number, which should be unique to each chip (although there have been reports of same SIDs for particular batches of chips). .RE .PP .B sid-registers .RS 4 As the "sid" command above, but use the alternative MMIO register access method on the device. There are SoCs that require this method due to bugs in the SID implementation, those known will automatically choose this workaround when using the "sid" command. This command here is to test new SoCs for compliance. .RE .PP .B sid-dump .RS 4 Read the entire SID eFuses array and dump its content. For SoCs with a known eFuses layout, this will annotate the known regions. .RE .PP .B clear
.RS 4 Clear bytes of memory starting at
(filling with zeroes). .RE .PP .B fill
.RS 4 Fills bytes of memory starting at
with the byte . .RE .PP .B spiflash-info .RS 4 Retrieves basic information about a SPI flash chip attached to the SPI0 pins. This is using the same method as the BootROM does, to accesses the same storage that the device could boot from. Prints the manufacturer of the flash chip and its capacity. Should also be used to detect the presence of a SPI flash chip. .RE .PP .B spiflash-read .RS 4 Reads bytes starting from offset of a SPI flash chip, storing the result into . .RE .PP .B spiflash-write .RS 4 Reads and stores its content in the SPI flash, starting at offset . .RE .SH EXAMPLES .RS 4 \fB$\fR sunxi-fel -v -p ver .RE .sp .RS 4 \fB$\fR sunxi-fel uboot u-boot-sunxi-with-spl.bin .RE .sp .RS 4 \fB$\fR sunxi-fel -v -p spl sunxi-spl.bin write 0x44000 bl31.bin write 0x4a000000 u-boot.bin reset64 0x44000 .RE .SH AUTHOR Andre Przywara sunxi-tools-1.4.2+git20240825.4390ca/tests/000077500000000000000000000000001476263345700175155ustar00rootroot00000000000000sunxi-tools-1.4.2+git20240825.4390ca/tests/Makefile000066400000000000000000000041571476263345700211640ustar00rootroot00000000000000# # tests/Makefile # # 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 . 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: $(BOARDS_DIR)/README # 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+git20240825.4390ca/tests/fextest.sh000077500000000000000000000021461476263345700215410ustar00rootroot00000000000000#!/bin/bash # # 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 . 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+git20240825.4390ca/tests/test_all_fex.sh000077500000000000000000000015321476263345700225260ustar00rootroot00000000000000#!/bin/sh # # 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 . 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+git20240825.4390ca/tests/test_bin2fex_corner_cases.sh000077500000000000000000000022611476263345700251770ustar00rootroot00000000000000#!/bin/bash # # === Test errors / corner cases of "bin2fex", improving on code coverage === # # 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 . 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+git20240825.4390ca/tests/test_fex2bin_corner_cases.sh000077500000000000000000000040251476263345700251770ustar00rootroot00000000000000#!/bin/bash # # === Test errors / corner cases of "fex2bin", improving on code coverage === # # 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 . 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+git20240825.4390ca/tests/unify-fex.c000066400000000000000000000077321476263345700216040ustar00rootroot00000000000000/* * 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); } /* Remove a trailing semicolon. */ c = strchr(p, 0); if (*--c == ';') *c = '\0'; } } } puts(line); } if (ferror(input)) { perror("file read error"); exit(EXIT_FAILURE); } fclose(input); return EXIT_SUCCESS; } sunxi-tools-1.4.2+git20240825.4390ca/thunks/000077500000000000000000000000001476263345700176675ustar00rootroot00000000000000sunxi-tools-1.4.2+git20240825.4390ca/thunks/Makefile000066400000000000000000000020241476263345700213250ustar00rootroot00000000000000# # 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,$<) -march=armv5te $< $(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) -v style=old > $@ $(THUNKS): %.h: %.S FORCE $(AS) -o $(subst .S,.o,$<) -march=armv5te $< $(OBJDUMP) -d $(subst .S,.o,$<) | $(AWK_O_TO_H) > $@ sunxi-tools-1.4.2+git20240825.4390ca/thunks/README.md000066400000000000000000000015511476263345700211500ustar00rootroot00000000000000 # 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+git20240825.4390ca/thunks/clrsetbits.S000066400000000000000000000007121476263345700221710ustar00rootroot00000000000000/* * 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+git20240825.4390ca/thunks/clrsetbits.h000066400000000000000000000010571476263345700222210ustar00rootroot00000000000000 /* : */ 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+git20240825.4390ca/thunks/fel-to-spl-thunk.S000066400000000000000000000111401476263345700231210ustar00rootroot00000000000000/* * 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. */ .arm 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: .space 32, 0xff 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 tst TMP1, #(1 << 2) tsteq TMP1, #(1 << 12) bne cache_is_unsupported bl swap_all_buffers verify_checksum: ldr CHECKSUM, checksum_seed 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' */ ldr TMP1, egon_fel_str str TMP1, [SPL_ADDR, #8] /* * Call the SPL code, but before that make sure the CPU sees the * recently uploaded code. This requires a DSB and ISB. * The "dsb" and "isb" *instructions* are not available in ARMv5TE, * but at least for DSB we can use the CP15 register encoding. This * works for ARMv7 and v8 as well, because we have checked our SCTLR * before (in fel.c), so we know that CP15BEN is set. * The ARM926 core does not implement ISB, instead the TRM recommends * just a branch to achieve the same "flush the pipeline" effect. * As just this is not sufficient for later cores, check the MIDR * register, and do the DSB only for ARMv6 or later. * The input register for the CP15 instruction is ignored. */ mcr p15, 0, TMP1, c7, c10, 4 /* CP15DSB */ mrc p15, 0, TMP1, c0, c0, 0 /* read MIDR */ and TMP1, TMP1, #(0xf << 16) /* architecture */ cmp TMP1, #(0x6 << 16) /* ARMv5TEJ */ mcrgt p15, 0, TMP1, c7, c5, 4 /* CP15ISB, if > ARMv5TEJ */ 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.???' */ ldr TMP1, cache_enabled_str str TMP1, [SPL_ADDR, #8] b return_to_fel_noswap checksum_is_bad: /* The checksum test failed, so change 'eGON.BT0' -> 'eGON.BAD' */ ldr TMP1, checksum_failed_str 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 checksum_seed: .word 0x5f0a6c39 egon_fel_str: .ascii ".FEL" cache_enabled_str: .ascii ".???" checksum_failed_str: .ascii ".BAD" 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+git20240825.4390ca/thunks/fel-to-spl-thunk.h000066400000000000000000000124321476263345700231530ustar00rootroot00000000000000 /* : */ 0xea000015, /* 0: b 5c */ /* : */ 0xffffffff, /* 4: .word 0xffffffff */ 0xffffffff, /* 8: .word 0xffffffff */ 0xffffffff, /* c: .word 0xffffffff */ 0xffffffff, /* 10: .word 0xffffffff */ 0xffffffff, /* 14: .word 0xffffffff */ 0xffffffff, /* 18: .word 0xffffffff */ 0xffffffff, /* 1c: .word 0xffffffff */ 0xffffffff, /* 20: .word 0xffffffff */ /* : */ 0xe1a00000, /* 24: nop */ /* : */ 0xe28f40e8, /* 28: add r4, pc, #232 */ /* : */ 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 */ /* : */ 0xe59f80b0, /* 5c: ldr r8, [pc, #176] */ 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} */ 0xe3120004, /* 80: tst r2, #4 */ 0x03120a01, /* 84: tsteq r2, #4096 */ 0x1a000013, /* 88: bne dc */ 0xebffffe5, /* 8c: bl 28 */ /* : */ 0xe59f706c, /* 90: ldr r7, [pc, #108] */ 0xe1a00008, /* 94: mov r0, r8 */ 0xe5905010, /* 98: ldr r5, [r0, #16] */ /* : */ 0xe4902004, /* 9c: ldr r2, [r0], #4 */ 0xe2555004, /* a0: subs r5, r5, #4 */ 0xe0877002, /* a4: add r7, r7, r2 */ 0x1afffffb, /* a8: bne 9c */ 0xe598200c, /* ac: ldr r2, [r8, #12] */ 0xe0577082, /* b0: subs r7, r7, r2, lsl #1 */ 0x1a00000b, /* b4: bne e8 */ 0xe59f2048, /* b8: ldr r2, [pc, #72] */ 0xe5882008, /* bc: str r2, [r8, #8] */ 0xee072f9a, /* c0: mcr 15, 0, r2, cr7, cr10, {4} */ 0xee102f10, /* c4: mrc 15, 0, r2, cr0, cr0, {0} */ 0xe202280f, /* c8: and r2, r2, #983040 */ 0xe3520806, /* cc: cmp r2, #393216 */ 0xce072f95, /* d0: mcrgt 15, 0, r2, cr7, cr5, {4} */ 0xe12fff38, /* d4: blx r8 */ 0xea000004, /* d8: b f0 */ /* : */ 0xe59f2028, /* dc: ldr r2, [pc, #40] */ 0xe5882008, /* e0: str r2, [r8, #8] */ 0xea000002, /* e4: b f4 */ /* : */ 0xe59f2020, /* e8: ldr r2, [pc, #32] */ 0xe5882008, /* ec: str r2, [r8, #8] */ /* : */ 0xebffffcc, /* f0: bl 28 */ /* : */ 0xe8bd4004, /* f4: pop {r2, lr} */ 0xe121f002, /* f8: msr CPSR_c, r2 */ 0xe59dd000, /* fc: ldr sp, [sp] */ 0xe12fff1e, /* 100: bx lr */ /* : */ 0x5f0a6c39, /* 104: .word 0x5f0a6c39 */ /* : */ 0x4c45462e, /* 108: .word 0x4c45462e */ /* : */ 0x3f3f3f2e, /* 10c: .word 0x3f3f3f2e */ /* : */ 0x4441422e, /* 110: .word 0x4441422e */ sunxi-tools-1.4.2+git20240825.4390ca/thunks/memcpy.S000066400000000000000000000037301476263345700213100ustar00rootroot00000000000000/* * 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+git20240825.4390ca/thunks/memcpy.h000066400000000000000000000063341476263345700213400ustar00rootroot00000000000000 /* : */ 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+git20240825.4390ca/thunks/objdump_to_h.awk000066400000000000000000000012721476263345700230460ustar00rootroot00000000000000# 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+git20240825.4390ca/thunks/readl_writel.S000066400000000000000000000014601476263345700224710ustar00rootroot00000000000000/* * 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+git20240825.4390ca/thunks/readl_writel.h000066400000000000000000000026021476263345700225150ustar00rootroot00000000000000 /* : */ 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+git20240825.4390ca/thunks/rmr-thunk.S000066400000000000000000000013061476263345700217420ustar00rootroot00000000000000/* * Request AArch32/AArch64 warm reset, using RVBAR and Reset Management Register * This is used on ARMv8 cores only, so force v7 code to allow dsb and isb. */ .arch armv7-a 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+git20240825.4390ca/thunks/rmr-thunk.h000066400000000000000000000014631476263345700217730ustar00rootroot00000000000000 /* : */ 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+git20240825.4390ca/thunks/sid_read_root.S000066400000000000000000000054141476263345700226340ustar00rootroot00000000000000/* * 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 ldr sid_key_index, offset 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], #4 /* store SID value */ add sid_key_index, #4 ldr r2, end cmp sid_key_index, r2 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 offset: .word 0 end: .word 0 sid_result: /* receives the values read from the SID registers */ sunxi-tools-1.4.2+git20240825.4390ca/thunks/sid_read_root.h000066400000000000000000000030601476263345700226540ustar00rootroot00000000000000 /* : */ htole32(0xe59f0044), /* 0: ldr r0, [pc, #68] */ htole32(0xe59f1044), /* 4: ldr r1, [pc, #68] */ htole32(0xe28f3048), /* 8: add r3, pc, #72 */ /* : */ 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(0xe4832004), /* 2c: str r2, [r3], #4 */ htole32(0xe2811004), /* 30: add r1, r1, #4 */ htole32(0xe59f2018), /* 34: ldr r2, [pc, #24] */ htole32(0xe1510002), /* 38: cmp r1, r2 */ htole32(0x3afffff2), /* 3c: bcc c */ htole32(0xe3a02000), /* 40: mov r2, #0 */ htole32(0xe5802040), /* 44: str r2, [r0, #64] */ htole32(0xe12fff1e), /* 48: bx lr */ /* : */ htole32(0x00000000), /* 4c: .word 0x00000000 */ /* : */ htole32(0x00000000), /* 50: .word 0x00000000 */ /* : */ htole32(0x00000000), /* 54: .word 0x00000000 */ sunxi-tools-1.4.2+git20240825.4390ca/uart0-helloworld-sdboot.c000066400000000000000000000463741476263345700232310ustar00rootroot00000000000000/* * 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; typedef unsigned short int u16; typedef unsigned char u8; #ifndef NULL #define NULL ((void*)0) #endif #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 A80_SRAMCTRL_BASE 0x00800000 #define A80_CCM_BASE 0x06000000 #define A80_PIO_BASE 0x06000800 #define A80_UART0_BASE 0x07000000 #define H6_UART0_BASE 0x05000000 #define H6_PIO_BASE 0x0300B000 #define H6_CCM_BASE 0x03001000 #define H6_SRAMCTRL_BASE 0x03000000 #define R329_UART0_BASE 0x02500000 #define R329_PIO_BASE 0x02000400 #define R329_CCM_BASE 0x02001000 #define V853_PIO_BASE 0x02000000 #define SUNIV_UART0_BASE 0x01c25000 #define SRAM_A1_ADDR_0 0x00000000 #define SRAM_A1_ADDR_10000 0x00010000 #define SRAM_A1_ADDR_20000 0x00020000 #define SRAM_A1_ADDR_100000 0x00100000 /***************************************************************************** * 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 #define GPIO_BANK(pin) ((pin) >> 5) #define GPIO_NUM(pin) ((pin) & 0x1F) #define GPIO_CFG_BASE(bank) ((u32 *)(pio_base + (bank) * pio_bank_size)) #define GPIO_CFG_INDEX(pin) (((pin) & 0x1F) >> 3) #define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1F) & 0x7) << 2) #define GPIO_PULL_BASE(bank) ((u32 *)(pio_base + (bank) * pio_bank_size + pio_pull_off)) #define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4) #define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1) #define GPIO_DAT_BASE(bank) ((u32 *)(pio_base + (bank) * pio_bank_size + pio_dat_off)) /* 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 MUX_GPIO_INPUT 0 #define MUX_GPIO_OUTPUT 1 #define MUX_2 2 #define MUX_3 3 #define MUX_4 4 #define MUX_5 5 #define MUX_6 6 /* GPIO pin pull-up/down config */ #define SUNXI_GPIO_PULL_DISABLE (0) #define SUNXI_GPIO_PULL_UP (1) #define SUNXI_GPIO_PULL_DOWN (2) #define BIT(x) (1U << (x)) #define FLAG_VAR0 0 #define FLAG_VAR1 BIT(0) #define FLAG_UART_ON_PORTF BIT(1) #define FLAG_NEW_GPIO BIT(2) #define FLAG_NEW_CLOCK BIT(3) #define FLAG_UART_ON_APB1 BIT(4) #define FLAG_A80_CLOCK BIT(5) #define FLAG_NCAT2 FLAG_NEW_GPIO | FLAG_NEW_CLOCK static const struct soc_info { u16 soc_id; char soc_name[10]; u32 pio_base; u32 ccu_base; u32 sram_a1_base; u32 uart0_base; u16 uart0_tx_pin; u8 uart0_pinmux; u8 flags; } soc_table[] = { { 0x1623, "A10", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPB(22), MUX_2 }, { 0x1625, "A10s", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPB(19), MUX_2, FLAG_VAR0 }, { 0x1625, "A13", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPB(19), MUX_2, FLAG_VAR1 | FLAG_UART_ON_PORTF }, { 0x1633, "A31/A31s", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPH(20), MUX_2, }, { 0x1639, "A80", A80_PIO_BASE, A80_CCM_BASE, SRAM_A1_ADDR_10000, A80_UART0_BASE, SUNXI_GPH(12), MUX_2, FLAG_A80_CLOCK }, { 0x1651, "A20", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPB(22), MUX_2 }, { 0x1663, "F1C100s", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNIV_UART0_BASE, SUNXI_GPE(0), MUX_5, FLAG_UART_ON_APB1 }, { 0x1673, "A83T", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPB(9), MUX_2 }, { 0x1689, "A64", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_10000, SUNXI_UART0_BASE, SUNXI_GPB(8), MUX_4 }, { 0x1680, "H2+", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPA(4), MUX_2, FLAG_VAR1 }, { 0x1680, "H3", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPA(4), MUX_2, FLAG_VAR0 }, { 0x1681, "V3s", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPB(8), MUX_3 }, { 0x1701, "R40", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_0, SUNXI_UART0_BASE, SUNXI_GPB(22), MUX_2 }, { 0x1708, "T7", H6_PIO_BASE, H6_CCM_BASE, SRAM_A1_ADDR_20000, H6_UART0_BASE, SUNXI_GPB(8), MUX_4, FLAG_NEW_CLOCK }, { 0x1718, "H5", SUNXI_PIO_BASE, AW_CCM_BASE, SRAM_A1_ADDR_10000, SUNXI_UART0_BASE, SUNXI_GPA(4), MUX_2 }, { 0x1719, "A63", H6_PIO_BASE, H6_CCM_BASE, SRAM_A1_ADDR_10000, H6_UART0_BASE, SUNXI_GPB(9), MUX_4, FLAG_NEW_CLOCK }, { 0x1721, "V5", H6_PIO_BASE, H6_CCM_BASE, SRAM_A1_ADDR_20000, H6_UART0_BASE, SUNXI_GPB(9), MUX_2, FLAG_NEW_CLOCK }, { 0x1728, "H6", H6_PIO_BASE, H6_CCM_BASE, SRAM_A1_ADDR_20000, H6_UART0_BASE, SUNXI_GPH(0), MUX_2, FLAG_NEW_CLOCK }, { 0x1817, "V831", H6_PIO_BASE, H6_CCM_BASE, SRAM_A1_ADDR_20000, H6_UART0_BASE, SUNXI_GPH(9), MUX_5, FLAG_NEW_CLOCK }, { 0x1823, "H616", H6_PIO_BASE, H6_CCM_BASE, SRAM_A1_ADDR_20000, H6_UART0_BASE, SUNXI_GPH(0), MUX_2, FLAG_NEW_CLOCK }, { 0x1851, "R329", R329_PIO_BASE, R329_CCM_BASE, SRAM_A1_ADDR_100000, R329_UART0_BASE, SUNXI_GPB(4), MUX_2, FLAG_NCAT2 }, { 0x1859, "R528", V853_PIO_BASE, R329_CCM_BASE, SRAM_A1_ADDR_20000, R329_UART0_BASE, SUNXI_GPE(2), MUX_6, FLAG_NCAT2 }, { 0x1886, "V853", V853_PIO_BASE, R329_CCM_BASE, SRAM_A1_ADDR_20000, R329_UART0_BASE, SUNXI_GPH(9), MUX_5, FLAG_NCAT2 }, { 0x1890, "A523", V853_PIO_BASE, R329_CCM_BASE, SRAM_A1_ADDR_20000, R329_UART0_BASE, SUNXI_GPB(9), MUX_2, FLAG_NCAT2 }, }; #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) static const struct soc_info *find_soc_info(int soc_id, int variant) { int i; for (i = 0; i < ARRAY_SIZE(soc_table); i++) { if (soc_table[i].soc_id != soc_id) continue; if (variant == (soc_table[i].flags & FLAG_VAR1)) return &soc_table[i]; } return NULL; } static u32 pio_base; static u32 pio_bank_size, pio_dat_off, pio_pull_off; static 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); u32 *addr = GPIO_CFG_BASE(bank) + index; cfg = readl(addr); cfg &= ~(0xf << offset); cfg |= val << offset; writel(cfg, addr); return 0; } static 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); u32 *addr = GPIO_PULL_BASE(bank) + index; cfg = readl(addr); cfg &= ~(0x3 << offset); cfg |= val << offset; writel(cfg, addr); return 0; } /***************************************************************************** * 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 A80_VER_REG (A80_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 */ static 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; } /* A10s and A13 share the same ID, so we need a little more effort on those */ static int sunxi_get_sun5i_variant(void) { if ((readl(SUN4I_SID_BASE + 8) & 0xf000) != 0x7000) return FLAG_VAR1; return FLAG_VAR0; } /* H2+ and H3 share the same ID, we can differentiate them by SID_RKEY0 */ static int sunxi_get_h3_variant(void) { u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0); /* H2+ uses those SID IDs */ if ((sid0 & 0xff) == 0x42 || (sid0 & 0xff) == 0x83) return FLAG_VAR1; /* * Note: according to Allwinner sources, H3 is expected * to show up as 0x00, 0x81 or ("H3D") 0x58 here. */ return FLAG_VAR0; } static const struct soc_info *sunxi_detect_soc(void) { int variant = 0; u32 soc_id; u32 midr; u32 reg; asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (midr)); if (((midr >> 4) & 0xFFF) == 0xc08) { /* ARM Cortex-A8: A10/A10s/A13 */ reg = VER_REG; } else if ((readl(0x03021008) & 0xfff) == 0x43b) {// GICD_IIDR @ NCAT reg = H6_VER_REG; } else if ((readl(0x01c81008) & 0xfff) == 0x43b) {// GICD_IIDR @ legacy reg = VER_REG; } else if ((readl(0x01c41008) & 0xfff) == 0x43b) {// GICD_IIDR @ A80 reg = A80_VER_REG; } else if ((readl(0x03400008) & 0xfff) == 0x43b) {// GICD_IIDR @ GIC-600 reg = H6_VER_REG; } else { while (1); // unknown } set_wbit(reg, 1U << 15); soc_id = readl(reg) >> 16; switch(soc_id) { case 0x1680: variant = sunxi_get_h3_variant(); break; case 0x1625: variant = sunxi_get_sun5i_variant(); break; } return find_soc_info(soc_id, variant); } /***************************************************************************** * 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 static void clock_init_uart(const struct soc_info *soc) { if (soc->flags & FLAG_NEW_CLOCK) { set_wbit(soc->ccu_base + 0x90c, 0x10001 << (CONFIG_CONS_INDEX - 1)); } else { int bit = 16 + CONFIG_CONS_INDEX - 1; int gate_ofs = 0x06c; int reset_ofs = 0x2d8; if (soc->flags & FLAG_UART_ON_APB1) { bit = 20 + CONFIG_CONS_INDEX - 1; gate_ofs = 0x068; reset_ofs = 0x2d0; } else if (soc->flags & FLAG_A80_CLOCK) { gate_ofs = 0x594; reset_ofs = 0x5b4; } /* Open the clock gate for UART0 */ set_wbit(soc->ccu_base + gate_ofs, 1U << bit); /* Deassert UART0 reset (not really needed on old SoCs) */ set_wbit(soc->ccu_base + reset_ofs, 1U << bit); } } /***************************************************************************** * 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. * *****************************************************************************/ static void gpio_init(const struct soc_info *soc) { pio_base = soc->pio_base; if (soc->flags & FLAG_NEW_GPIO) { /* GPIO V2 */ pio_bank_size = 0x30; pio_dat_off = 0x10; pio_pull_off = 0x24; } else { /* GPIO V1 */ pio_bank_size = 0x24; pio_dat_off = 0x10; pio_pull_off = 0x1c; } if (soc->flags & FLAG_UART_ON_PORTF) { /* Disable normal UART0 pins to avoid conflict */ sunxi_gpio_set_cfgpin(soc->uart0_tx_pin, MUX_GPIO_INPUT); sunxi_gpio_set_cfgpin(soc->uart0_tx_pin + 1, MUX_GPIO_INPUT); /* Use SD breakout board to access UART0 on MMC0 pins */ sunxi_gpio_set_cfgpin(SUNXI_GPF(2), soc->uart0_pinmux); sunxi_gpio_set_cfgpin(SUNXI_GPF(4), soc->uart0_pinmux); sunxi_gpio_set_pull(SUNXI_GPF(4), SUNXI_GPIO_PULL_UP); } else { sunxi_gpio_set_cfgpin(soc->uart0_tx_pin, soc->uart0_pinmux); sunxi_gpio_set_cfgpin(soc->uart0_tx_pin + 1, soc->uart0_pinmux); sunxi_gpio_set_pull(soc->uart0_tx_pin + 1, SUNXI_GPIO_PULL_UP); } } /*****************************************************************************/ 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 13 /* 24 * 1000 * 1000 / 16 / 115200 */ /* The BROM sets the CPU clock to 204MHz, AHB=CPU/2, APB=AHB/2 => 51 MHz */ #define BAUD_115200_SUNIV 28 /* 51 * 1000 * 1000 / 16 / 115200 */ #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) static void uart0_init(const struct soc_info *soc) { clock_init_uart(soc); uart0_base = soc->uart0_base; /* select dll dlh */ writel(0x80, UART0_LCR); /* set baudrate */ writel(0, UART0_DLH); if (soc->soc_id == 0x1663) writel(BAUD_115200_SUNIV, UART0_DLL); else writel(BAUD_115200, UART0_DLL); /* set line control */ writel(LC_8_N_1, UART0_LCR); } static void uart0_putc(char c) { while (!(readl(UART0_LSR) & (1 << 6))) {} writel(c, UART0_THR); } static 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 }; static int get_boot_device(const struct soc_info *soc) { u32 *spl_signature = (void *)soc->sram_a1_base + 0x4; /* Check the eGON.BT0 magic in the SPL header */ if (spl_signature[0] != 0x4E4F4765 || spl_signature[1] != 0x3054422E) return BOOT_DEVICE_FEL; u32 boot_dev = spl_signature[9] & 0xFF; /* offset into SPL = 0x28 */ if (boot_dev == 0) return BOOT_DEVICE_MMC0; if (boot_dev == 3) return BOOT_DEVICE_SPI; return BOOT_DEVICE_UNK; } int main(void) { const struct soc_info *soc = sunxi_detect_soc(); if (soc == NULL) return 0; gpio_init(soc); uart0_init(soc); uart0_puts("\nHello from Allwinner "); uart0_puts(soc->soc_name); uart0_puts("!\n"); switch (get_boot_device(soc)) { 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+git20240825.4390ca/uart0-helloworld-sdboot.lds000066400000000000000000000017161476263345700235600ustar00rootroot00000000000000/* * 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*) } }