first commit

This commit is contained in:
Qubot 2023-10-20 19:54:56 +08:00
commit a96e96064a
83 changed files with 13845 additions and 0 deletions

50
.travis.yml Normal file
View File

@ -0,0 +1,50 @@
# 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

361
LICENSE.md Normal file
View File

@ -0,0 +1,361 @@
### 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.

211
Makefile Normal file
View File

@ -0,0 +1,211 @@
# Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
# Copyright (C) 2012,2013 Henrik Nordstrom <henrik@henriknordstrom.net>
# Copyright (C) 2013 Patrick Wood <patrickhwood@gmail.com>
# Copyright (C) 2013 Pat Wood <Pat.Wood@efi.com>
#
# 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 <http://www.gnu.org/licenses/>.
# 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/

158
README.md Normal file
View File

@ -0,0 +1,158 @@
# 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 <amery@geeks.cl>
<br>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 <infmt>] [-O <outfmt>] [<input> [<output>]]
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*.
<br>_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

50
adb-devprobe.sh Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
# Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
#
# 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

18
autoversion.sh Executable file
View File

@ -0,0 +1,18 @@
#
# 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}\""

BIN
bin/fel-sdboot.sunxi Normal file

Binary file not shown.

BIN
bin/jtag-loop.sunxi Normal file

Binary file not shown.

BIN
bin/ramboot.scr Normal file

Binary file not shown.

7
bin/ramboot.uboot-sh Normal file
View File

@ -0,0 +1,7 @@
# 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

Binary file not shown.

42
boot_head.S Normal file
View File

@ -0,0 +1,42 @@
/*
* Boot header to work around broken Allwinner A1x boot loaders
*
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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

28
boot_head.lds Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
SECTIONS
{
. = 0x40007000;
.text : { *(.text) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
/DISCARD/ : { *(.note*) }
}

384
bootinfo.c Normal file
View File

@ -0,0 +1,384 @@
/*
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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 <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#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 [<filename>]\n", cmd);
printf(" With no <filename> 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;
}

48
common.h Normal file
View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _SUNXI_TOOLS_COMMON_H
#define _SUNXI_TOOLS_COMMON_H
#include <stddef.h> /* 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 */

44
fel-gpio Executable file
View File

@ -0,0 +1,44 @@
#!/bin/sh -e
# Copyright (C) 2012,2013 Henrik Nordstrom <henrik@henriknordstrom.net>
#
# 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

149
fel-remotefunc-compiler.rb Executable file
View File

@ -0,0 +1,149 @@
#!/usr/bin/env ruby
#
# (C) Copyright 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
if ARGV.size < 2
printf("Usage: #{$PROGRAM_NAME} [c_source_input] [marshalled_header_output]\n\n")
printf("This script uses an ARM toolchain to compile native ARM code, and then\n")
printf("automatically generates the necessary wrapper code for calling it from\n")
printf("the sunxi-fel tool. Executing such compiled pieces of code natively on\n")
printf("the device may be needed for the performance critical parts.\n")
printf("\nExample input file:\n\n")
printf(" unsigned sum(unsigned a, unsigned b)\n")
printf(" {\n")
printf(" return a + b;\n")
printf(" }\n")
printf("\n")
printf("Using this example code inside of sunxi-fel:\n")
printf("\n")
printf(" uint32_t a = 1, b = 2, c;\n")
printf(" aw_fel_remotefunc_prepare_sum(dev, a, b);\n")
printf(" aw_fel_remotefunc_execute(dev, &c);\n")
printf(" printf(\"%%d + %%d = %%d\\n\", a, b, c);\n\n")
printf("If the returned result is not needed (a void function), then the second\n")
printf("argument to the 'aw_fel_remotefunc_execute' function can be NULL.\n\n")
exit(1)
end
def tool_exists(tool_name)
`which #{tool_name} > /dev/null 2>&1`
return $?.to_i == 0
end
def parse_stack_usage(filename)
return unless File.exists?(filename)
File.read(filename).strip.split("\n").map do |l|
if l =~ /\:([^\:\s]+)\s+(\d+)\s+(\S+)/
if $3 != "static"
abort sprintf("Non-static stack usage for function '%s'\n", $1)
end
{function_name: $1, stack_usage: $2.to_i}
else
abort sprintf("Failed to parse stack usage information '%s'\n", l.strip)
end
end
end
toolchains = [
"arm-none-eabi-",
"arm-linux-gnueabihf-",
"arm-none-linux-gnueabi-",
"armv7a-hardfloat-linux-gnueabi-",
]
toolchain = toolchains.find { |toolchain| tool_exists("#{toolchain}gcc") }
abort "Can't find any usable ARM crosscompiler.\n" unless toolchain
# Compile the source file
system("#{toolchain}gcc -c -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")

View File

@ -0,0 +1,187 @@
/*
* Copyright © 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*
* 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;
}
}
}

View File

@ -0,0 +1,147 @@
/* 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 <spi_batch_data_transfer+0x1cc> */
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 <spi_batch_data_transfer+0x10c> */
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 <spi_batch_data_transfer+0x11c> */
0x02, 0x20, 0x45, 0xe0, /* d4: sub r2, r5, r2 */
0x03, 0x00, 0x52, 0xe3, /* d8: cmp r2, #3 */
0x14, 0x00, 0x00, 0x8a, /* dc: bhi 134 <spi_batch_data_transfer+0x134> */
0x00, 0x00, 0x52, 0xe3, /* e0: cmp r2, #0 */
0x25, 0x00, 0x00, 0x1a, /* e4: bne 180 <spi_batch_data_transfer+0x180> */
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 <spi_batch_data_transfer+0x18> */
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 <spi_batch_data_transfer+0x18> */
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 <spi_batch_data_transfer+0x94> */
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 <spi_batch_data_transfer+0xcc> */
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 <spi_batch_data_transfer+0xd8> */
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 <spi_batch_data_transfer+0xe0> */
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);
}

69
fel-sdboot.S Normal file
View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* Based on previous works
* Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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

