Browse Source

Merge git://git.denx.de/u-boot-sunxi

trini: Make Kconfig SPL_xxx entires only show if SPL, so that we don't
get Kconfig errors on platforms without SPL, ie sandbox (without SPL).

Signed-off-by: Tom Rini <trini@konsulko.com>
tags/2020-06-01
Tom Rini 4 years ago
parent
commit
711391131c
18 changed files with 714 additions and 224 deletions
  1. +25
    -1
      Kconfig
  2. +20
    -0
      Makefile
  3. +16
    -3
      arch/arm/include/asm/arch-sunxi/spl.h
  4. +3
    -1
      arch/arm/lib/Makefile
  5. +4
    -0
      arch/arm/mach-sunxi/Kconfig
  6. +5
    -1
      board/sunxi/MAINTAINERS
  7. +0
    -97
      board/sunxi/README.pine64
  8. +165
    -0
      board/sunxi/README.sunxi64
  9. +32
    -4
      board/sunxi/board.c
  10. +75
    -0
      board/sunxi/mksunxi_fit_atf.sh
  11. +196
    -105
      common/spl/spl_fit.c
  12. +0
    -1
      configs/am335x_evm_defconfig
  13. +1
    -0
      configs/pine64_plus_defconfig
  14. +21
    -0
      doc/uImage.FIT/howto.txt
  15. +89
    -0
      doc/uImage.FIT/multi_spl.its
  16. +13
    -4
      include/configs/sunxi-common.h
  17. +2
    -1
      scripts/Makefile.spl
  18. +47
    -6
      tools/mksunxiboot.c

+ 25
- 1
Kconfig View File

@@ -220,18 +220,22 @@ config FIT_IMAGE_POST_PROCESS
injected into the FIT creation (i.e. the blobs would have been pre-
processed before being added to the FIT image).

if SPL

config SPL_FIT
bool "Support Flattened Image Tree within SPL"
depends on SPL
select SPL_OF_LIBFDT

config SPL_FIT_SIGNATURE
bool "Enable signature verification of FIT firmware within SPL"
depends on SPL_FIT
depends on SPL_DM
select SPL_FIT
select SPL_RSA

config SPL_LOAD_FIT
bool "Enable SPL loading U-Boot as a FIT"
select SPL_FIT
help
Normally with the SPL framework a legacy image is generated as part
of the build. This contains U-Boot along with information as to
@@ -254,6 +258,26 @@ config SPL_FIT_IMAGE_POST_PROCESS
injected into the FIT creation (i.e. the blobs would have been pre-
processed before being added to the FIT image).

config SPL_FIT_SOURCE
string ".its source file for U-Boot FIT image"
depends on SPL_FIT
help
Specifies a (platform specific) FIT source file to generate the
U-Boot FIT image. This could specify further image to load and/or
execute.

config SPL_FIT_GENERATOR
string ".its file generator script for U-Boot FIT image"
depends on SPL_FIT
default "board/sunxi/mksunxi_fit_atf.sh" if SPL_LOAD_FIT && ARCH_SUNXI
help
Specifies a (platform specific) script file to generate the FIT
source file used to build the U-Boot FIT image file. This gets
passed a list of supported device tree file stub names to
include in the generated image.

endif # SPL

endif # FIT

config OF_BOARD_SETUP


+ 20
- 0
Makefile View File

@@ -833,6 +833,10 @@ quiet_cmd_mkimage = MKIMAGE $@
cmd_mkimage = $(objtree)/tools/mkimage $(MKIMAGEFLAGS_$(@F)) -d $< $@ \
$(if $(KBUILD_VERBOSE:1=), >$(MKIMAGEOUTPUT))

quiet_cmd_mkfitimage = MKIMAGE $@
cmd_mkfitimage = $(objtree)/tools/mkimage $(MKIMAGEFLAGS_$(@F)) -f $(U_BOOT_ITS) -E $@ \
$(if $(KBUILD_VERBOSE:1=), >$(MKIMAGEOUTPUT))

quiet_cmd_cat = CAT $@
cmd_cat = cat $(filter-out $(PHONY), $^) > $@

@@ -952,6 +956,19 @@ quiet_cmd_cpp_cfg = CFG $@
cmd_cpp_cfg = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) -ansi \
-DDO_DEPS_ONLY -D__ASSEMBLY__ -x assembler-with-cpp -P -dM -E -o $@ $<