28
fel-sdboot.lds Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
SECTIONS
{
. = 0x0030;
.text : { *(.text) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
/DISCARD/ : { *(.note*) }
}

606
fel-spiflash.c Normal file
View File

@ -0,0 +1,606 @@
/*
* (C) Copyright 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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");
}

34
fel-spiflash.h Normal file
View File

@ -0,0 +1,34 @@
/*
* (C) Copyright 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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

1494
fel.c Normal file

File diff suppressed because it is too large Load Diff

873
fel_lib.c Normal file
View File

@ -0,0 +1,873 @@
/*
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
* Copyright (C) 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/**********************************************************************
* USB library and helper functions for the FEL utility
**********************************************************************/
#include "portable_endian.h"
#include "fel_lib.h"
#include <libusb.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 = le16toh(buf->scratchpad);
buf->pad[0] = le32toh(buf->pad[0]);
buf->pad[1] = le32toh(buf->pad[1]);
}
/* AW_FEL_1_READ request */
void aw_fel_read(feldev_handle *dev, uint32_t offset, void *buf, size_t len)
{
aw_send_fel_request(dev, AW_FEL_1_READ, offset, len);
aw_usb_read(dev, buf, len);
aw_read_fel_status(dev);
}
/* AW_FEL_1_WRITE request */
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[] = {
/* <sid_read_root_key>: */
htole32(0xe59f0044), /* 0: ldr r0, [pc, #68] */
htole32(0xe59f1044), /* 4: ldr r1, [pc, #68] */
htole32(0xe28f3048), /* 8: add r3, pc, #72 */
/* <sid_read_loop>: */
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] */
/* <sid_read_wait>: */
htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */
htole32(0xe3120002), /* 20: tst r2, #2 */
htole32(0x1afffffc), /* 24: bne 1c <sid_read_wait> */
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 <sid_read_loop> */
htole32(0xe3a02000), /* 40: mov r2, #0 */
htole32(0xe5802040), /* 44: str r2, [r0, #64] */
htole32(0xe12fff1e), /* 48: bx lr */
/* <sid_base>: */
htole32(dev->soc_info->sid_base), /* SID base addr */
/* <offset>: */
htole32(offset), /* first word to read */
/* <end>: */
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);
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;
}

92
fel_lib.h Normal file
View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _SUNXI_TOOLS_FEL_LIB_H
#define _SUNXI_TOOLS_FEL_LIB_H
#include <stdbool.h>
#include <stdint.h>
#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 */

338
fexc.c Normal file
View File

@ -0,0 +1,338 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "fexc.h"
#include <errno.h>
#include <libgen.h>
#include <stdlib.h>
#include <string.h>
#ifndef NO_MMAP
#include <sys/mman.h>
#endif
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#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 = "<stdin>";
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 = "<stdin>";
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 = "<stdout>";
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 = "<stdout>";
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, &sections, &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[<input> [<output>]]\n", arg0,
mode ? " " : " [-I <infmt>] [-O <outfmt>] ");
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]:"<stdin>",
formats[outfmt], filename[1]?filename[1]:"<stdout>");
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;
}

30
fexc.h Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _SUNXI_TOOLS_FEXC_H
#define _SUNXI_TOOLS_FEXC_H
#include "common.h"
#include <stdint.h>
#include <stdio.h>
#include "script.h"
#include "script_bin.h"
#include "script_fex.h"
#include "script_uboot.h"
#endif

13
find-arm-gcc.sh Executable file
View File

@ -0,0 +1,13 @@
#
# 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/-/'

282
fit_image.c Normal file
View File

@ -0,0 +1,282 @@
/*
* Copyright (C) 2018-2020 Andre Przywara <osp@andrep.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdint.h>
#include <libfdt.h>
#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;
}

32
fit_image.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2018-2020 Andre Przywara <osp@andrep.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __FIT_IMAGE_H__
#define __FIT_IMAGE_H__
#include <stdint.h>
#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

84
include/list.h Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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 */

125
include/portable_endian.h Normal file
View File

@ -0,0 +1,125 @@
// "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 <endian.h>
#elif defined(__APPLE__)
# include <libkern/OSByteOrder.h>
# 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 <sys/endian.h>
/* 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 <winsock2.h>
# include <sys/param.h>
# 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

46
include/types.h Normal file
View File

@ -0,0 +1,46 @@
/*
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
* (C) Copyright 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <stdint.h>
#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

41
jtag-loop.S Normal file
View File

@ -0,0 +1,41 @@
/*
* (C) Copyright 2012 Jens Andersen <jens.andersen@gmail.com>
*
* 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 .

36
jtag-loop.c Normal file
View File

@ -0,0 +1,36 @@
/*
* (C) Copyright 2012 Jens Andersen <jens.andersen@gmail.com>
* (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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);
}

28
jtag-loop.lds Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
SECTIONS
{
. = 0x0030;
.text : { *(.text) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
/DISCARD/ : { *(.note*) }
}

791
meminfo.c Normal file
View File

@ -0,0 +1,791 @@
/*
* Copyright (C) 2012 Floris Bos <bos@je-eigen-domein.nl>
* Copyright (c) 2014 Luc Verhaegen <libv@skynet.be>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
#include <errno.h>
#include <stdbool.h>
#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 <asm/arch/dram.h>\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;
}

29
nand-common.h Normal file
View File

@ -0,0 +1,29 @@
/*
* (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);

1111
nand-image-builder.c Normal file

File diff suppressed because it is too large Load Diff

81
nand-part-a10.h Normal file
View File

@ -0,0 +1,81 @@
/*
* drivers/block/sun4i_nand/nfd/mbr.h
*
* (C) Copyright 2007-2012
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
*
* 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__

83
nand-part-a20.h Normal file
View File

@ -0,0 +1,83 @@
/*
* drivers/block/sun4i_nand/nfd/mbr.h
*
* (C) Copyright 2007-2012
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
*
* 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__

117
nand-part-main.c Normal file
View File

@ -0,0 +1,117 @@
/*
* (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 <stdio.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#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);
}

327
nand-part.c Normal file
View File

@ -0,0 +1,327 @@
/*
* 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. <www.allwinnertech.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#ifdef __linux__
# include <sys/ioctl.h>
# include <sys/mount.h> /* 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;
}

173
phoenix_info.c Normal file
View File

@ -0,0 +1,173 @@
/*
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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);
}
}
}

432
pio.c Normal file
View File

@ -0,0 +1,432 @@
/*
* (C) Copyright 2011 Henrik Nordstrom <henrik@henriknordstrom.net>
*
* 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 <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#ifndef NO_MMAP
#include <sys/mman.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#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<mode><pull><drive><data> 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;
}

165
progress.c Normal file
View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2015 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "progress.h"
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
/* 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);
}
}

41
progress.h Normal file
View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2015 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _SUNXI_TOOLS_PROGRESS_H
#define _SUNXI_TOOLS_PROGRESS_H
#include <stddef.h>
/* 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 */