# Boards with more complex image requirments can provide an .its source file
# or a generator script
ifneq ($(CONFIG_SPL_FIT_SOURCE),"")
U_BOOT_ITS = $(subst ",,$(CONFIG_SPL_FIT_SOURCE))
else
ifneq ($(CONFIG_SPL_FIT_GENERATOR),"")
U_BOOT_ITS := u-boot.its
$(U_BOOT_ITS): FORCE
$(srctree)/$(CONFIG_SPL_FIT_GENERATOR) \
$(patsubst %,arch/$(ARCH)/dts/%.dtb,$(subst ",,$(CONFIG_OF_LIST))) > $@
endif
endif

ifdef CONFIG_SPL_LOAD_FIT
MKIMAGEFLAGS_u-boot.img = -f auto -A $(ARCH) -T firmware -C none -O u-boot \
-a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_UBOOT_START) \
@@ -984,6 +1001,9 @@ u-boot-dtb.img u-boot.img u-boot.kwb u-boot.pbl u-boot-ivt.img: \
$(if $(CONFIG_SPL_LOAD_FIT),u-boot-nodtb.bin dts/dt.dtb,u-boot.bin) FORCE
$(call if_changed,mkimage)

u-boot.itb: u-boot-nodtb.bin dts/dt.dtb $(U_BOOT_ITS) FORCE
$(call if_changed,mkfitimage)

u-boot-spl.kwb: u-boot.img spl/u-boot-spl.bin FORCE
$(call if_changed,mkimage)



+ 16
- 3
arch/arm/include/asm/arch-sunxi/spl.h View File

@@ -10,7 +10,7 @@

#define BOOT0_MAGIC "eGON.BT0"
#define SPL_SIGNATURE "SPL" /* marks "sunxi" SPL header */
#define SPL_HEADER_VERSION 1
#define SPL_HEADER_VERSION 2

#ifdef CONFIG_SUNXI_HIGH_SRAM
#define SPL_ADDR 0x10000
@@ -58,11 +58,24 @@ struct boot_file_head {
* compatible format, ready to be imported via "env import -t".
*/
uint32_t fel_uEnv_length;
uint32_t reserved1[2];
/*
* Offset of an ASCIIZ string (relative to the SPL header), which
* contains the default device tree name (CONFIG_DEFAULT_DEVICE_TREE).
* This is optional and may be set to NULL. Is intended to be used
* by flash programming tools for providing nice informative messages
* to the users.
*/
uint32_t dt_name_offset;
uint32_t reserved1;
uint32_t boot_media; /* written here by the boot ROM */
uint32_t reserved2[5]; /* padding, align to 64 bytes */
/* A padding area (may be used for storing text strings) */
uint32_t string_pool[13];
/* The header must be a multiple of 32 bytes (for VBAR alignment) */
};

/* Compile time check to assure proper alignment of structure */
typedef char boot_file_head_not_multiple_of_32[1 - 2*(sizeof(struct boot_file_head) % 32)];

#define is_boot0_magic(addr) (memcmp((void *)addr, BOOT0_MAGIC, 8) == 0)

#endif

+ 3
- 1
arch/arm/lib/Makefile View File

@@ -44,8 +44,10 @@ obj-y += stack.o
ifdef CONFIG_CPU_V7M
obj-y += interrupts_m.o
else ifdef CONFIG_ARM64
obj-y += ccn504.o
obj-$(CONFIG_FSL_LAYERSCAPE) += ccn504.o
ifneq ($(CONFIG_GICV2)$(CONFIG_GICV3),)
obj-y += gic_64.o
endif
obj-y += interrupts_64.o
else
obj-y += interrupts.o


+ 4
- 0
arch/arm/mach-sunxi/Kconfig View File

@@ -143,12 +143,16 @@ config MACH_SUN50I
select SUNXI_GEN_SUN6I
select SUNXI_HIGH_SRAM
select SUPPORT_SPL
select FIT
select SPL_LOAD_FIT

config MACH_SUN50I_H5
bool "sun50i (Allwinner H5)"
select ARM64
select MACH_SUNXI_H3_H5
select SUNXI_HIGH_SRAM
select FIT
select SPL_LOAD_FIT

endchoice



+ 5
- 1
board/sunxi/MAINTAINERS View File

@@ -72,7 +72,6 @@ F: configs/q8_a33_tablet_1024x600_defconfig
F: include/configs/sun9i.h
F: configs/Merrii_A80_Optimus_defconfig
F: include/configs/sun50i.h
F: configs/pine64_plus_defconfig

A20-OLIMEX-SOM-EVB BOARD
M: Marcus Cooper <codekipper@gmail.com>
@@ -263,6 +262,11 @@ M: Andre Przywara <andre.przywara@arm.com>
S: Maintained
F: configs/orangepi_pc2_defconfig

PINE64 BOARDS
M: Andre Przywara <andre.przywara@arm.com>
S: Maintained
F: configs/pine64_plus_defconfig

R16 EVB PARROT BOARD
M: Quentin Schulz <quentin.schulz@free-electrons.com>
S: Maintained


+ 0
- 97
board/sunxi/README.pine64 View File

@@ -1,98 +0,0 @@
Pine64 board README
====================

The Pine64(+) is a single board computer equipped with an AArch64 capable ARMv8
compliant Allwinner A64 SoC.
This chip has ARM Cortex A-53 cores and thus can run both in AArch32
(compatible to 32-bit ARMv7) and AArch64 modes. Upon reset the SoC starts
in AArch32 mode and executes 32-bit code from the Boot ROM (BROM).
This has some implications on U-Boot.

Quick start
============
- Get hold of a boot0.img file (see below for more details).
- Get the boot0img tool source from the tools directory in [1] and compile
that on your host.
- Build U-Boot:
$ export CROSS_COMPILE=aarch64-linux-gnu-
$ make pine64_plus_defconfig
$ make
- You also need a compiled ARM Trusted Firmware (ATF) binary. Checkout the
"allwinner" branch from the github repository [2] and build it:
$ export CROSS_COMPILE=aarch64-linux-gnu-
$ make PLAT=sun50iw1p1 DEBUG=1 bl31
The resulting binary is build/sun50iw1p1/debug/bl31.bin.

Now put an empty (or disposable) micro SD card in your card reader and learn
its device file name, replacing /dev/sd<x> below with the result (that could
be /dev/mmcblk<x> as well):

$ ./boot0img --device /dev/sd<x> -e -u u-boot.bin -B boot0.img \
-d trampoline64:0x44000 -s bl31.bin -a 0x44008 -p 100
(either copying the respective files to the working directory or specifying
the paths directly)

This will create a new partition table (with a 100 MB FAT boot partition),
copies boot0.img, ATF and U-Boot to the proper locations on the SD card and
will fill in the magic Allwinner header to be recognized by boot0.
Prefix the above call with "sudo" if you don't have write access to the
uSD card. You can also use "-o output.img" instead of "--device /dev/sd<x>"
to create an image file and "dd" that to the uSD card.
Omitting the "-p" option will skip the partition table.

Now put this uSD card in the board and power it on. You should be greeted by
the U-Boot prompt.


Main U-Boot
============
The main U-Boot proper is a real 64-bit ARMv8 port and runs entirely in the
64-bit AArch64 mode. It can load any AArch64 code, EFI applications or arm64
Linux kernel images (often named "Image") using the booti command.
Launching 32-bit code and kernels is technically possible, though not without
drawbacks (or hacks to avoid them) and currently not implemented.

SPL support
============
The main task of the SPL support is to bring up the DRAM controller and make
DRAM actually accessible. At the moment there is no documentation or source
code available which would do this.
There are currently two ways to overcome this situation: using a tainted 32-bit
SPL (involving some hacks and resulting in a non-redistributable binary, thus
not described here) or using the Allwinner boot0 blob.

boot0 method
boot0 is Allwiner's secondary program loader and it can be used as some kind
of SPL replacement to get U-Boot up and running.
The binary is a 32 KByte blob and contained on every Pine64 image distributed
so far. It can be easily extracted from a micro SD card or an image file:
# dd if=/dev/sd<x> of=boot0.bin bs=8k skip=1 count=4
where /dev/sd<x> is the device name of the uSD card or the name of the image
file. Apparently Allwinner allows re-distribution of this proprietary code
as-is.
For the time being this boot0 blob is the only redistributable way of making
U-Boot work on the Pine64. Beside loading the various parts of the (original)
firmware it also switches the core into AArch64 mode.
The original boot0 code looks for U-Boot at a certain place on an uSD card
(at 19096 KB), also it expects a header with magic bytes and a checksum.
There is a tool called boot0img[1] which takes a boot0.bin image and a compiled
U-Boot binary (plus other binaries) and will populate that header accordingly.
To make space for the magic header, the pine64_plus_defconfig will make sure
there is sufficient space at the beginning of the U-Boot binary.
boot0img will also take care of putting the different binaries at the right
places on the uSD card and works around unused, but mandatory parts by using
trampoline code. See the output of "boot0img -h" for more information.
boot0img can also patch boot0 to avoid loading U-Boot from 19MB, instead
fetching it from just behind the boot0 binary (-B option).

FEL boot
=========
FEL is the name of the Allwinner defined USB boot protocol built-in the
mask ROM of most Allwinner SoCs. It allows to bootstrap a board solely
by using the USB-OTG interface and a host port on another computer.
Since FEL boot does not work with boot0, it requires the libdram hack, which
is not described here.

[1] https://github.com/apritzel/pine64/
[2] https://github.com/apritzel/arm-trusted-firmware.git

+ 165
- 0
board/sunxi/README.sunxi64 View File

@@ -0,0 +1,165 @@
Allwinner 64-bit boards README
==============================

Newer Allwinner SoCs feature ARMv8 cores (ARM Cortex-A53) with support for
both the 64-bit AArch64 mode and the ARMv7 compatible 32-bit AArch32 mode.
Examples are the Allwinner A64 (used for instance on the Pine64 board) or
the Allwinner H5 SoC (as used on the OrangePi PC 2).
These SoCs are wired to start in AArch32 mode on reset and execute 32-bit
code from the Boot ROM (BROM). As this has some implications on U-Boot, this
file describes how to make full use of the 64-bit capabilities.

Quick Start / Overview
======================
- Build the ARM Trusted Firmware binary (see "ARM Trusted Firmware (ATF)" below)
- Build U-Boot (see "SPL/U-Boot" below)
- Transfer to an uSD card (see "microSD card" below)
- Boot and enjoy!

Building the firmware
=====================

The Allwinner A64/H5 firmware consists of three parts: U-Boot's SPL, an
ARM Trusted Firmware (ATF) build and the U-Boot proper.
The SPL will load both ATF and U-Boot proper along with the right device
tree blob (.dtb) and will pass execution to ATF (in EL3), which in turn will
drop into the U-Boot proper (in EL2).
As the ATF binary will become part of the U-Boot image file, you will need
to build it first.

ARM Trusted Firmware (ATF)
----------------------------
Checkout the "allwinner" branch from the github repository [1] and build it:
$ export CROSS_COMPILE=aarch64-linux-gnu-
$ make PLAT=sun50iw1p1 DEBUG=1 bl31
The resulting binary is build/sun50iw1p1/debug/bl31.bin. Either put the
location of this file into the BL31 environment variable or copy this to
the root of your U-Boot build directory (or create a symbolic link).
$ export BL31=/src/arm-trusted-firmware/build/sun50iw1p1/debug/bl31.bin
(adjust the actual path accordingly)

SPL/U-Boot
------------
Both U-Boot proper and the SPL are using the 64-bit mode. As the boot ROM
enters the SPL still in AArch32 secure SVC mode, there is some shim code to
enter AArch64 very early. The rest of the SPL runs in AArch64 EL3.
U-Boot proper runs in EL2 and can load any AArch64 code (using the "go"
command), EFI applications (with "bootefi") or arm64 Linux kernel images
(often named "Image"), using the "booti" command.

$ make clean
$ export CROSS_COMPILE=aarch64-linux-gnu-
$ make pine64_plus_defconfig
$ make

This will build the SPL in spl/sunxi-spl.bin and a FIT image called u-boot.itb,
which contains the rest of the firmware.


Boot process
============
The on-die BROM code will try several methods to load and execute the firmware.
On a typical board like the Pine64 this will result in the following boot order:

1) Reading 32KB from sector 16 (@8K) of the microSD card to SRAM A1. If the
BROM finds the magic "eGON" header in the first bytes, it will execute that
code. If not (no SD card at all or invalid magic), it will:
2) Try to read 32KB from sector 16 (@8K) of memory connected to the MMC2
controller, typically an on-board eMMC chip. If there is no eMMC or it does
not contain a valid boot header, it will:
3) Initialize the SPI0 controller and try to access a NOR flash connected to
it (using the CS0 pin). If a flash chip is found, the BROM will load the
first 32KB (from offset 0) into SRAM A1. Now it checks for the magic eGON
header and checksum and will execute the code upon finding it. If not, it will:
4) Initialize the USB OTG controller and will wait for a host to connect to
it, speaking the Allwinner proprietary (but deciphered) "FEL" USB protocol.