268
script.c Normal file
View File

@ -0,0 +1,268 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#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(&section->entries);
list_append(&script->sections, &section->sections);
}
return section;
}
void script_section_delete(struct script_section *section)
{
struct list_entry *o;
assert(section);
while ((o = list_last(&section->entries))) {
struct script_entry *entry = container_of(o,
struct script_entry, entries);
script_entry_delete(entry);
}
if (!list_empty(&section->sections))
list_remove(&section->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(&section->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(&section->entries); o;
o = list_next(&section->entries, o)) {
ep = container_of(o, struct script_entry, entries);
if (strcmp(ep->name, name) == 0)
return ep;
}
return NULL;
}

120
script.h Normal file
View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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

356
script_bin.c Normal file
View File

@ -0,0 +1,356 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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(&section->entries); le;
le = list_next(&section->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;
}

60
script_bin.h Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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

42
script_extractor.c Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2015 Olliver Schinagl <oliver@schinagl.nl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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;
}

379
script_fex.c Normal file
View File

@ -0,0 +1,379 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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(&section->entries); le;
le = list_next(&section->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("<default>", 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, "<default>", 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;
}

23
script_fex.h Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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

256
script_uboot.c Normal file
View File

@ -0,0 +1,256 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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<ARRAY_SIZE(sections); i++) {
struct script_section *sp;
sp = script_find_section(script, sections[i].name);
if (sp)
sections[i].sp = sp;
else {
pr_err("%s: critical section missing",
sections[i].name);
return 0;
}
}
fputs("/* this file is generated, don't edit it yourself */\n\n"
"#include <common.h>\n"
#if 0
"#include <asm/arch/pmu.h>\n"
#endif
"#include <asm/arch/dram.h>\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;
}

22
script_uboot.h Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2012 Alejandro Mery <amery@geeks.cl>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#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

602
soc_info.c Normal file
View File

@ -0,0 +1,602 @@
/*
* Copyright (C) 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
* Copyright (C) 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/**********************************************************************
* SoC information and retrieval of soc_sram_info
**********************************************************************/
#include "soc_info.h"
#include <stdio.h>
#include <string.h>
/*
* 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 */
};
/*
* 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 */
};
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,
};
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)
};
/* 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 = 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,
.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,
},{
.swap_buffers = NULL /* End of the table */
}
};
/*
* This generic record assumes BROM with similar properties to A10/A13/A20/A31,
* but no extra SRAM sections beyond 0x8000. It also assumes that the IRQ
* handler stack usage never exceeds 0x400 bytes.
*
* The users may or may not hope that the 0x7000-0x8000 area is also unused
* by the BROM and re-purpose it for the SPL stack.
*
* The size limit for the ".text + .data" sections is ~21 KiB.
*/
sram_swap_buffers generic_sram_swap_buffers[] = {
{ .buf1 = 0x1C00, .buf2 = 0x5800, .size = 0x400 },
{ .size = 0 } /* End of the table */
};
soc_info_t generic_soc_info = {
.scratch_addr = 0x1000,
.thunk_addr = 0x5680, .thunk_size = 0x180,
.swap_buffers = generic_sram_swap_buffers,
};
/* functions to retrieve SoC information */
soc_info_t *get_soc_info_from_id(uint32_t soc_id)
{
soc_info_t *soc, *result = NULL;
for (soc = soc_info_table; soc->swap_buffers; soc++)
if (soc->soc_id == soc_id) {
result = soc;
break;
}
if (!result) {
printf("Warning: no 'soc_sram_info' data for your SoC (id=%04X)\n",
soc_id);
result = &generic_soc_info;
}
return result;
}
soc_info_t *get_soc_info_from_version(struct aw_fel_version *buf)
{
return get_soc_info_from_id(buf->soc_id);
}
void get_soc_name_from_id(soc_name_t buffer, uint32_t soc_id)
{
soc_info_t *soc;
for (soc = soc_info_table; soc->swap_buffers; soc++)
if (soc->soc_id == soc_id && soc->name != NULL) {
strncpy(buffer, soc->name, sizeof(soc_name_t) - 1);
return;
}
/* unknown SoC (or name string missing), use the hexadecimal ID */
snprintf(buffer, sizeof(soc_name_t) - 1, "0x%04X", soc_id);
}

145
soc_info.h Normal file
View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _SUNXI_TOOLS_SOC_INFO_H
#define _SUNXI_TOOLS_SOC_INFO_H
#include <stdbool.h>
#include <stdint.h>
/* 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 */
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);
#endif /* _SUNXI_TOOLS_SOC_INFO_H */

270
sunxi-fel.1 Normal file
View File

@ -0,0 +1,270 @@
.\" Manpage for sunxi-fel
.\" Copyright (C) 2018 by Andre Przywara <osp@andrep.de>
.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 \-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 <filename>
.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 <u-boot-sunxi-with-spl.bin>
.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] <address> <length>
.RS 4
Hexadecimal memory dump. Dumps <length> bytes of the memory region starting at
<address>. The context will be displayed as a hexdump, suitable for human
inspection.
.RE
.PP
.B dump <address> <length>
.RS 4
Binary memory dump. Dumps <length> bytes of the memory region starting at
<address>. 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] <address>
.RS 4
Start executing code at <address> in memory on the device.
.RE
.PP
.B reset64 <address>
.RS 4
Request a warm reset of the core, starting execution in the 64-bit AArch64
execution state, at <address>.
.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 <dest> <source> <size>
.RS 4
Copy <size> bytes within device memory, from <source> to <dest>.
.RE
.PP
.B readl <address>
.RS 4
Read a 32-bit value from device memory at <address>. The value will be output
as a hexadecimal number, prefixed with "0x". The address needs to be 4-byte
aligned.
.RE
.PP
.B writel <address> <value>
.RS 4
Write the given 32-bit <value> to device memory at <address>.
.RE
.PP
.B read <address> <length> <file>
.RS 4
Write memory contents into file. Reads <length> bytes from memory at <address>
and writes the content into <file>.
.RE
.PP
.B write <address> <file>
.RS 4
Store file contents into memory. Writes the entire content of <file> into
memory at <address>.
.RE
.PP
.B write-with-progress <addr> <file>
.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 <addr> <file>
.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 <addr> <file>
.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] # <addr> <file> ...
.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 <address> <length>
.RS 4
Clear <length> bytes of memory starting at <address> (filling with zeroes).
.RE
.PP
.B fill <address> <length> <value>
.RS 4
Fills <length> bytes of memory starting at <address> with the byte <value>.
.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 <addr> <length> <file>
.RS 4
Reads <length> bytes starting from offset <addr> of a SPI flash chip, storing
the result into <file>.
.RE
.PP
.B spiflash-write <addr> <file>
.RS 4
Reads <file> and stores its content in the SPI flash, starting at offset <addr>.
.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 <osp@andrep.de>