To boot the Pine64 board, you can use U-Boot and any of the described methods.

FEL boot (USB OTG)
------------------
FEL is the name of the Allwinner defined USB boot protocol built in the
mask ROM of most Allwinner SoCs. It allows to bootstrap a board solely
by using the USB-OTG interface and a host port on another computer.
As the FEL mode is controlled by the boot ROM, it expects to be running in
AArch32. For now the AArch64 SPL cannot properly return into FEL mode, so the
feature is disabled in the configuration at the moment.

microSD card
------------
Transfer the SPL and the U-Boot FIT image directly to an uSD card:
# dd if=spl/sunxi-spl.bin of=/dev/sdx bs=8k seek=1
# dd if=u-boot.itb of=/dev/sdx bs=8k seek=5
# sync
(replace /dev/sdx with you SD card device file name, which could be
/dev/mmcblk[x] as well).

Alternatively you can concatenate the SPL and the U-Boot FIT image into a
single file and transfer that instead:
$ cat spl/sunxi-spl.bin u-boot.itb > u-boot-sunxi-with-spl.bin
# dd if=u-boot-sunxi-with-spl.bin of=/dev/sdx bs=8k seek=1

You can partition the microSD card, but leave the first MB unallocated (most
partitioning tools will do this anyway).

NOR flash
---------
Some boards (like the SoPine, Pinebook or the OrangePi PC2) come with a
soldered SPI NOR flash chip. On other boards like the Pine64 such a chip
can be connected to the SPI0/CS0 pins on the PI-2 headers.
Create the SPL and FIT image like described above for the SD card.
Now connect either an "A to A" USB cable to the upper USB port on the Pine64
or get an adaptor and use a regular A-microB cable connected to it. Other
boards often have a proper micro-B USB socket connected to the USB OTB port.
Remove a microSD card from the slot and power on the board.
On your host computer download and build the sunxi-tools package[2], then
use "sunxi-fel" to access the board:
$ ./sunxi-fel ver -v -p
This should give you an output starting with: AWUSBFEX soc=00001689(A64) ...
Now use the sunxi-fel tool to write to the NOR flash:
$ ./sunxi-fel spiflash-write 0 spl/sunxi-spl.bin
$ ./sunxi-fel spiflash-write 32768 u-boot.itb
Now boot the board without an SD card inserted and you should see the
U-Boot prompt on the serial console.