62
tests/Makefile Normal file
View File

@ -0,0 +1,62 @@
#
# tests/Makefile
#
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
#
# 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 <http://www.gnu.org/licenses/>.
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)

35
tests/fextest.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
#
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
#
# 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 <http://www.gnu.org/licenses/>.
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

23
tests/test_all_fex.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/sh
#
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
#
# 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 <http://www.gnu.org/licenses/>.
FEXFILES=fexfiles.lst
find $1 -name '*.fex' > ${FEXFILES}
while read fex; do
./fextest.sh ${fex} || exit
done <${FEXFILES}
rm -f ${FEXFILES}

View File

@ -0,0 +1,28 @@
#!/bin/bash
#
# === Test errors / corner cases of "bin2fex", improving on code coverage ===
#
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
#
# 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 <http://www.gnu.org/licenses/>.
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

View File

@ -0,0 +1,89 @@
#!/bin/bash
#
# === Test errors / corner cases of "fex2bin", improving on code coverage ===
#
# Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
#
# 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 <http://www.gnu.org/licenses/>.
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 <key> = <value> 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

148
tests/unify-fex.c Normal file
View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <ctype.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 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 <key> = <value> line, reformat it */
c = strdup(p + 1);
sprintf(p, " = %s", c);
free(c);
p += 3; /* have p point to the beginning of <value> */
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 "<default>" for missing options */
c = strrchr(p, '\0');
while (num < 4) {
c += sprintf(c, "<default>");
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;
}

35
thunks/Makefile Normal file
View File

@ -0,0 +1,35 @@
#
# 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) > $@

20
thunks/README.md Normal file
View File

@ -0,0 +1,20 @@
# 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

17
thunks/clrsetbits.S Normal file
View File

@ -0,0 +1,17 @@
/*
* 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) */

9
thunks/clrsetbits.h Normal file
View File

@ -0,0 +1,9 @@
/* <fel_clrsetbits_le32>: */
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 */

162
thunks/fel-to-spl-thunk.S Normal file
View File

@ -0,0 +1,162 @@
/*
* Copyright © 2015 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*
* 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.
*/

86
thunks/fel-to-spl-thunk.h Normal file
View File

@ -0,0 +1,86 @@
/* <entry_point>: */
0xea000015, /* 0: b 5c <setup_stack> */
/* <stack_begin>: */
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 */
/* <stack_end>: */
0xe1a00000, /* 24: nop */
/* <swap_all_buffers>: */
0xe28f40e8, /* 28: add r4, pc, #232 */
/* <swap_next_buffer>: */
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 */
/* <swap_next_word>: */
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 <swap_next_word> */
0xeafffff3, /* 58: b 2c <swap_next_buffer> */
/* <setup_stack>: */
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 <cache_is_unsupported> */
0xebffffe5, /* 8c: bl 28 <swap_all_buffers> */
/* <verify_checksum>: */
0xe59f706c, /* 90: ldr r7, [pc, #108] */
0xe1a00008, /* 94: mov r0, r8 */
0xe5905010, /* 98: ldr r5, [r0, #16] */
/* <check_next_word>: */
0xe4902004, /* 9c: ldr r2, [r0], #4 */
0xe2555004, /* a0: subs r5, r5, #4 */
0xe0877002, /* a4: add r7, r7, r2 */
0x1afffffb, /* a8: bne 9c <check_next_word> */
0xe598200c, /* ac: ldr r2, [r8, #12] */
0xe0577082, /* b0: subs r7, r7, r2, lsl #1 */
0x1a00000b, /* b4: bne e8 <checksum_is_bad> */
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 <return_to_fel> */
/* <cache_is_unsupported>: */
0xe59f2028, /* dc: ldr r2, [pc, #40] */
0xe5882008, /* e0: str r2, [r8, #8] */
0xea000002, /* e4: b f4 <return_to_fel_noswap> */
/* <checksum_is_bad>: */
0xe59f2020, /* e8: ldr r2, [pc, #32] */
0xe5882008, /* ec: str r2, [r8, #8] */
/* <return_to_fel>: */
0xebffffcc, /* f0: bl 28 <swap_all_buffers> */
/* <return_to_fel_noswap>: */
0xe8bd4004, /* f4: pop {r2, lr} */
0xe121f002, /* f8: msr CPSR_c, r2 */
0xe59dd000, /* fc: ldr sp, [sp] */
0xe12fff1e, /* 100: bx lr */
/* <checksum_seed>: */
0x5f0a6c39, /* 104: .word 0x5f0a6c39 */
/* <egon_fel_str>: */
0x4c45462e, /* 108: .word 0x4c45462e */
/* <cache_enabled_str>: */
0x3f3f3f2e, /* 10c: .word 0x3f3f3f2e */
/* <checksum_failed_str>: */
0x4441422e, /* 110: .word 0x4441422e */

70
thunks/memcpy.S Normal file
View File

@ -0,0 +1,70 @@
/*
* 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 */

55
thunks/memcpy.h Normal file
View File

@ -0,0 +1,55 @@
/* <fel_memcpy_up>: */
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 <copyup_tail> */
/* <copyup_head>: */
htole32(0xe3110003), /* 18: tst r1, #3 */
htole32(0x0a000004), /* 1c: beq 34 <copyup_loop> */
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 <copyup_head> */
htole32(0xe12fff1e), /* 30: bx lr */
/* <copyup_loop>: */
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 <copyup_loop> */
htole32(0xe2822004), /* 44: add r2, r2, #4 */
/* <copyup_tail>: */
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 <copyup_tail> */
/* <fel_memcpy_down>: */
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 <copydn_tail> */
/* <copydn_head>: */
htole32(0xe0813002), /* 80: add r3, r1, r2 */
htole32(0xe3130003), /* 84: tst r3, #3 */
htole32(0x0a000004), /* 88: beq a0 <copydn_loop> */
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 <copydn_head> */
/* <copydn_loop>: */
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 <copydn_loop> */
htole32(0xe2822004), /* b0: add r2, r2, #4 */
/* <copydn_tail>: */
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 <copydn_tail> */

33
thunks/objdump_to_h.awk Normal file
View File

@ -0,0 +1,33 @@
# 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
}

41
thunks/readl_writel.S Normal file
View File

@ -0,0 +1,41 @@
/*
* 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 */

24
thunks/readl_writel.h Normal file
View File

@ -0,0 +1,24 @@
/* <fel_readl_n>: */
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 */
/* <read_loop>: */
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 <read_loop> */
/* <fel_writel_n>: */
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 */
/* <write_loop>: */
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 <write_loop> */

26
thunks/rmr-thunk.S Normal file
View File

@ -0,0 +1,26 @@
/*
* 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) */

13
thunks/rmr-thunk.h Normal file
View File

@ -0,0 +1,13 @@
/* <rmr_request>: */
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 <rmr_request+0x28> */

72
thunks/sid_read_root.S Normal file
View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2016 Bernhard Nortmann <bernhard.nortmann@web.de>
*
* 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 */

28
thunks/sid_read_root.h Normal file
View File

@ -0,0 +1,28 @@
/* <sid_read_root_key>: */
htole32(0xe59f0044), /* 0: ldr r0, [pc, #68] */
htole32(0xe59f1044), /* 4: ldr r1, [pc, #68] */
htole32(0xe28f3048), /* 8: add r3, pc, #72 */
/* <sid_read_loop>: */
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] */
/* <sid_read_wait>: */
htole32(0xe5902040), /* 1c: ldr r2, [r0, #64] */
htole32(0xe3120002), /* 20: tst r2, #2 */
htole32(0x1afffffc), /* 24: bne 1c <sid_read_wait> */
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 <sid_read_loop> */
htole32(0xe3a02000), /* 40: mov r2, #0 */
htole32(0xe5802040), /* 44: str r2, [r0, #64] */
htole32(0xe12fff1e), /* 48: bx lr */
/* <sid_base>: */
htole32(0x00000000), /* 4c: .word 0x00000000 */
/* <offset>: */
htole32(0x00000000), /* 50: .word 0x00000000 */
/* <end>: */
htole32(0x00000000), /* 54: .word 0x00000000 */

712
uart0-helloworld-sdboot.c Normal file
View File