(Legacy) boot0 method
---------------------
boot0 is Allwiner's secondary program loader and it can be used as some kind
of SPL replacement to get U-Boot up and running from an microSD card.
For some time using boot0 was the only option to get the Pine64 booted.
With working DRAM init code in U-Boot's SPL this is no longer necessary,
but this method is described here for the sake of completeness.
Please note that this method works only with the boot0 files shipped with
A64 based boards, the H5 uses an incompatible layout which is not supported
by this method.

The boot0 binary is a 32 KByte blob and contained in the official Pine64 images
distributed by Pine64 or Allwinner. It can be easily extracted from a micro
SD card or an image file:
# dd if=/dev/sd<x> of=boot0.bin bs=8k skip=1 count=4
where /dev/sd<x> is the device name of the uSD card or the name of the image
file. Apparently Allwinner allows re-distribution of this proprietary code
"as-is".
This boot0 blob takes care of DRAM initialisation and loads the remaining
firmware parts, then switches the core into AArch64 mode.
The original boot0 code looks for U-Boot at a certain place on an uSD card
(at 19096 KB), also it expects a header with magic bytes and a checksum.
There is a tool called boot0img[3] which takes a boot0.bin image and a compiled
U-Boot binary (plus other binaries) and will populate that header accordingly.
To make space for the magic header, the pine64_plus_defconfig will make sure
there is sufficient space at the beginning of the U-Boot binary.
boot0img will also take care of putting the different binaries at the right
places on the uSD card and works around unused, but mandatory parts by using
trampoline code. See the output of "boot0img -h" for more information.
boot0img can also patch boot0 to avoid loading U-Boot from 19MB, instead
fetching it from just behind the boot0 binary (-B option).
$ ./boot0img -o firmware.img -B boot0.img -u u-boot-dtb.bin -e -s bl31.bin \
-a 0x44008 -d trampoline64:0x44000
Then write this image to a microSD card, replacing /dev/sdx with the right
device file (see above):
$ dd if=firmware.img of=/dev/sdx bs=8k seek=1

[1] https://github.com/apritzel/arm-trusted-firmware.git
[2] git://github.com/linux-sunxi/sunxi-tools.git
[3] https://github.com/apritzel/pine64/

+ 32
- 4
board/sunxi/board.c View File

@@ -512,7 +512,6 @@ int board_mmc_init(bd_t *bis)
void sunxi_board_init(void)
{
int power_failed = 0;
unsigned long ramsize;

#ifdef CONFIG_SY8106A_POWER
power_failed = sy8106a_set_vout1(CONFIG_SY8106A_VOUT1_VOLT);
@@ -573,9 +572,9 @@ void sunxi_board_init(void)
#endif
#endif
printf("DRAM:");
ramsize = sunxi_dram_init();
printf(" %d MiB\n", (int)(ramsize >> 20));
if (!ramsize)
gd->ram_size = sunxi_dram_init();
printf(" %d MiB\n", (int)(gd->ram_size >> 20));
if (!gd->ram_size)
hang();

/*
@@ -758,3 +757,32 @@ int ft_board_setup(void *blob, bd_t *bd)
#endif
return 0;
}

#ifdef CONFIG_SPL_LOAD_FIT
int board_fit_config_name_match(const char *name)
{
struct boot_file_head *spl = (void *)(ulong)SPL_ADDR;
const char *cmp_str = (void *)(ulong)SPL_ADDR;

/* Check if there is a DT name stored in the SPL header and use that. */
if (spl->dt_name_offset) {
cmp_str += spl->dt_name_offset;
} else {
#ifdef CONFIG_DEFAULT_DEVICE_TREE
cmp_str = CONFIG_DEFAULT_DEVICE_TREE;
#else
return 0;
#endif
};

/* Differentiate the two Pine64 board DTs by their DRAM size. */
if (strstr(name, "-pine64") && strstr(cmp_str, "-pine64")) {
if ((gd->ram_size > 512 * 1024 * 1024))
return !strstr(name, "plus");
else
return !!strstr(name, "plus");
} else {
return strcmp(name, cmp_str);
}
}
#endif

+ 75
- 0
board/sunxi/mksunxi_fit_atf.sh View File

@@ -0,0 +1,75 @@
#!/bin/sh
#
# script to generate FIT image source for 64-bit sunxi boards with
# ARM Trusted Firmware and multiple device trees (given on the command line)
#
# usage: $0 <dt_name> [<dt_name> [<dt_name] ...]

[ -z "$BL31" ] && BL31="bl31.bin"

cat << __HEADER_EOF
/dts-v1/;

/ {
description = "Configuration to load ATF before U-Boot";
#address-cells = <1>;

images {
uboot@1 {
description = "U-Boot (64-bit)";
data = /incbin/("u-boot-nodtb.bin");
type = "standalone";
arch = "arm64";
compression = "none";
load = <0x4a000000>;
};
atf@1 {
description = "ARM Trusted Firmware";
data = /incbin/("$BL31");
type = "firmware";
arch = "arm64";
compression = "none";
load = <0x44000>;
entry = <0x44000>;
};
__HEADER_EOF

cnt=1
for dtname in $*
do
cat << __FDT_IMAGE_EOF
fdt@$cnt {
description = "$(basename $dtname .dtb)";
data = /incbin/("$dtname");
type = "flat_dt";
compression = "none";
};
__FDT_IMAGE_EOF
cnt=$((cnt+1))
done

cat << __CONF_HEADER_EOF
};
configurations {
default = "config@1";

__CONF_HEADER_EOF

cnt=1
for dtname in $*
do
cat << __CONF_SECTION_EOF
config@$cnt {
description = "$(basename $dtname .dtb)";
firmware = "uboot@1";
loadables = "atf@1";
fdt = "fdt@$cnt";
};
__CONF_SECTION_EOF
cnt=$((cnt+1))
done

cat << __ITS_EOF
};
};
__ITS_EOF

+ 196
- 105
common/spl/spl_fit.c View File

@@ -11,24 +11,30 @@
#include <libfdt.h>
#include <spl.h>

#define FDT_ERROR ((ulong)(-1))

static ulong fdt_getprop_u32(const void *fdt, int node, const char *prop)
{
const u32 *cell;
int len;

cell = fdt_getprop(fdt, node, prop, &len);
if (len != sizeof(*cell))
return -1U;
if (!cell || len != sizeof(*cell))
return FDT_ERROR;

return fdt32_to_cpu(*cell);
}