@ -0,0 +1,712 @@
/*
* Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* Partially based on the uart code from ar100-info
*
* (C) Copyright 2013 Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
*
* 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 <henrik@henriknordstrom.net>
*
* Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c:
*
* (C) Copyright 2007-2011
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
* Tom Cubie <tangliang@allwinnertech.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
typedef unsigned int u32;
#define set_wbit(addr, v) (*((volatile unsigned long *)(addr)) |= (unsigned long)(v))
#define readl(addr) (*((volatile unsigned long *)(addr)))
#define writel(v, addr) (*((volatile unsigned long *)(addr)) = (unsigned long)(v))
#define SUNXI_UART0_BASE 0x01C28000
#define SUNXI_PIO_BASE 0x01C20800
#define AW_CCM_BASE 0x01c20000
#define AW_SRAMCTRL_BASE 0x01c00000
#define H6_UART0_BASE 0x05000000
#define H6_PIO_BASE 0x0300B000
#define H6_CCM_BASE 0x03001000
#define H6_SRAMCTRL_BASE 0x03000000
#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
/*****************************************************************************
* 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 SUNXI_GPIO_INPUT (0)
#define SUNXI_GPIO_OUTPUT (1)
#define SUNIV_GPE_UART0 (5)
#define SUN4I_GPB_UART0 (2)
#define SUN5I_GPB_UART0 (2)
#define SUN6I_GPH_UART0 (2)
#define SUN8I_H3_GPA_UART0 (2)
#define SUN8I_R528_GPE_UART0 (6)
#define SUN8I_V3S_GPB_UART0 (3)
#define SUN8I_V5_GPB_UART0 (2)
#define SUN8I_V831_GPH_UART0 (5)
#define SUN8I_V853_GPH_UART0 (5)
#define SUN50I_H5_GPA_UART0 (2)
#define SUN50I_H6_GPH_UART0 (2)
#define SUN50I_H616_GPH_UART0 (2)
#define SUN50I_R329_GPB_UART0 (2)
#define SUN50I_A64_GPB_UART0 (4)
#define SUNXI_GPF_UART0 (4)
/* GPIO pin pull-up/down config */
#define SUNXI_GPIO_PULL_DISABLE (0)
#define SUNXI_GPIO_PULL_UP (1)
#define SUNXI_GPIO_PULL_DOWN (2)
static u32 pio_base;
static u32 pio_bank_size, pio_dat_off, pio_pull_off;
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;
}
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;
}
int sunxi_gpio_output(u32 pin, u32 val)
{
u32 dat;
u32 bank = GPIO_BANK(pin);
u32 num = GPIO_NUM(pin);
u32 *addr = GPIO_DAT_BASE(bank);
dat = readl(addr);
if(val)
dat |= 1 << num;
else
dat &= ~(1 << num);
writel(dat, addr);
return 0;
}
int sunxi_gpio_input(u32 pin)
{
u32 dat;
u32 bank = GPIO_BANK(pin);
u32 num = GPIO_NUM(pin);
u32 *addr = GPIO_DAT_BASE(bank);
dat = readl(addr);
dat >>= num;
return (dat & 0x1);
}
int gpio_direction_input(unsigned gpio)
{
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);
return sunxi_gpio_input(gpio);
}
int gpio_direction_output(unsigned gpio, int value)
{
sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
return sunxi_gpio_output(gpio, value);
}
/*****************************************************************************
* Nearly all the Allwinner SoCs are using the same VER_REG register for *
* runtime SoC type identification. For additional details see: *
* *
* https://linux-sunxi.org/SRAM_Controller_Register_Guide *
* *
* Allwinner A80 is an oddball and has a non-standard address of the VER_REG *
* *
* Allwinner A10s and A13 are using the same SoC type id, but they can be *
* differentiated using a certain part of the SID register. *
* *
* Allwinner H6 has its memory map totally reworked, but the SRAM controller *
* remains similar; the base of it is moved to 0x03000000. *
*****************************************************************************/
#define VER_REG (AW_SRAMCTRL_BASE + 0x24)
#define H6_VER_REG (H6_SRAMCTRL_BASE + 0x24)
#define SUN4I_SID_BASE 0x01C23800
#define SUN8I_SID_BASE 0x01C14000
#define SID_PRCTL 0x40 /* SID program/read control register */
#define SID_RDKEY 0x60 /* SID read key value register */
#define SID_OP_LOCK 0xAC /* Efuse operation lock value */
#define SID_READ_START (1 << 1) /* bit 1 of SID_PRCTL, Software Read Start */
u32 sid_read_key(u32 sid_base, u32 offset)
{
u32 reg_val;
reg_val = (offset & 0x1FF) << 16; /* PG_INDEX value */
reg_val |= (SID_OP_LOCK << 8) | SID_READ_START; /* request read access */
writel(reg_val, sid_base + SID_PRCTL);
while (readl(sid_base + SID_PRCTL) & SID_READ_START) ; /* wait while busy */
reg_val = readl(sid_base + SID_RDKEY); /* read SID key value */
writel(0, sid_base + SID_PRCTL); /* clear SID_PRCTL (removing SID_OP_LOCK) */
return reg_val;
}
static u32 soc_id;
void soc_detection_init(void)
{
u32 midr;
asm volatile("mrc p15, 0, %0, c0, c0, 0" : "=r" (midr));
if (((midr >> 4) & 0xFFF) == 0xC0F) {
soc_id = 0x1639; /* ARM Cortex-A15, so likely Allwinner A80 */
} else {
u32 reg;
/*
* This register is GICD_IIDR on H6, but unmapped according to
* other known SoCs' user manuals.
*/
reg = readl(0x03021008);
if ((reg & 0xfff) == 0x43b) /* Found GICv2 here, so it's a H6 */
reg = H6_VER_REG;
else
reg = VER_REG;
set_wbit(reg, 1 << 15);
soc_id = readl(reg) >> 16;
}
}
/* Most SoCs can reliably be distinguished by simply checking their ID value */
#define soc_is_a10() (soc_id == 0x1623)
#define soc_is_a20() (soc_id == 0x1651)
#define soc_is_a31() (soc_id == 0x1633)
#define soc_is_a80() (soc_id == 0x1639)
#define soc_is_a64() (soc_id == 0x1689)
#define soc_is_h5() (soc_id == 0x1718)
#define soc_is_a63() (soc_id == 0x1719)
#define soc_is_h6() (soc_id == 0x1728)
#define soc_is_h616() (soc_id == 0x1823)
#define soc_is_r329() (soc_id == 0x1851)
#define soc_is_r40() (soc_id == 0x1701)
#define soc_is_v3s() (soc_id == 0x1681)
#define soc_is_v831() (soc_id == 0x1817)
#define soc_is_v853() (soc_id == 0x1886)
#define soc_is_r528() (soc_id == 0x1859)
#define soc_is_v5() (soc_id == 0x1721)
#define soc_is_suniv() (soc_id == 0x1663)
/* A10s and A13 share the same ID, so we need a little more effort on those */
int soc_is_a10s(void)
{
return soc_id == 0x1625 &&
(readl(SUN4I_SID_BASE + 8) & 0xf000) == 0x7000;
}
int soc_is_a13(void)
{
return soc_id == 0x1625 &&
(readl(SUN4I_SID_BASE + 8) & 0xf000) != 0x7000;
}
/* H2+ and H3 share the same ID, we can differentiate them by SID_RKEY0 */
int soc_is_h2_plus(void)
{
if (soc_id != 0x1680) return 0;
u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0);
return (sid0 & 0xff) == 0x42 || (sid0 & 0xff) == 0x83;
}
int soc_is_h3(void)
{
if (soc_id != 0x1680) return 0;
u32 sid0 = sid_read_key(SUN8I_SID_BASE, 0);
/*
* Note: according to Allwinner sources, H3 is expected
* to show up as 0x00, 0x81 or ("H3D") 0x58 here.
*/
return (sid0 & 0xff) != 0x42 && (sid0 & 0xff) != 0x83;
}
/*****************************************************************************
* UART is mostly the same on A10/A13/A20/A31/H3/A64, except that newer SoCs *
* have changed the APB numbering scheme (A10/A13/A20 used to have APB0 and *
* APB1 names, but newer SoCs just have renamed them into APB1 and APB2). *
* The constants below are using the new APB numbering convention. *
* Also the newer SoCs have introduced the APB2_RESET register, but writing *
* to it effectively goes nowhere on older SoCs and is harmless. *
*****************************************************************************/
#define CONFIG_CONS_INDEX 1
#define APB2_CFG (AW_CCM_BASE + 0x058)
#define APB1_GATE (AW_CCM_BASE + 0x068)
#define APB2_GATE (AW_CCM_BASE + 0x06C)
#define APB1_RESET (AW_CCM_BASE + 0x2D0)
#define APB2_RESET (AW_CCM_BASE + 0x2D8)
#define APB2_GATE_UART_SHIFT (16)
#define APB1_GATE_UART_SHIFT 20
#define APB2_RESET_UART_SHIFT (16)
#define APB1_RESET_UART_SHIFT 20
#define H6_UART_GATE_RESET (H6_CCM_BASE + 0x90C)
#define R329_UART_GATE_RESET (R329_CCM_BASE + 0x90C)
#define H6_UART_GATE_SHIFT (0)
#define H6_UART_RESET_SHIFT (16)
void clock_init_uart_legacy(void)
{
/* Open the clock gate for UART0 */
set_wbit(APB2_GATE, 1 << (APB2_GATE_UART_SHIFT + CONFIG_CONS_INDEX - 1));
/* Deassert UART0 reset (only needed on A31/A64/H3) */
set_wbit(APB2_RESET, 1 << (APB2_RESET_UART_SHIFT + CONFIG_CONS_INDEX - 1));
}
void clock_init_uart_suniv(void)
{
/* open the clock for uart */
set_wbit(APB1_GATE,
1U << (APB1_GATE_UART_SHIFT + CONFIG_CONS_INDEX - 1));
/* deassert uart reset */
set_wbit(APB1_RESET,
1U << (APB1_RESET_UART_SHIFT + CONFIG_CONS_INDEX - 1));
}
void clock_init_uart_h6(void)
{
/* Open the clock gate for UART0 */
set_wbit(H6_UART_GATE_RESET, 1 << (H6_UART_GATE_SHIFT + CONFIG_CONS_INDEX - 1));
/* Deassert UART0 reset */
set_wbit(H6_UART_GATE_RESET, 1 << (H6_UART_RESET_SHIFT + CONFIG_CONS_INDEX - 1));
}
void clock_init_uart_r329(void)
{
/* Open the clock gate for UART0 */
set_wbit(R329_UART_GATE_RESET, 1 << (H6_UART_GATE_SHIFT + CONFIG_CONS_INDEX - 1));
/* Deassert UART0 reset */
set_wbit(R329_UART_GATE_RESET, 1 << (H6_UART_RESET_SHIFT + CONFIG_CONS_INDEX - 1));
}
void clock_init_uart(void)
{
if (soc_is_h6() || soc_is_v831() || soc_is_h616() || soc_is_v5() ||
soc_is_a63())
clock_init_uart_h6();
else if (soc_is_r329() || soc_is_v853() || soc_is_r528())
clock_init_uart_r329();
else if (soc_is_suniv())
clock_init_uart_suniv();
else
clock_init_uart_legacy();
}
/*****************************************************************************
* UART0 pins muxing is different for different SoC variants. *
* Allwinner A13 is a bit special, because there are no dedicated UART0 pins *
* and they are shared with MMC0. *
*****************************************************************************/
void gpio_init(void)
{
if (soc_is_v853() || soc_is_r528()) {
/* 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_is_a10() || soc_is_a20() || soc_is_r40()) {
sunxi_gpio_set_cfgpin(SUNXI_GPB(22), SUN4I_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(23), SUN4I_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(23), SUNXI_GPIO_PULL_UP);
} else if (soc_is_a10s()) {
sunxi_gpio_set_cfgpin(SUNXI_GPB(19), SUN5I_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN5I_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(20), SUNXI_GPIO_PULL_UP);
} else if (soc_is_a13()) {
/* Disable PB19/PB20 as UART0 to avoid conflict */
gpio_direction_input(SUNXI_GPB(19));
gpio_direction_input(SUNXI_GPB(20));
/* Use SD breakout board to access UART0 on MMC0 pins */
sunxi_gpio_set_cfgpin(SUNXI_GPF(2), SUNXI_GPF_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPF(4), SUNXI_GPF_UART0);
sunxi_gpio_set_pull(SUNXI_GPF(4), SUNXI_GPIO_PULL_UP);
} else if (soc_is_a31()) {
sunxi_gpio_set_cfgpin(SUNXI_GPH(20), SUN6I_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(21), SUN6I_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(21), SUNXI_GPIO_PULL_UP);
} else if (soc_is_a64()) {
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN50I_A64_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_A64_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
} else if (soc_is_h3() || soc_is_h2_plus()) {
sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN8I_H3_GPA_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN8I_H3_GPA_UART0);
sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP);
} else if (soc_is_h5()) {
sunxi_gpio_set_cfgpin(SUNXI_GPA(4), SUN50I_H5_GPA_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPA(5), SUN50I_H5_GPA_UART0);
sunxi_gpio_set_pull(SUNXI_GPA(5), SUNXI_GPIO_PULL_UP);
} else if (soc_is_a63()) {
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN50I_A64_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN50I_A64_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
} else if (soc_is_h6()) {
sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H6_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H6_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP);
} else if (soc_is_h616()) {
sunxi_gpio_set_cfgpin(SUNXI_GPH(0), SUN50I_H616_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(1), SUN50I_H616_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(1), SUNXI_GPIO_PULL_UP);
} else if (soc_is_r329()) {
sunxi_gpio_set_cfgpin(SUNXI_GPB(4), SUN50I_R329_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(5), SUN50I_R329_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(5), SUNXI_GPIO_PULL_UP);
} else if (soc_is_v3s()) {
sunxi_gpio_set_cfgpin(SUNXI_GPB(8), SUN8I_V3S_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_V3S_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(9), SUNXI_GPIO_PULL_UP);
} else if (soc_is_v831()) {
sunxi_gpio_set_cfgpin(SUNXI_GPH(9), SUN8I_V831_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(10), SUN8I_V831_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(10), SUNXI_GPIO_PULL_UP);
} else if (soc_is_v853()) {
sunxi_gpio_set_cfgpin(SUNXI_GPH(9), SUN8I_V853_GPH_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPH(10), SUN8I_V853_GPH_UART0);
sunxi_gpio_set_pull(SUNXI_GPH(10), SUNXI_GPIO_PULL_UP);
} else if (soc_is_r528()) {
sunxi_gpio_set_cfgpin(SUNXI_GPE(2), SUN8I_R528_GPE_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPE(3), SUN8I_R528_GPE_UART0);
sunxi_gpio_set_pull(SUNXI_GPE(3), SUNXI_GPIO_PULL_UP);
} else if (soc_is_v5()) {
sunxi_gpio_set_cfgpin(SUNXI_GPB(9), SUN8I_V5_GPB_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPB(10), SUN8I_V5_GPB_UART0);
sunxi_gpio_set_pull(SUNXI_GPB(10), SUNXI_GPIO_PULL_UP);
} else if (soc_is_suniv()) {
sunxi_gpio_set_cfgpin(SUNXI_GPE(0), SUNIV_GPE_UART0);
sunxi_gpio_set_cfgpin(SUNXI_GPE(1), SUNIV_GPE_UART0);
sunxi_gpio_set_pull(SUNXI_GPE(1), SUNXI_GPIO_PULL_UP);
} else {
/* Unknown SoC */
while (1) {}
}
}
/*****************************************************************************/
static u32 uart0_base;
#define UART0_RBR (uart0_base + 0x0) /* receive buffer register */
#define UART0_THR (uart0_base + 0x0) /* transmit holding register */
#define UART0_DLL (uart0_base + 0x0) /* divisor latch low register */
#define UART0_DLH (uart0_base + 0x4) /* divisor latch high register */
#define UART0_IER (uart0_base + 0x4) /* interrupt enable reigster */
#define UART0_IIR (uart0_base + 0x8) /* interrupt identity register */
#define UART0_FCR (uart0_base + 0x8) /* fifo control register */
#define UART0_LCR (uart0_base + 0xc) /* line control register */
#define UART0_LSR (uart0_base + 0x14) /* line status register */
#define BAUD_115200 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)
void uart0_init(void)
{
clock_init_uart();
/* select dll dlh */
writel(0x80, UART0_LCR);
/* set baudrate */
writel(0, UART0_DLH);
if (soc_is_suniv())
writel(BAUD_115200_SUNIV, UART0_DLL);
else
writel(BAUD_115200, UART0_DLL);
/* set line control */
writel(LC_8_N_1, UART0_LCR);
}
void uart0_putc(char c)
{
while (!(readl(UART0_LSR) & (1 << 6))) {}
writel(c, UART0_THR);
}
void uart0_puts(const char *s)
{
while (*s) {
if (*s == '\n')
uart0_putc('\r');
uart0_putc(*s++);
}
}
/*****************************************************************************/
/* A workaround for https://patchwork.ozlabs.org/patch/622173 */
void __attribute__((section(".start"))) __attribute__((naked)) start(void)
{
asm volatile("b main \n"
".long 0xffffffff \n"
".long 0xffffffff \n"
".long 0xffffffff \n");
}
enum { BOOT_DEVICE_UNK, BOOT_DEVICE_FEL, BOOT_DEVICE_MMC0, BOOT_DEVICE_SPI };
int get_boot_device(void)
{
u32 *spl_signature = (void *)0x4;
if (soc_is_a64() || soc_is_a80() || soc_is_h5())
spl_signature = (void *)0x10004;
if (soc_is_h6() || soc_is_v831() || soc_is_h616() || soc_is_v853() ||
soc_is_a63())
spl_signature = (void *)0x20004;
if (soc_is_r329())
spl_signature = (void *)0x100004;
/* Check the eGON.BT0 magic in the SPL header */
if (spl_signature[0] != 0x4E4F4765 || spl_signature[1] != 0x3054422E)
return BOOT_DEVICE_FEL;
u32 boot_dev = spl_signature[9] & 0xFF; /* offset into SPL = 0x28 */
if (boot_dev == 0)
return BOOT_DEVICE_MMC0;
if (boot_dev == 3)
return BOOT_DEVICE_SPI;
return BOOT_DEVICE_UNK;
}
void bases_init(void)
{
if (soc_is_h6() || soc_is_v831() || soc_is_h616() || soc_is_v5() ||
soc_is_a63()) {
pio_base = H6_PIO_BASE;
uart0_base = H6_UART0_BASE;
} else if (soc_is_r329()) {
pio_base = R329_PIO_BASE;
uart0_base = R329_UART0_BASE;
} else if (soc_is_v853() || soc_is_r528()) {
pio_base = V853_PIO_BASE;
uart0_base = R329_UART0_BASE;
} else if (soc_is_suniv()) {
pio_base = SUNXI_PIO_BASE;
uart0_base = SUNIV_UART0_BASE;
} else {
pio_base = SUNXI_PIO_BASE;
uart0_base = SUNXI_UART0_BASE;
}
}
int main(void)
{
soc_detection_init();
bases_init();
gpio_init();
uart0_init();
uart0_puts("\nHello from ");
if (soc_is_a10())
uart0_puts("Allwinner A10!\n");
else if (soc_is_a10s())
uart0_puts("Allwinner A10s!\n");
else if (soc_is_a13())
uart0_puts("Allwinner A13!\n");
else if (soc_is_a20())
uart0_puts("Allwinner A20!\n");
else if (soc_is_a31())
uart0_puts("Allwinner A31/A31s!\n");
else if (soc_is_a64())
uart0_puts("Allwinner A64!\n");
else if (soc_is_h2_plus())
uart0_puts("Allwinner H2+!\n");
else if (soc_is_h3())
uart0_puts("Allwinner H3!\n");
else if (soc_is_h5())
uart0_puts("Allwinner H5!\n");
else if (soc_is_a63())
uart0_puts("Allwinner A63!\n");
else if (soc_is_h6())
uart0_puts("Allwinner H6!\n");
else if (soc_is_h616())
uart0_puts("Allwinner H616!\n");
else if (soc_is_r329())
uart0_puts("Allwinner R329!\n");
else if (soc_is_r40())
uart0_puts("Allwinner R40!\n");
else if (soc_is_v3s())
uart0_puts("Allwinner V3s!\n");
else if (soc_is_v831())
uart0_puts("Allwinner V831!\n");
else if (soc_is_v853())
uart0_puts("Allwinner V853!\n");
else if (soc_is_r528())
uart0_puts("Allwinner R528/T113!\n");
else if (soc_is_v5())
uart0_puts("Allwinner V5!\n");
else if (soc_is_suniv())
uart0_puts("Allwinner F1C100s!\n");
else
uart0_puts("unknown Allwinner SoC!\n");
switch (get_boot_device()) {
case BOOT_DEVICE_FEL:
uart0_puts("Returning back to FEL.\n");
return 0;
case BOOT_DEVICE_MMC0:
uart0_puts("Booted from MMC0, entering an infinite loop.\n");
while (1) {}
case BOOT_DEVICE_SPI:
uart0_puts("Booted from SPI0, entering an infinite loop.\n");
while (1) {}
default:
uart0_puts("Booted from unknown media, entering an infinite loop.\n");
while (1) {}
};
return 0;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
SECTIONS
{
. = 0x0000;
.start : { *(.start) }
.text : { *(.text) }
/DISCARD/ : { *(.dynstr*) }
/DISCARD/ : { *(.dynamic*) }
/DISCARD/ : { *(.plt*) }
/DISCARD/ : { *(.interp*) }
/DISCARD/ : { *(.gnu*) }
/DISCARD/ : { *(.note*) }
}