static int spl_fit_select_fdt(const void *fdt, int images, int *fdt_offsetp)
/*
* Iterate over all /configurations subnodes and call a platform specific
* function to find the matching configuration.
* Returns the node offset or a negative error number.
*/
static int spl_fit_find_config_node(const void *fdt)
{
const char *name, *fdt_name;
int conf, node, fdt_node;
int len;
const char *name;
int conf, node, len;

*fdt_offsetp = 0;
conf = fdt_path_offset(fdt, FIT_CONFS_PATH);
if (conf < 0) {
debug("%s: Cannot find /configurations node: %d\n", __func__,
@@ -50,39 +56,69 @@ static int spl_fit_select_fdt(const void *fdt, int images, int *fdt_offsetp)
continue;

debug("Selecting config '%s'", name);
fdt_name = fdt_getprop(fdt, node, FIT_FDT_PROP, &len);
if (!fdt_name) {
debug("%s: Cannot find fdt name property: %d\n",
__func__, len);
return -EINVAL;
}

debug(", fdt '%s'\n", fdt_name);
fdt_node = fdt_subnode_offset(fdt, images, fdt_name);
if (fdt_node < 0) {
debug("%s: Cannot find fdt node '%s': %d\n",
__func__, fdt_name, fdt_node);
return -EINVAL;
return node;
}

return -ENOENT;
}

/**
* spl_fit_get_image_node(): By using the matching configuration subnode,
* retrieve the name of an image, specified by a property name and an index
* into that.
* @fit: Pointer to the FDT blob.
* @images: Offset of the /images subnode.
* @type: Name of the property within the configuration subnode.
* @index: Index into the list of strings in this property.
*
* Return: the node offset of the respective image node or a negative
* error number.
*/
static int spl_fit_get_image_node(const void *fit, int images,
const char *type, int index)
{
const char *name, *str;
int node, conf_node;
int len, i;

conf_node = spl_fit_find_config_node(fit);
if (conf_node < 0) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
printf("No matching DT out of these options:\n");
for (node = fdt_first_subnode(fit, conf_node);
node >= 0;
node = fdt_next_subnode(fit, node)) {
name = fdt_getprop(fit, node, "description", &len);
printf(" %s\n", name);
}
#endif
return conf_node;
}

*fdt_offsetp = fdt_getprop_u32(fdt, fdt_node, "data-offset");
len = fdt_getprop_u32(fdt, fdt_node, "data-size");
debug("FIT: Selected '%s'\n", name);
name = fdt_getprop(fit, conf_node, type, &len);
if (!name) {
debug("cannot find property '%s': %d\n", type, len);
return -EINVAL;
}

return len;
str = name;
for (i = 0; i < index; i++) {
str = strchr(str, '\0') + 1;
if (!str || (str - name >= len)) {
debug("no string for index %d\n", index);
return -E2BIG;
}
}

#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
printf("No matching DT out of these options:\n");
for (node = fdt_first_subnode(fdt, conf);
node >= 0;
node = fdt_next_subnode(fdt, node)) {
name = fdt_getprop(fdt, node, "description", &len);
printf(" %s\n", name);
debug("%s: '%s'\n", type, str);
node = fdt_subnode_offset(fit, images, str);
if (node < 0) {
debug("cannot find image node '%s': %d\n", str, node);
return -EINVAL;
}
#endif

return -ENOENT;
return node;
}

static int get_aligned_image_offset(struct spl_load_info *info, int offset)
@@ -123,19 +159,82 @@ static int get_aligned_image_size(struct spl_load_info *info, int data_size,
return (data_size + info->bl_len - 1) / info->bl_len;
}

/**
* spl_load_fit_image(): load the image described in a certain FIT node
* @info: points to information about the device to load data from
* @sector: the start sector of the FIT image on the device
* @fit: points to the flattened device tree blob describing the FIT
* image
* @base_offset: the beginning of the data area containing the actual
* image data, relative to the beginning of the FIT
* @node: offset of the DT node describing the image to load (relative
* to @fit)
* @image_info: will be filled with information about the loaded image
* If the FIT node does not contain a "load" (address) property,
* the image gets loaded to the address pointed to by the
* load_addr member in this struct.
*
* Return: 0 on success or a negative error number.
*/
static int spl_load_fit_image(struct spl_load_info *info, ulong sector,
void *fit, ulong base_offset, int node,
struct spl_image_info *image_info)
{
ulong offset;
size_t length;
ulong load_addr, load_ptr;
void *src;
ulong overhead;
int nr_sectors;
int align_len = ARCH_DMA_MINALIGN - 1;

offset = fdt_getprop_u32(fit, node, "data-offset");
if (offset == FDT_ERROR)
return -ENOENT;
offset += base_offset;
length = fdt_getprop_u32(fit, node, "data-size");
if (length == FDT_ERROR)
return -ENOENT;
load_addr = fdt_getprop_u32(fit, node, "load");
if (load_addr == FDT_ERROR && image_info)
load_addr = image_info->load_addr;
load_ptr = (load_addr + align_len) & ~align_len;

overhead = get_aligned_image_overhead(info, offset);
nr_sectors = get_aligned_image_size(info, length, offset);

if (info->read(info, sector + get_aligned_image_offset(info, offset),
nr_sectors, (void*)load_ptr) != nr_sectors)
return -EIO;
debug("image: dst=%lx, offset=%lx, size=%lx\n", load_ptr, offset,
(unsigned long)length);

src = (void *)load_ptr + overhead;
#ifdef CONFIG_SPL_FIT_IMAGE_POST_PROCESS
board_fit_image_post_process(&src, &length);
#endif

memcpy((void*)load_addr, src, length);

if (image_info) {
image_info->load_addr = load_addr;
image_info->size = length;
image_info->entry_point = fdt_getprop_u32(fit, node, "entry");
}

return 0;
}

int spl_load_simple_fit(struct spl_image_info *spl_image,
struct spl_load_info *info, ulong sector, void *fit)
{
int sectors;
ulong size, load;
ulong size;
unsigned long count;
int node, images;
void *load_ptr;
int fdt_offset, fdt_len;
int data_offset, data_size;
struct spl_image_info image_info;
int node, images, ret;
int base_offset, align_len = ARCH_DMA_MINALIGN - 1;
int src_sector;
void *dst, *src;
int index = 0;

/*
* Figure out where the external images start. This is the base for the
@@ -168,90 +267,82 @@ int spl_load_simple_fit(struct spl_image_info *spl_image,
if (count == 0)
return -EIO;

/* find the firmware image to load */
/* find the node holding the images information */
images = fdt_path_offset(fit, FIT_IMAGES_PATH);
if (images < 0) {
debug("%s: Cannot find /images node: %d\n", __func__, images);
return -1;
}
node = fdt_first_subnode(fit, images);

/* find the U-Boot image */
node = spl_fit_get_image_node(fit, images, "firmware", 0);
if (node < 0) {
debug("could not find firmware image, trying loadables...\n");
node = spl_fit_get_image_node(fit, images, "loadables", 0);
/*
* If we pick the U-Boot image from "loadables", start at
* the second image when later loading additional images.
*/
index = 1;
}
if (node < 0) {
debug("%s: Cannot find first image node: %d\n", __func__, node);
debug("%s: Cannot find u-boot image node: %d\n",
__func__, node);
return -1;
}

/* Get its information and set up the spl_image structure */
data_offset = fdt_getprop_u32(fit, node, "data-offset");
data_size = fdt_getprop_u32(fit, node, "data-size");
load = fdt_getprop_u32(fit, node, "load");
debug("data_offset=%x, data_size=%x\n", data_offset, data_size);
spl_image->load_addr = load;
spl_image->entry_point = load;
spl_image->os = IH_OS_U_BOOT;

/*
* Work out where to place the image. We read it so that the first
* byte will be at 'load'. This may mean we need to load it starting
* before then, since we can only read whole blocks.
*/
data_offset += base_offset;
sectors = get_aligned_image_size(info, data_size, data_offset);
load_ptr = (void *)load;
debug("U-Boot size %x, data %p\n", data_size, load_ptr);
dst = load_ptr;

/* Read the image */
src_sector = sector + get_aligned_image_offset(info, data_offset);
debug("Aligned image read: dst=%p, src_sector=%x, sectors=%x\n",
dst, src_sector, sectors);
count = info->read(info, src_sector, sectors, dst);
if (count != sectors)
return -EIO;
debug("image: dst=%p, data_offset=%x, size=%x\n", dst, data_offset,
data_size);
src = dst + get_aligned_image_overhead(info, data_offset);
/* Load the image and set up the spl_image structure */
ret = spl_load_fit_image(info, sector, fit, base_offset, node,
spl_image);
if (ret)
return ret;

#ifdef CONFIG_SPL_FIT_IMAGE_POST_PROCESS
board_fit_image_post_process((void **)&src, (size_t *)&data_size);
#endif

memcpy(dst, src, data_size);
spl_image->os = IH_OS_U_BOOT;

/* Figure out which device tree the board wants to use */
fdt_len = spl_fit_select_fdt(fit, images, &fdt_offset);
if (fdt_len < 0)
return fdt_len;
node = spl_fit_get_image_node(fit, images, FIT_FDT_PROP, 0);
if (node < 0) {
debug("%s: cannot find FDT node\n", __func__);
return node;
}

/*
* Read the device tree and place it after the image. There may be
* some extra data before it since we can only read entire blocks.
* And also align the destination address to ARCH_DMA_MINALIGN.
* Read the device tree and place it after the image.
* Align the destination address to ARCH_DMA_MINALIGN.
*/
dst = (void *)((load + data_size + align_len) & ~align_len);
fdt_offset += base_offset;
sectors = get_aligned_image_size(info, fdt_len, fdt_offset);
src_sector = sector + get_aligned_image_offset(info, fdt_offset);
count = info->read(info, src_sector, sectors, dst);
debug("Aligned fdt read: dst %p, src_sector = %x, sectors %x\n",
dst, src_sector, sectors);
if (count != sectors)
return -EIO;
image_info.load_addr = spl_image->load_addr + spl_image->size;
ret = spl_load_fit_image(info, sector, fit, base_offset, node,
&image_info);
if (ret < 0)
return ret;

/* Now check if there are more images for us to load */
for (; ; index++) {
node = spl_fit_get_image_node(fit, images, "loadables", index);
if (node < 0)
break;

ret = spl_load_fit_image(info, sector, fit, base_offset, node,
&image_info);
if (ret < 0)
continue;

/*
* If the "firmware" image did not provide an entry point,
* use the first valid entry point from the loadables.
*/
if (spl_image->entry_point == FDT_ERROR &&
image_info.entry_point != FDT_ERROR)
spl_image->entry_point = image_info.entry_point;
}

/*
* Copy the device tree so that it starts immediately after the image.
* After this we will have the U-Boot image and its device tree ready
* for us to start.
* If a platform does not provide CONFIG_SYS_UBOOT_START, U-Boot's
* Makefile will set it to 0 and it will end up as the entry point
* here. What it actually means is: use the load address.
*/
debug("fdt: dst=%p, data_offset=%x, size=%x\n", dst, fdt_offset,
fdt_len);
src = dst + get_aligned_image_overhead(info, fdt_offset);
dst = load_ptr + data_size;

#ifdef CONFIG_SPL_FIT_IMAGE_POST_PROCESS
board_fit_image_post_process((void **)&src, (size_t *)&fdt_len);
#endif

memcpy(dst, src, fdt_len);
if (spl_image->entry_point == FDT_ERROR || spl_image->entry_point == 0)
spl_image->entry_point = spl_image->load_addr;

return 0;
}

+ 0
- 1
configs/am335x_evm_defconfig View File

@@ -48,4 +48,3 @@ CONFIG_G_DNL_MANUFACTURER="Texas Instruments"
CONFIG_G_DNL_VENDOR_NUM=0x0451
CONFIG_G_DNL_PRODUCT_NUM=0xd022
CONFIG_RSA=y
CONFIG_SPL_OF_LIBFDT=y

+ 1
- 0
configs/pine64_plus_defconfig View File

@@ -3,6 +3,7 @@ CONFIG_ARCH_SUNXI=y
CONFIG_MACH_SUN50I=y
CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER=y
CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-pine64-plus"
CONFIG_OF_LIST="sun50i-a64-pine64 sun50i-a64-pine64-plus"
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
CONFIG_CONSOLE_MUX=y
CONFIG_SPL=y


+ 21
- 0
doc/uImage.FIT/howto.txt View File

@@ -44,6 +44,27 @@ image source file mkimage + dtc transfer to target
+ ---------------> image file --------------------> bootm
image data file(s)

SPL usage
---------

The SPL can make use of the new image format as well, this traditionally
is used to ship multiple device tree files within one image. Code in the SPL
will choose the one matching the current board and append this to the
U-Boot proper binary to be automatically used up by it.
Aside from U-Boot proper and one device tree blob the SPL can load multiple,
arbitrary image files as well. These binaries should be specified in their
own subnode under the /images node, which should then be referenced from one or
multiple /configurations subnodes. The required images must be enumerated in
the "loadables" property as a list of strings.

If a platform specific image source file (.its) is shipped with the U-Boot
source, it can be specified using the CONFIG_SPL_FIT_SOURCE Kconfig symbol.
In this case it will be automatically used by U-Boot's Makefile to generate
the image.
If a static source file is not flexible enough, CONFIG_SPL_FIT_GENERATOR
can point to a script which generates this image source file during
the build process. It gets passed a list of device tree files (taken from the
CONFIG_OF_LIST symbol).

Example 1 -- old-style (non-FDT) kernel booting
-----------------------------------------------


+ 89
- 0
doc/uImage.FIT/multi_spl.its View File

@@ -0,0 +1,89 @@
/dts-v1/;

/*
* (Bogus) example FIT image description file demonstrating the usage
* of multiple images loaded by the SPL.
* Several binaries will be loaded at their respective load addresses.
* Finally the one image specifying an entry point will be entered by the SPL.
*/

/ {
description = "multiple firmware blobs and U-Boot, loaded by SPL";
#address-cells = <0x1>;

images {

uboot {
description = "U-Boot (64-bit)";
type = "standalone";
arch = "arm64";
compression = "none";
load = <0x4a000000>;
};

atf {
description = "ARM Trusted Firmware";
type = "firmware";
arch = "arm64";
compression = "none";
load = <0x18000>;
entry = <0x18000>;
};

mgmt-firmware {
description = "arisc management processor firmware";
type = "firmware";
arch = "or1k";
compression = "none";
load = <0x40000>;
};

fdt@1 {
description = "Pine64+ DT";
type = "flat_dt";
compression = "none";
load = <0x4fa00000>;
arch = "arm64";
};

fdt@2 {
description = "Pine64 DT";
type = "flat_dt";
compression = "none";
load = <0x4fa00000>;
arch = "arm64";
};

kernel {
description = "4.7-rc5 kernel";
type = "kernel";
compression = "none";
load = <0x40080000>;
arch = "arm64";
};

initrd {
description = "Debian installer initrd";
type = "ramdisk";
compression = "none";
load = <0x4fe00000>;
arch = "arm64";
};
};

configurations {
default = "config@1";

config@1 {
description = "sun50i-a64-pine64-plus";
loadables = "uboot", "atf", "kernel", "initrd";
fdt = "fdt@1";
};

config@2 {
description = "sun50i-a64-pine64";
loadables = "uboot", "atf", "mgmt-firmware";
fdt = "fdt@2";
};
};
};

+ 13
- 4
include/configs/sunxi-common.h View File

@@ -32,6 +32,10 @@
# define CONFIG_MACH_TYPE_COMPAT_REV 1
#endif

#ifdef CONFIG_ARM64
#define CONFIG_BUILD_TARGET "u-boot.itb"
#endif

/* Serial & console */
#define CONFIG_SYS_NS16550_SERIAL
/* ns16550 reg in the low bits of cpu reg */
@@ -185,12 +189,17 @@
#endif

#ifdef CONFIG_SUNXI_HIGH_SRAM
#define CONFIG_SPL_TEXT_BASE 0x10040 /* sram start+header */
#define CONFIG_SPL_MAX_SIZE 0x7fc0 /* 32 KiB */
#define CONFIG_SPL_TEXT_BASE 0x10060 /* sram start+header */
#define CONFIG_SPL_MAX_SIZE 0x7fa0 /* 32 KiB */
#ifdef CONFIG_ARM64
/* end of SRAM A2 for now, as SRAM A1 is pretty tight for an ARM64 build */
#define LOW_LEVEL_SRAM_STACK 0x00054000
#else
#define LOW_LEVEL_SRAM_STACK 0x00018000
#endif /* !CONFIG_ARM64 */
#else
#define CONFIG_SPL_TEXT_BASE 0x40 /* sram start+header */
#define CONFIG_SPL_MAX_SIZE 0x5fc0 /* 24KB on sun4i/sun7i */
#define CONFIG_SPL_TEXT_BASE 0x60 /* sram start+header */
#define CONFIG_SPL_MAX_SIZE 0x5fa0 /* 24KB on sun4i/sun7i */
#define LOW_LEVEL_SRAM_STACK 0x00008000 /* End of sram */
#endif



+ 2
- 1
scripts/Makefile.spl View File

@@ -304,7 +304,8 @@ $(obj)/$(SPL_BIN).sfp: $(obj)/$(SPL_BIN).bin FORCE
$(call if_changed,mkimage)

quiet_cmd_mksunxiboot = MKSUNXI $@
cmd_mksunxiboot = $(objtree)/tools/mksunxiboot $< $@
cmd_mksunxiboot = $(objtree)/tools/mksunxiboot \
--default-dt $(CONFIG_DEFAULT_DEVICE_TREE) $< $@
$(obj)/sunxi-spl.bin: $(obj)/$(SPL_BIN).bin FORCE
$(call if_changed,mksunxiboot)



+ 47
- 6
tools/mksunxiboot.c View File

@@ -48,8 +48,8 @@ int gen_check_sum(struct boot_file_head *head_p)
#define ALIGN(x, a) __ALIGN_MASK((x), (typeof(x))(a)-1)
#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))

#define SUN4I_SRAM_SIZE 0x7600 /* 0x7748+ is used by BROM */
#define SRAM_LOAD_MAX_SIZE (SUN4I_SRAM_SIZE - sizeof(struct boot_file_head))
#define SUNXI_SRAM_SIZE 0x8000 /* SoC with smaller size are limited before */
#define SRAM_LOAD_MAX_SIZE (SUNXI_SRAM_SIZE - sizeof(struct boot_file_head))

/*
* BROM (at least on A10 and A20) requires NAND-images to be explicitly aligned
@@ -70,11 +70,40 @@ int main(int argc, char *argv[])
struct boot_img img;
unsigned file_size;
int count;
char *tool_name = argv[0];
char *default_dt = NULL;

if (argc < 2) {
printf("\tThis program makes an input bin file to sun4i " \
"bootable image.\n" \
"\tUsage: %s input_file out_putfile\n", argv[0]);
/* a sanity check */
if ((sizeof(img.header) % 32) != 0) {
fprintf(stderr, "ERROR: the SPL header must be a multiple ");
fprintf(stderr, "of 32 bytes.\n");
return EXIT_FAILURE;
}

/* process optional command line switches */
while (argc >= 2 && argv[1][0] == '-') {
if (strcmp(argv[1], "--default-dt") == 0) {
if (argc >= 3) {
default_dt = argv[2];
argv += 2;
argc -= 2;
continue;
}
fprintf(stderr, "ERROR: no --default-dt arg\n");
return EXIT_FAILURE;
} else {
fprintf(stderr, "ERROR: bad option '%s'\n", argv[1]);
return EXIT_FAILURE;
}
}

if (argc < 3) {
printf("This program converts an input binary file to a sunxi bootable image.\n");
printf("\nUsage: %s [options] input_file output_file\n",
tool_name);
printf("Where [options] may be:\n");
printf(" --default-dt arg - 'arg' is the default device tree name\n");
printf(" (CONFIG_DEFAULT_DEVICE_TREE).\n");
return EXIT_FAILURE;
}

@@ -122,6 +151,18 @@ int main(int argc, char *argv[])
memcpy(img.header.spl_signature, SPL_SIGNATURE, 3); /* "sunxi" marker */
img.header.spl_signature[3] = SPL_HEADER_VERSION;

if (default_dt) {
if (strlen(default_dt) + 1 <= sizeof(img.header.string_pool)) {
strcpy((char *)img.header.string_pool, default_dt);
img.header.dt_name_offset =
cpu_to_le32(offsetof(struct boot_file_head,
string_pool));
} else {
printf("WARNING: The SPL header is too small\n");
printf(" and has no space to store the dt name.\n");
}
}

gen_check_sum(&img.header);

count = write(fd_out, &img, le32_to_cpu(img.header.length));